From aea2e5d17f2b370982fec3cbb7920e4d5e04ec25 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 6 May 2023 13:57:39 +0200 Subject: [PATCH 001/672] c host skeletal support early smoke test code reduction cleanup fmt cpp code skeleton correct file names still working on understanding the structure working typedef and struct pascal case and enums better function names okish header file less duplicate namespaces less namespace dup close to operational generate wamr host types fix compilation after merge namespace inconsistencies but at least generates code again introduce namespaces for the package proper namespace handling in more places handle optional includes well fmt consistent namespace with C code include hypothetical result header condittional string include initial function definition work initial wamr inclusion stub add missing first arg return a string, but pass a string_view there is tartan llama as a fallback correct expected support initial calling of functions string storing correct string passing result deep copying incomplete result copying implementation cargo fmt return integer even after multiple typedef chains proper newlining proper result return proper result output handling proper result handling, remove verbosity resulting c++ code compiles, records need more work clean up reuse more of the common infrastructure (size+align) proper array handling failed attempt to use lower, copy array elements using memcpy doesn't make much of a difference right now record copying initial short cut support use malloc natively use a specific symbol naming scheme correct function signature mark string argument as such adapt to resources branch more wit examples with resources cargo update (new wasm tools) select the wasm namespace for the external symbol for drop dummy code for resources (and more) more complex example pass options down to the bottom (e.g. to change expected representation) wit fixes and test cases hack around this keyword problem for now implement a common base class remove methods from the global notation, add fake compilation environment more resources used correct result handling restart c++ codegen from rust code wasm-tools was fixed, activate cpp, resource class moved borrow vs owned wrong order in header proper method arguments and names option to generate guest headers (for use by cpp target) more guest support private implementation pointer cargo fmt small wit corrections reduce rust functionality a very simple test for simple.wit method args proper method naming create naming schema type conversion omit more self arguments cleanup method parameters are correct (in the simple case) proper return proper method return arguments working guest header for simple silence the type generations implement more load and store and ret_area implement proper Result type mapping adapt to new version fix compilation after merge version the generated header (order is hand-designed) adapt to the new infrastructure add hint to readme that this fork has additional functionality adapt to the new base class generated code incomplete attempt to override instead of rename cut down the number of warnings to a manageable amount add constructor from handle Handle creation works correct array handling post-merge fix updated lockfile reuse more code from other bindgens wit update ongoing effort to unify cpp and cpp-host down to 22 problems down to 19 errors compilation fixed replace link by contents band-aid over todos for now changes motivated by implementation remove owned from c++ code adapt to new dependency layout include destructor in all cases properly output enum lowering remove union, adapt to recent changes properly output the code for resource-demo proper method and constructor name and implementation (WIP) proper parameter to methods add drop function for host make objects non-copyable and member functions const real world example update fix compilation c++ skeleton correct threads wit start c++ generator from scratch host option wamr function specification separated out moving more content over reactivate parts host function registration add c_str and h_str namespace infrastructure move autosar to tests updated test wit wit dependency update separate host and guest reduce the number of warnings create interface generator cargo fmt, ResourceBase created initial class in header resources have proper namespace change namespace 1 change namespace 2 add function bindgen first instructions on simple call very crude function signature fix many warnings more cpp code generation dependency update updated test case rustfmt incomplete function signature correct cpp guest header for resource_demo cabi_realloc simplify call logic proper function addressing fix compilation after merge with main incomplete function call use proper import name proper module name for methods run clang-format proper destructor generation remove dead code proper this handling guest mode is now fully functional for resource-demo fix compilation after merge with main host header works (simple example) correct host arguments nearly complete host support proper wamr signature less special-case the destructor proper destructor implementation drop + host parameter fixes correct dtor export fix warnings nearly right host code properly qualified delete handle ids from map fully operational resource demo remove unmaintained parts hide unused parts more well print_signature is two different things at once, separate it signature work fix after separation string lowering and handles many stubs added more stubs cleanup initial registration of host functions for wasi, still lot of stubs and wrong types remove outdated parts example dependency update remove one todo --- Cargo.lock | 16 + Cargo.toml | 12 +- README.md | 13 + TODO.md | 8 + crates/c/src/lib.rs | 2 +- crates/cpp/Cargo.toml | 30 + crates/cpp/src/lib.rs | 1871 ++++++++++++ crates/cpp/src/lib_old.rs | 2628 +++++++++++++++++ crates/cpp/src/wamr.rs | 136 + crates/cpp/tests/codegen.rs | 39 + crates/test-helpers/codegen-macro/Cargo.toml | 2 +- src/bin/wit-bindgen.rs | 14 + tests/autosar/ara.wit | 99 + tests/autosar/deps.lock | 26 + tests/autosar/deps.toml | 4 + tests/autosar/deps/cli/command.wit | 7 + tests/autosar/deps/cli/environment.wit | 18 + tests/autosar/deps/cli/exit.wit | 4 + tests/autosar/deps/cli/reactor.wit | 31 + tests/autosar/deps/cli/run.wit | 4 + tests/autosar/deps/cli/stdio.wit | 17 + tests/autosar/deps/cli/terminal.wit | 47 + tests/autosar/deps/clocks/monotonic-clock.wit | 45 + tests/autosar/deps/clocks/wall-clock.wit | 42 + tests/autosar/deps/clocks/world.wit | 6 + tests/autosar/deps/filesystem/preopens.wit | 8 + tests/autosar/deps/filesystem/types.wit | 634 ++++ tests/autosar/deps/filesystem/world.wit | 6 + tests/autosar/deps/io/error.wit | 34 + tests/autosar/deps/io/poll.wit | 41 + tests/autosar/deps/io/streams.wit | 251 ++ tests/autosar/deps/io/world.wit | 6 + tests/autosar/deps/random/insecure-seed.wit | 25 + tests/autosar/deps/random/insecure.wit | 22 + tests/autosar/deps/random/random.wit | 26 + tests/autosar/deps/random/world.wit | 7 + .../autosar/deps/sockets/instance-network.wit | 9 + tests/autosar/deps/sockets/ip-name-lookup.wit | 51 + tests/autosar/deps/sockets/network.wit | 147 + .../deps/sockets/tcp-create-socket.wit | 26 + tests/autosar/deps/sockets/tcp.wit | 321 ++ .../deps/sockets/udp-create-socket.wit | 26 + tests/autosar/deps/sockets/udp.wit | 277 ++ tests/autosar/deps/sockets/world.wit | 11 + tests/autosar/deps/threads/threads.wit | 16 + tests/autosar/deps/threads/world.wit | 5 + tests/autosar/fusion.wit | 12 + tests/autosar/poll.wit | 37 + tests/autosar/radar.wit | 135 + tests/autosar/wit | 1 + tests/other/easy_test.cpp | 7 + tests/other/expected | 6 + tests/other/expected.hpp | 2444 +++++++++++++++ tests/other/fusion_cpp.h | 242 ++ tests/other/result_resource.wit | 12 + tests/other/simple.wit | 13 + tests/other/simple2.wit | 25 + tests/other/wasm_export.h | 11 + 58 files changed, 10009 insertions(+), 6 deletions(-) create mode 100644 crates/cpp/Cargo.toml create mode 100644 crates/cpp/src/lib.rs create mode 100644 crates/cpp/src/lib_old.rs create mode 100644 crates/cpp/src/wamr.rs create mode 100644 crates/cpp/tests/codegen.rs create mode 100644 tests/autosar/ara.wit create mode 100644 tests/autosar/deps.lock create mode 100644 tests/autosar/deps.toml create mode 100644 tests/autosar/deps/cli/command.wit create mode 100644 tests/autosar/deps/cli/environment.wit create mode 100644 tests/autosar/deps/cli/exit.wit create mode 100644 tests/autosar/deps/cli/reactor.wit create mode 100644 tests/autosar/deps/cli/run.wit create mode 100644 tests/autosar/deps/cli/stdio.wit create mode 100644 tests/autosar/deps/cli/terminal.wit create mode 100644 tests/autosar/deps/clocks/monotonic-clock.wit create mode 100644 tests/autosar/deps/clocks/wall-clock.wit create mode 100644 tests/autosar/deps/clocks/world.wit create mode 100644 tests/autosar/deps/filesystem/preopens.wit create mode 100644 tests/autosar/deps/filesystem/types.wit create mode 100644 tests/autosar/deps/filesystem/world.wit create mode 100644 tests/autosar/deps/io/error.wit create mode 100644 tests/autosar/deps/io/poll.wit create mode 100644 tests/autosar/deps/io/streams.wit create mode 100644 tests/autosar/deps/io/world.wit create mode 100644 tests/autosar/deps/random/insecure-seed.wit create mode 100644 tests/autosar/deps/random/insecure.wit create mode 100644 tests/autosar/deps/random/random.wit create mode 100644 tests/autosar/deps/random/world.wit create mode 100644 tests/autosar/deps/sockets/instance-network.wit create mode 100644 tests/autosar/deps/sockets/ip-name-lookup.wit create mode 100644 tests/autosar/deps/sockets/network.wit create mode 100644 tests/autosar/deps/sockets/tcp-create-socket.wit create mode 100644 tests/autosar/deps/sockets/tcp.wit create mode 100644 tests/autosar/deps/sockets/udp-create-socket.wit create mode 100644 tests/autosar/deps/sockets/udp.wit create mode 100644 tests/autosar/deps/sockets/world.wit create mode 100644 tests/autosar/deps/threads/threads.wit create mode 100644 tests/autosar/deps/threads/world.wit create mode 100644 tests/autosar/fusion.wit create mode 100644 tests/autosar/poll.wit create mode 100644 tests/autosar/radar.wit create mode 120000 tests/autosar/wit create mode 100644 tests/other/easy_test.cpp create mode 100644 tests/other/expected create mode 100755 tests/other/expected.hpp create mode 100644 tests/other/fusion_cpp.h create mode 100644 tests/other/result_resource.wit create mode 100644 tests/other/simple.wit create mode 100644 tests/other/simple2.wit create mode 100644 tests/other/wasm_export.h diff --git a/Cargo.lock b/Cargo.lock index 767e4b349..3ace1cce4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2205,6 +2205,7 @@ dependencies = [ "wasmtime-wasi", "wit-bindgen-c", "wit-bindgen-core", + "wit-bindgen-cpp", "wit-bindgen-csharp", "wit-bindgen-go", "wit-bindgen-markdown", @@ -2223,6 +2224,21 @@ dependencies = [ "wit-parser", ] +[[package]] +name = "wit-bindgen-cpp" +version = "0.2.0" +dependencies = [ + "anyhow", + "clap", + "heck", + "test-helpers", + "wasm-encoder 0.38.0", + "wasm-metadata", + "wit-bindgen-c", + "wit-bindgen-core", + "wit-component", +] + [[package]] name = "wit-bindgen-csharp" version = "0.15.0" diff --git a/Cargo.toml b/Cargo.toml index d28818d39..5fcab40b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,13 +20,13 @@ resolver = "2" edition = "2021" [workspace.dependencies] -anyhow = "1.0.72" -bitflags = "2.3.3" +anyhow = "1.0.75" +bitflags = "2.4.1" heck = { version = "0.4", features = ["unicode"] } pulldown-cmark = { version = "0.9", default-features = false } -clap = { version = "4.3.19", features = ["derive"] } +clap = { version = "4.4.7", features = ["derive"] } env_logger = "0.10.0" -indexmap = "2.0.0" +indexmap = "2.0.2" wasm-encoder = "0.38.0" wasm-metadata = "0.10.13" @@ -42,6 +42,7 @@ wit-bindgen-go = { path = 'crates/go', version = '0.15.0' } wit-bindgen-csharp = { path = 'crates/csharp', version = '0.15.0' } wit-bindgen-markdown = { path = 'crates/markdown', version = '0.15.0' } wit-bindgen = { path = 'crates/guest-rust', version = '0.15.0', default-features = false } +wit-bindgen-cpp = { path = 'crates/cpp', version = '0.2.0' } [[bin]] name = "wit-bindgen" @@ -52,6 +53,7 @@ clap = { workspace = true } wit-bindgen-core = { workspace = true } wit-bindgen-rust = { workspace = true, features = ['clap'], optional = true } wit-bindgen-c = { workspace = true, features = ['clap'], optional = true } +wit-bindgen-cpp = { workspace = true, features = ['clap'], optional = true } wit-bindgen-markdown = { workspace = true, features = ['clap'], optional = true } wit-bindgen-teavm-java = { workspace = true, features = ['clap'], optional = true } wit-bindgen-go = { workspace = true, features = ['clap'], optional = true } @@ -67,8 +69,10 @@ default = [ 'teavm-java', 'go', 'csharp-naot', + 'cpp', ] c = ['dep:wit-bindgen-c'] +cpp = ['dep:wit-bindgen-cpp'] rust = ['dep:wit-bindgen-rust'] markdown = ['dep:wit-bindgen-markdown'] teavm-java = ['dep:wit-bindgen-teavm-java'] diff --git a/README.md b/README.md index a15ac39b1..877370b75 100644 --- a/README.md +++ b/README.md @@ -374,6 +374,17 @@ wasm-tools component new main.embed.wasm --adapt wasi_snapshot_preview1.wasm -o wasm-tools validate main.component.wasm --features component-model ``` +### 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: Other Languages Other languages such as JS, Ruby, Python, etc, are hoped to be supported one day @@ -433,6 +444,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. + - Tooling: the [`wasm-tools`] project can be used to inspect and modify low-level details of components. For example as previously mentioned you can inspect the WIT-based interface of a component with `wasm-tools component diff --git a/TODO.md b/TODO.md index 5d08da9df..e27278c7a 100644 --- a/TODO.md +++ b/TODO.md @@ -58,3 +58,11 @@ * Imported handle types show up as `any` in TS, unsure how to plumb through actual types to get that actually typed. + +# Cpp + +* Use Resolve::wasm_signature ? + +* Reuse code between host and guest (RustFunctionGenerator like?) + +* Evaluate emit/call for host side code generation diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index b6145f298..eccede4f2 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -2658,7 +2658,7 @@ impl SourceExt for wit_bindgen_core::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", diff --git a/crates/cpp/Cargo.toml b/crates/cpp/Cargo.toml new file mode 100644 index 000000000..16482c718 --- /dev/null +++ b/crates/cpp/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "wit-bindgen-cpp" +authors = ["Christof Petig "] +version = "0.2.0" +edition.workspace = true +repository = 'https://github.com/cpetig/wit-bindgen' +license = "Apache-2.0 WITH LLVM-exception" +description = """ +C host binding generator for WIT and the component model, targeting the common C API. +""" + +[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/src/lib.rs b/crates/cpp/src/lib.rs new file mode 100644 index 000000000..ed57f6826 --- /dev/null +++ b/crates/cpp/src/lib.rs @@ -0,0 +1,1871 @@ +use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase, *}; +use std::{ + collections::{HashMap, HashSet}, + fmt::Write as FmtWrite, + io::{Read, Write}, + process::{Command, Stdio}, +}; +use wit_bindgen_c::{to_c_ident, wasm_type}; +use wit_bindgen_core::{ + abi::{self, AbiVariant, LiftLower, WasmSignature}, + abi::{Bindgen, WasmType}, + uwrite, uwriteln, + wit_parser::{ + Docs, Function, FunctionKind, Handle, InterfaceId, Resolve, Results, SizeAlign, Type, + TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, + }, + Files, InterfaceGenerator, Source, WorldGenerator, +}; + +mod wamr; + +pub const RESOURCE_BASE_CLASS_NAME: &str = "ResourceBase"; +pub const OWNED_CLASS_NAME: &str = "Owned"; + +type CppType = String; + +#[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, +} + +// 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_resources: bool, +} + +#[derive(Clone)] +struct HostFunction { + wasm_name: String, + wamr_signature: String, + host_name: String, +} + +#[derive(Default)] +struct SourceWithState { + src: Source, + namespace: Vec, +} + +#[derive(Default)] +struct Cpp { + opts: Opts, + c_src: SourceWithState, + h_src: SourceWithState, + dependencies: Includes, + includes: Vec, + host_functions: HashMap>, + world: String, + world_id: Option, + imported_interfaces: HashSet, +} + +#[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 + #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))] + 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, +} + +impl Opts { + pub fn build(self) -> Box { + let mut r = Cpp::new(); + r.opts = self; + Box::new(r) + } +} + +impl Cpp { + fn new() -> Cpp { + Cpp::default() + } + + fn include(&mut self, s: &str) { + self.includes.push(s.to_string()); + } + + fn interface<'a>( + &'a mut self, + resolve: &'a Resolve, + name: &'a Option<&'a WorldKey>, + in_import: bool, + wasm_import_module: Option, + ) -> CppInterfaceGenerator<'a> { + let mut sizes = SizeAlign::default(); + sizes.fill(resolve); + + CppInterfaceGenerator { + _src: Source::default(), + gen: self, + resolve, + interface: None, + _name: name, + sizes, + // public_anonymous_types: BTreeSet::new(), + in_import, + // export_funcs: Vec::new(), + return_pointer_area_size: 0, + return_pointer_area_align: 0, + wasm_import_module, + } + } +} + +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 { + uwriteln!( + self.c_src.src, + r#"#include "{}_cpp.h" + #include + #include // 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, + ) { + 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); + // if self.gen.interfaces_with_types_printed.insert(id) { + gen.types(id); + // } + + for (_name, func) in resolve.interfaces[id].functions.iter() { + if matches!(func.kind, FunctionKind::Freestanding) { + gen.generate_guest_import(func); + } + } + // gen.finish(); + } + + fn export_interface( + &mut self, + _resolve: &Resolve, + name: &WorldKey, + _iface: InterfaceId, + _files: &mut Files, + ) -> anyhow::Result<()> { + self.h_src + .src + .push_str(&format!("// export_interface {name:?}\n")); + Ok(()) + } + + fn import_funcs( + &mut self, + _resolve: &Resolve, + _world: WorldId, + _funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + todo!() + } + + fn export_funcs( + &mut self, + _resolve: &Resolve, + _world: WorldId, + _funcs: &[(&str, &Function)], + _files: &mut Files, + ) -> anyhow::Result<()> { + todo!() + } + + fn import_types( + &mut self, + _resolve: &Resolve, + _world: WorldId, + _types: &[(&str, TypeId)], + _files: &mut Files, + ) { + todo!() + } + + fn finish(&mut self, resolve: &Resolve, world_id: WorldId, files: &mut Files) { + let world = &resolve.worlds[world_id]; + let snake = world.name.to_snake_case(); + + 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.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\n", + world.name.to_shouty_snake_case(), + ); + } + self.include(""); + if self.dependencies.needs_string { + self.include(""); + } + if self.dependencies.needs_string_view { + self.include(""); + } + if self.dependencies.needs_vector { + self.include(""); + } + if self.dependencies.needs_expected { + self.include(""); + } + if self.dependencies.needs_optional { + self.include(""); + } + if self.dependencies.needs_cstring { + self.include(""); + } + if !self.opts.host && self.dependencies.needs_resources { + self.include(""); + } + if self.opts.host && self.dependencies.needs_resources { + self.include(""); + } + + 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.host { + // uwriteln!(c_str.src, "#include \"{snake}_cpp.h\""); + } else { + uwriteln!(c_str.src, "#include \"{snake}_cpp_host.h\""); + if !self.opts.short_cut { + uwriteln!( + c_str.src, + "#include // wasm-micro-runtime header" + ); + + 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.dependencies.needs_resources { + let namespace = namespace(resolve, &TypeOwner::World(world_id)); + h_str.change_namespace(&namespace); + // this is export, not host + if self.opts.host { + uwriteln!( + h_str.src, + "template + class {RESOURCE_BASE_CLASS_NAME} {{ + static std::map 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(id, std::move(value))); + return id; + }} + static void remove_resource(int32_t id) {{ + resources.erase(id); + }} + }}; + template struct {OWNED_CLASS_NAME} {{ + T *ptr; + }};" + ); + } else { + // somehow spaces get removed, newlines remain (problem occurs before const&) + // TODO: should into_handle become && ??? + uwriteln!( + h_str.src, + "class {RESOURCE_BASE_CLASS_NAME} {{ + static const int32_t invalid = -1; + protected: + int32_t handle; + public: + {RESOURCE_BASE_CLASS_NAME}(int32_t h=invalid) : handle(h) {{}} + {RESOURCE_BASE_CLASS_NAME}({RESOURCE_BASE_CLASS_NAME}&&r) + : handle(r.handle) {{ + r.handle=invalid; + }} + {RESOURCE_BASE_CLASS_NAME}({RESOURCE_BASE_CLASS_NAME} + const&) = delete; + void set_handle(int32_t h) {{ handle=h; }} + int32_t get_handle() const {{ return handle; }} + int32_t into_handle() {{ + int32_t h= handle; + handle= invalid; + return h; + }} + {RESOURCE_BASE_CLASS_NAME}& operator=({RESOURCE_BASE_CLASS_NAME}&&r) {{ + assert(handle<0); + handle= r.handle; + r.handle= invalid; + return *this; + }} + {RESOURCE_BASE_CLASS_NAME}& operator=({RESOURCE_BASE_CLASS_NAME} + const&r) = delete; + }};" + ); + } + } + h_str.change_namespace(&Vec::default()); + + self.c_src.change_namespace(&Vec::default()); + 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); + // c_str.push_str(&self.src.c_fns); + + // if self.src.h_defs.len() > 0 { + // h_str.push_str(&self.src.h_defs); + // } + + // h_str.push_str(&self.src.h_fns); + + uwriteln!(c_str.src, "\n// Component Adapters"); + + // c_str.push_str(&self.src.c_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 { + 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(c_str.src.as_bytes()) + .unwrap(); + c_str.src.as_mut_string().truncate(0); + child + .stdout + .take() + .unwrap() + .read_to_string(c_str.src.as_mut_string()) + .unwrap(); + let status = child.wait().unwrap(); + assert!(status.success()); + child = Command::new("clang-format") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .expect("failed to spawn `clang-format`"); + child + .stdin + .take() + .unwrap() + .write_all(h_str.src.as_bytes()) + .unwrap(); + h_str.src.as_mut_string().truncate(0); + child + .stdout + .take() + .unwrap() + .read_to_string(h_str.src.as_mut_string()) + .unwrap(); + let status = child.wait().unwrap(); + assert!(status.success()); + } + + 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()); + } + } +} + +// determine namespace +fn namespace(resolve: &Resolve, owner: &TypeOwner) -> Vec { + let mut result = Vec::default(); + 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) { + 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) { + let mut same = 0; + // itertools::fold_while? + for (a, b) in self.namespace.iter().zip(target.iter()) { + if a == b { + same += 1; + } else { + break; + } + } + // if same == 0 { + // 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, + _name: &'a Option<&'a WorldKey>, + sizes: SizeAlign, + in_import: bool, + return_pointer_area_size: usize, + return_pointer_area_align: usize, + pub wasm_import_module: Option, +} + +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!(), + } + } + + fn func_namespace_name(&self, func: &Function) -> (Vec, 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), + } + .map(|i| { + let ty = &self.resolve.types[*i]; + (ty.name.as_ref().unwrap().to_pascal_case(), ty.owner) + }) + .unwrap_or(( + Default::default(), + TypeOwner::World(self.gen.world_id.unwrap()), + )); + let mut namespace = namespace(self.resolve, &owner); + let is_drop = is_drop_method(func); + let func_name_h = if !matches!(&func.kind, FunctionKind::Freestanding) { + namespace.push(object.clone()); + if let FunctionKind::Constructor(_i) = &func.kind { + object.clone() + } else if is_drop { + "~".to_string() + &object + } else { + func.item_name().to_pascal_case() + } + } else { + func.name.to_pascal_case() + }; + (namespace, func_name_h) + } + + // print the signature of the lowered (wasm) function calling into highlevel + fn export_signature(&mut self, func: &Function) -> Vec { + let is_drop = is_drop_method(func); + let signature = if is_drop { + WasmSignature { + params: vec![WasmType::I32], + results: Vec::new(), + indirect_params: false, + retptr: false, + } + } else { + // TODO perhaps remember better names for the arguments + self.resolve.wasm_signature(AbiVariant::GuestImport, func) + }; + self.gen.c_src.src.push_str("static "); + self.gen + .c_src + .src + .push_str(if signature.results.is_empty() { + "void" + } else { + wasm_type(signature.results[0]) + }); + self.gen.c_src.src.push_str(" "); + let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + let export_name = CppInterfaceGenerator::export_name2(&module_name, &func.name); + self.gen.c_src.src.push_str(&export_name); + self.gen.c_src.src.push_str("("); + let mut params = Vec::new(); + for (n, ty) in signature.params.iter().enumerate() { + let name = format!("arg{n}"); + self.gen.c_src.src.push_str(wasm_type(*ty)); + self.gen.c_src.src.push_str(" "); + self.gen.c_src.src.push_str(&name); + params.push(name); + if n + 1 != signature.params.len() { + self.gen.c_src.src.push_str(", "); + } + } + self.gen.c_src.src.push_str(")\n"); + if self.gen.opts.host { + 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) + .and_modify(|v| v.push(remember.clone())) + .or_insert(vec![remember]); + } + params + } + + fn high_level_signature(&mut self, func: &Function, import: bool) -> HighlevelSignature { + let mut res = HighlevelSignature::default(); + + let (namespace, func_name_h) = self.func_namespace_name(func); + res.name = func_name_h; + res.namespace = namespace; + let is_drop = is_drop_method(func); + // we might want to separate c_sig and h_sig + // let mut sig = String::new(); + if !matches!(&func.kind, FunctionKind::Constructor(_)) && !is_drop { + match &func.results { + wit_bindgen_core::wit_parser::Results::Named(n) => { + if n.is_empty() { + res.result = "void".into(); + } else { + todo!(); + } + } + wit_bindgen_core::wit_parser::Results::Anon(ty) => { + res.result = self.type_name(ty); + } + } + } + if matches!(func.kind, FunctionKind::Static(_)) && !is_drop { + res.static_member = true; + } + for (i, (name, param)) in func.params.iter().enumerate() { + if i == 0 && name == "self" { + continue; + } + res.arguments.push((name.clone(), self.type_name(param))); + } + // default to non-const when exporting a method + if matches!(func.kind, FunctionKind::Method(_)) && import { + res.const_member = true; + } + res + } + + fn print_signature(&mut self, func: &Function, import: bool) -> Vec { + let cpp_sig = self.high_level_signature(func, import); + if cpp_sig.static_member { + self.gen.h_src.src.push_str("static "); + } + self.gen.h_src.src.push_str(&cpp_sig.result); + 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("("); + 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"); + } + self.gen.h_src.src.push_str(";\n"); + + // we want to separate the lowered signature (wasm) and the high level signature + if !import { + return self.export_signature(func); + } + + // self.rustdoc(&func.docs); + // self.rustdoc_params(&func.params, "Parameters"); + // TODO: re-add this when docs are back + // self.rustdoc_params(&func.results, "Return"); + + let (namespace, func_name_h) = self.func_namespace_name(func); + let is_drop = is_drop_method(func); + // we might want to separate c_sig and h_sig + let mut sig = String::new(); + let mut result_ptr: Option = None; + if !matches!(&func.kind, FunctionKind::Constructor(_)) && !is_drop { + match &func.results { + wit_bindgen_core::wit_parser::Results::Named(n) => { + if n.len() == 0 { + sig.push_str("void"); + } else { + todo!(); + } + } + wit_bindgen_core::wit_parser::Results::Anon(ty) => { + if is_arg_by_pointer(self.resolve, ty) { + sig.push_str("void"); + result_ptr = Some(ty.clone()); + } else { + sig.push_str(&self.type_name(ty)); + } + } + } + sig.push_str(" "); + } + if import { + self.gen.c_src.src.push_str(&sig); + self.gen.c_src.qualify(&namespace); + self.gen.c_src.src.push_str(&func_name_h); + } else { + self.gen.c_src.src.push_str("static "); + if matches!(&func.kind, FunctionKind::Constructor(_)) { + self.gen.c_src.src.push_str("int32_t "); + } else if is_drop { + self.gen.c_src.src.push_str("void "); + } else { + self.gen.c_src.src.push_str(&sig); + } + let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + let full_name = "host_".to_string() + &Self::export_name2(&module_name, &func.name); + self.gen.c_src.src.push_str(&full_name); + if self.gen.opts.host { + let signature = wamr::wamr_signature(self.resolve, func); + let remember = HostFunction { + wasm_name: func.name.clone(), + wamr_signature: signature.to_string(), + host_name: full_name, + }; + self.gen + .host_functions + .entry(module_name) + .and_modify(|v| v.push(remember.clone())) + .or_insert(vec![remember]); + } + } + sig.push_str(&func_name_h); + //self.gen.h_src.src.push_str(&sig); + sig.clear(); + self.gen.c_src.src.push_str("("); + if self.gen.opts.host { + self.gen.c_src.src.push_str("wasm_exec_env_t exec_env"); + if func.params.len() > 0 { + self.gen.c_src.src.push_str(", "); + } + } + let mut params = Vec::new(); + for (i, (name, param)) in func.params.iter().enumerate() { + if is_arg_by_pointer(self.resolve, param) { + params.push(name.clone() + "_ptr"); + sig.push_str(&self.type_name(param)); + sig.push_str("* "); + sig.push_str(&(name.clone() + "_ptr")); + } else { + params.push(name.clone()); + if i == 0 && name == "self" { + if !import { + self.gen.c_src.src.push_str("int32_t "); + self.gen.c_src.src.push_str(&name); + if i + 1 != func.params.len() { + self.gen.c_src.src.push_str(", "); + } + } + continue; + } + sig.push_str(&self.type_name(param)); + sig.push_str(" "); + sig.push_str(&name); + } + if i + 1 != func.params.len() { + sig.push_str(","); + } + } + if let Some(result_ptr) = &result_ptr { + params.push("result_ptr".into()); + sig.push_str(&self.type_name(result_ptr)); + sig.push_str("* "); + sig.push_str("result_ptr"); + } + sig.push_str(")"); + // default to non-const when exporting a method + if matches!(func.kind, FunctionKind::Method(_)) && import { + sig.push_str("const"); + } + self.gen.c_src.src.push_str(&sig); + self.gen.c_src.src.push_str("\n"); + // self.gen.h_src.src.push_str("("); + // sig.push_str(";\n"); + // self.gen.h_src.src.push_str(&sig); + params + } + + fn generate_guest_import(&mut self, func: &Function) { + let params = self.print_signature(func, !self.gen.opts.host); + self.gen.c_src.src.push_str("{\n"); + let lift_lower = if self.gen.opts.host { + LiftLower::LiftArgsLowerResults + } else { + LiftLower::LowerArgsLiftResults + }; + if is_drop_method(func) { + match lift_lower { + LiftLower::LiftArgsLowerResults => { + let owner = &self.resolve.types[match &func.kind { + FunctionKind::Static(id) => *id, + _ => panic!("drop should be static"), + }]; + self.gen.c_src.src.push_str(" "); + let mut namespace = namespace(self.resolve, &owner.owner); + namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); + self.gen.c_src.qualify(&namespace); + self.gen.c_src.src.push_str("remove_resource(self);\n"); + } + LiftLower::LowerArgsLiftResults => { + 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); + }}" + ); + } + } + } else { + let mut f = FunctionBindgen::new(self, params); + abi::call( + f.gen.resolve, + AbiVariant::GuestImport, + lift_lower, + func, + &mut f, + ); + } + 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 { + 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() + } + + fn print_optional_ty(&mut self, ty: Option<&Type>, out: &mut String) { + match ty { + Some(ty) => self.push_ty_name(ty, out), + None => out.push_str("void"), + } + } + + fn type_name(&mut self, ty: &Type) -> 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::Float32 => "float".into(), + Type::Float64 => "double".into(), + Type::String => { + self.gen.dependencies.needs_string = true; + "std::string".into() + } + Type::Id(id) => match &self.resolve.types[*id].kind { + TypeDefKind::Record(_r) => { + format!("record.{}", self.resolve.types[*id].name.as_ref().unwrap()) + } + TypeDefKind::Resource => self.resolve.types[*id].name.as_ref().cloned().unwrap(), + TypeDefKind::Handle(Handle::Own(id)) => self.type_name(&Type::Id(*id)), + TypeDefKind::Handle(Handle::Borrow(id)) => { + "std::reference_wrapper<".to_string() + &self.type_name(&Type::Id(*id)) + ">" + } + TypeDefKind::Flags(_) => "Flags".to_string(), + TypeDefKind::Tuple(_) => "Tuple".to_string(), + TypeDefKind::Variant(v) => { + let mut result = "std::variant<".to_string(); + for (n, case) in v.cases.iter().enumerate() { + result += &case + .ty + .as_ref() + .map_or("void".to_string(), |ty| self.type_name(ty)); + if n + 1 != v.cases.len() { + result += ", "; + } + } + result += ">"; + result + } + TypeDefKind::Enum(_e) => "Enum".to_string(), + TypeDefKind::Option(o) => "std::optional<".to_string() + &self.type_name(o) + ">", + TypeDefKind::Result(r) => { + "std::expected<".to_string() + + &r.ok.as_ref().map_or("void".into(), |t| self.type_name(t)) + + ", " + + &r.err.as_ref().map_or("void".into(), |t| self.type_name(t)) + + ">" + } + TypeDefKind::List(ty) => { + self.gen.dependencies.needs_vector = true; + "std::vector<".to_string() + &self.type_name(ty) + ">" + } + TypeDefKind::Future(_) => todo!(), + TypeDefKind::Stream(_) => todo!(), + TypeDefKind::Type(ty) => self.type_name(ty), + TypeDefKind::Unknown => todo!(), + }, + } + } + + fn push_ty_name(&mut self, ty: &Type, out: &mut String) { + wit_bindgen_c::push_ty_name(self.resolve, ty, out); + } + + fn make_export_name(input: &str) -> String { + input + .chars() + .map(|c| match c { + 'A'..='Z' | 'a'..='z' | '0'..='9' => c, + _ => '_', + }) + .collect() + } + + fn export_name2(module_name: &str, name: &str) -> String { + let mut res = Self::make_export_name(module_name); + res.push('_'); + res.push_str(&Self::make_export_name(name)); + res + } + + fn declare_import2( + module_name: &str, + name: &str, + args: &str, + result: &str, + ) -> (String, String) { + let extern_name = Self::export_name2(module_name, name); + let import = format!("extern __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(wasm_type(*param)); + if n + 1 != params.len() { + args.push_str(", "); + } + } + let result = if results.is_empty() { + "void" + } else { + wasm_type(results[0]) + }; + let (name, code) = Self::declare_import2(module_name, name, &args, result); + self.gen.c_src.src.push_str(&code); + name + } +} + +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, + ) { + uwriteln!(self.gen.h_src.src, "// type_record({name})"); + } + + 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 import = self.gen.imported_interfaces.contains(&intf) ^ self.gen.opts.host; + let mut world_name = self.gen.world.to_snake_case(); + world_name.push_str("::"); + let funcs = self.resolve.interfaces[intf].functions.values(); + let namespc = namespace(self.resolve, &type_.owner); + self.gen.h_src.change_namespace(&namespc); + + self.gen.dependencies.needs_resources = true; + let pascal = name.to_upper_camel_case(); + + if !import { + uwriteln!(self.gen.c_src.src, "template std::map {world_name}{RESOURCE_BASE_CLASS_NAME}::resources;"); + } + + let base_type = if !import { + format!("<{pascal}>") + } else { + String::default() + }; + let derive = format!(" : public {world_name}{RESOURCE_BASE_CLASS_NAME}{base_type}"); + uwriteln!(self.gen.h_src.src, "class {pascal}{derive} {{\n"); + if !import { + // TODO: Replace with virtual functions + uwriteln!( + self.gen.h_src.src, + " // private implementation data\n struct pImpl;\n pImpl * p_impl;\n" + ); + } + uwriteln!(self.gen.h_src.src, "public:\n"); + if !import { + // because of pimpl + uwriteln!( + self.gen.h_src.src, + " {pascal}({pascal}&&);\n{pascal}(const {pascal}&) = delete;\nvoid operator=({pascal}&&);\nvoid operator=(const {pascal}&) = delete;" + ); + } + // destructor + { + let name = "[resource-drop]".to_string() + &name; + let func = Function { + name: name, + kind: FunctionKind::Static(id), + params: vec![("self".into(), Type::Id(id))], + results: Results::Named(vec![]), + docs: Docs::default(), + }; + self.generate_guest_import(&func); + } + for func in funcs { + // Some(name), + self.generate_guest_import(func); + } + + if import { + // consuming constructor from handle (bindings) + uwriteln!( + self.gen.h_src.src, + "{pascal}({world_name}{RESOURCE_BASE_CLASS_NAME}&&);\n" + ); + uwriteln!(self.gen.h_src.src, "{pascal}({pascal}&&) = default;\n"); + } + uwriteln!(self.gen.h_src.src, "}};\n"); + } + } + + fn type_flags( + &mut self, + _id: TypeId, + name: &str, + _flags: &wit_bindgen_core::wit_parser::Flags, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + uwriteln!(self.gen.h_src.src, "// type_flags({name})"); + } + + fn type_tuple( + &mut self, + _id: TypeId, + _name: &str, + _flags: &wit_bindgen_core::wit_parser::Tuple, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + todo!() + } + + fn type_variant( + &mut self, + _id: TypeId, + name: &str, + _variant: &wit_bindgen_core::wit_parser::Variant, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + uwriteln!(self.gen.h_src.src, "// type_variant({name})"); + } + + fn type_option( + &mut self, + _id: TypeId, + _name: &str, + _payload: &wit_bindgen_core::wit_parser::Type, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + todo!() + } + + fn type_result( + &mut self, + _id: TypeId, + _name: &str, + _result: &wit_bindgen_core::wit_parser::Result_, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + todo!() + } + + fn type_enum( + &mut self, + _id: TypeId, + name: &str, + _enum_: &wit_bindgen_core::wit_parser::Enum, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + uwriteln!(self.gen.h_src.src, "// type_enum({name})"); + } + + fn type_alias( + &mut self, + _id: TypeId, + name: &str, + _ty: &wit_bindgen_core::wit_parser::Type, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + uwriteln!(self.gen.h_src.src, "// type_alias({name})"); + } + + fn type_list( + &mut self, + _id: TypeId, + _name: &str, + _ty: &wit_bindgen_core::wit_parser::Type, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + todo!() + } + + fn type_builtin( + &mut self, + _id: TypeId, + _name: &str, + _ty: &wit_bindgen_core::wit_parser::Type, + _docs: &wit_bindgen_core::wit_parser::Docs, + ) { + todo!() + } +} + +struct FunctionBindgen<'a, 'b> { + gen: &'b mut CppInterfaceGenerator<'a>, + params: Vec, + tmp: usize, + import_return_pointer_area_size: usize, + import_return_pointer_area_align: usize, +} + +impl<'a, 'b> FunctionBindgen<'a, 'b> { + fn new(gen: &'b mut CppInterfaceGenerator<'a>, params: Vec) -> Self { + Self { + gen, + params, + tmp: 0, + import_return_pointer_area_size: 0, + import_return_pointer_area_align: 0, + } + } + + fn tmp(&mut self) -> usize { + let ret = self.tmp; + self.tmp += 1; + ret + } + + fn push_str(&mut self, s: &str) { + self.gen.gen.c_src.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) { + match amt { + 0 => {} + 1 => { + let tmp = self.tmp(); + let res = format!("result{}", tmp); + self.push_str("auto "); + self.push_str(&res); + results.push(res); + self.push_str(" = "); + } + _n => todo!(), + } + } + + fn load(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec) { + results.push(format!("*(({}*) ({} + {}))", ty, operands[0], offset)); + } + + // fn load_ext(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec) { + // self.load(ty, offset, operands, results); + // let result = results.pop().unwrap(); + // results.push(format!("(int32_t) ({})", result)); + // } + + fn store(&mut self, ty: &str, offset: i32, operands: &[String]) { + uwriteln!( + self.gen.gen.c_src.src, + "*(({}*)({} + {})) = {};", + ty, + operands[1], + offset, + operands[0] + ); + } + + 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(_) => todo!(), + 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, + results: &mut Vec, + ) { + 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_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: _ } => todo!(), + 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()), + } + } + } + abi::Instruction::I32Load { offset } => { + let tmp = self.tmp(); + uwriteln!( + self.gen.gen.c_src.src, + "int32_t l{tmp} = *((int32_t const*)({} + {offset}));", + operands[0] + ); + results.push(format!("l{tmp}")); + } + abi::Instruction::I32Load8U { offset } => { + results.push(format!( + "(int32_t)(*((uint8_t const*)({} + {})))", + operands[0], offset + )); + } + abi::Instruction::I32Load8S { offset: _ } => todo!(), + abi::Instruction::I32Load16U { offset: _ } => todo!(), + abi::Instruction::I32Load16S { offset: _ } => todo!(), + abi::Instruction::I64Load { offset: _ } => todo!(), + abi::Instruction::F32Load { offset: _ } => todo!(), + abi::Instruction::F64Load { offset: _ } => todo!(), + abi::Instruction::I32Store { offset } => self.store("int32_t", *offset, operands), + abi::Instruction::I32Store8 { offset } => self.store("int32_t", *offset, operands), + abi::Instruction::I32Store16 { offset } => self.store("int32_t", *offset, operands), + abi::Instruction::I64Store { offset } => self.store("int64_t", *offset, operands), + abi::Instruction::F32Store { offset: _ } => todo!(), + abi::Instruction::F64Store { offset: _ } => todo!(), + 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::F32FromFloat32 => todo!(), + abi::Instruction::F64FromFloat64 => todo!(), + abi::Instruction::S8FromI32 => todo!(), + abi::Instruction::U8FromI32 => todo!(), + abi::Instruction::S16FromI32 => todo!(), + abi::Instruction::U16FromI32 => todo!(), + abi::Instruction::S32FromI32 => top_as("int32_t"), + abi::Instruction::U32FromI32 => top_as("uint32_t"), + abi::Instruction::S64FromI64 => todo!(), + abi::Instruction::U64FromI64 => top_as("uint64_t"), + abi::Instruction::CharFromI32 => todo!(), + abi::Instruction::Float32FromF32 => todo!(), + abi::Instruction::Float64FromF64 => todo!(), + abi::Instruction::BoolFromI32 => top_as("bool"), + abi::Instruction::ListCanonLower { + element: _, + realloc: _, + } => { + results.push("ListCanonLower.addr".into()); + results.push("ListCanonLower.len".into()); + } + 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); + if realloc.is_none() { + self.push_str(&format!("auto {} = {};\n", val, operands[0])); + self.push_str(&format!("auto {} = (int32_t)({}.data());\n", ptr, val)); + self.push_str(&format!("auto {} = (int32_t)({}.size());\n", len, val)); + self.push_str("// is this correct?\n"); + } else { + self.gen.gen.dependencies.needs_guest_alloc = true; + uwriteln!( + self.gen.gen.c_src.src, + "int32_t {result} = guest_alloc(exec_env, {len});" + ); + uwriteln!(self.gen.gen.c_src.src, "memcpy(wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), {result}), {ptr}, {len});"); + } + results.push(result); + results.push(len); + } + abi::Instruction::ListLower { + element: _, + realloc: _, + } => { + results.push("ListLower1".into()); + results.push("ListLower2".into()); + } + abi::Instruction::ListCanonLift { element: _, ty: _ } => { + let tmp = self.tmp(); + let len = format!("len{}", tmp); + self.push_str(&format!("let {} = {};\n", len, operands[1])); + let result = format!("std::vector<...>({0}, {0}+{1})", operands[0], len); + results.push(result); + } + abi::Instruction::StringLift => { + let tmp = self.tmp(); + let len = format!("len{}", tmp); + uwriteln!(self.gen.gen.c_src.src, "auto {} = {};\n", len, operands[1]); + let result = format!("std::string((char const*)({}), {len})", operands[0]); + results.push(result); + } + abi::Instruction::ListLift { element, ty: _ } => { + // 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 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 mut {result} = std::vector<>(); + {result}.reserve({len}); + "#, + )); + + uwriteln!( + self.gen.gen.c_src.src, + "for (unsigned i=0; i<{len}; ++i) {{" + ); + uwriteln!(self.gen.gen.c_src.src, "auto base = {base} + i * {size};"); + uwriteln!(self.gen.gen.c_src.src, "auto e{tmp} = todo();"); + uwriteln!(self.gen.gen.c_src.src, "{result}.push_back(e{tmp});"); + uwriteln!(self.gen.gen.c_src.src, "}}"); + results.push(result); + // self.push_str(&format!( + // "{rt}::dealloc({base}, ({len} as usize) * {size}, {align});\n", + // rt = self.gen.gen.runtime_path(), + // )); + } + abi::Instruction::IterElem { element: _ } => 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, + name: _, + ty, + } => { + let mut result = self.typename_lift(*ty); + result.push_str("{"); + for (_field, val) in record.fields.iter().zip(operands) { + result.push_str(&val); + result.push_str(", "); + } + result.push_str("}"); + results.push(result); + } + abi::Instruction::HandleLower { + handle: Handle::Own(_ty), + .. + } => { + let op = &operands[0]; + // let namespace = namespace(self.gen.resolve, &self.gen.resolve.types[*ty].owner); + // let mut code = String::default(); + // for n in namespace { + // code.push_str(&n); + // code.push_str("::"); + // } + results.push(format!("{op}.store_resource(std::move({op}))")); + } + abi::Instruction::HandleLower { + handle: Handle::Borrow(_), + .. + } => { + let op = &operands[0]; + results.push(format!("{op}.get_handle()")); + } + abi::Instruction::HandleLift { + handle: _, + name: _, + ty: _, + } => { + let op = &operands[0]; + results.push(op.clone()); + } + abi::Instruction::TupleLower { tuple: _, ty: _ } => { + results.push("TupleLower1".into()); + results.push("TupleLower2".into()); + } + abi::Instruction::TupleLift { tuple: _, ty: _ } => todo!(), + abi::Instruction::FlagsLower { + flags, + name: _, + ty: _, + } => { + let tmp = self.tmp(); + self.push_str(&format!("auto flags{} = {};\n", tmp, operands[0])); + for i in 0..flags.repr().count() { + results.push(format!("((flags{} >> {})&1)!=0", tmp, i * 32)); + } + } + abi::Instruction::FlagsLift { + flags: _, + name: _, + ty: _, + } => results.push("FlagsLift".to_string()), + abi::Instruction::VariantPayloadName => results.push("e".to_string()), + abi::Instruction::VariantLower { + variant: _, + name, + ty: _, + results: _, + } => { + //let name = self.gen.type_name(*ty); + let op0 = &operands[0]; + self.push_str(&format!("({name}){op0}")); + } + abi::Instruction::VariantLift { + variant, + name: _, + 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::>(); + 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 = 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 { + enum_: _, + name: _, + ty: _, + } => results.push(format!("int32_t({})", operands[0])), + abi::Instruction::EnumLift { + enum_: _, + name, + ty: _, + } => { + results.push(format!("({name}){}", &operands[0])); + } + abi::Instruction::OptionLower { + payload: _, + ty: _, + results: _, + } => self.push_str("OptionLower"), + abi::Instruction::OptionLift { payload: _, ty: _ } => todo!(), + abi::Instruction::ResultLower { + result: _, + ty: _, + results: _, + } => self.push_str("ResultLower"), + abi::Instruction::ResultLift { result, ty: _ } => { + let mut err = String::default(); //self.blocks.pop().unwrap(); + let mut ok = String::default(); //self.blocks.pop().unwrap(); + if result.ok.is_none() { + ok.clear(); + } else { + ok = format!("std::move({ok})"); + } + if result.err.is_none() { + err.clear(); + } else { + err = format!("std::move({err})"); + } + let mut ok_type = String::default(); + self.gen.print_optional_ty(result.ok.as_ref(), &mut ok_type); + let mut err_type = String::default(); + self.gen + .print_optional_ty(result.err.as_ref(), &mut err_type); + let type_name = format!("std::expected<{ok_type}, {err_type}>",); + let err_type = "std::unexpected"; + let operand = &operands[0]; + results.push(format!( + "{operand}==0 \n? {type_name}({ok}) \n: {type_name}({err_type}({err}))" + )); + } + abi::Instruction::CallWasm { name, sig } => { + let module_name = self + .gen + .wasm_import_module + .as_ref() + .map(|e| e.clone()) + .unwrap(); + 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.gen.gen.c_src.src.push_str("auto ret = "); + results.push("ret".to_string()); + } + self.gen.gen.c_src.src.push_str(&func); + self.gen.gen.c_src.src.push_str("("); + self.gen.gen.c_src.src.push_str(&operands.join(", ")); + self.gen.gen.c_src.src.push_str(");\n"); + } + abi::Instruction::CallInterface { func } => { + // dbg!(func); + self.let_results(func.results.len(), results); + let (mut namespace, func_name_h) = self.gen.func_namespace_name(func); + if matches!(func.kind, FunctionKind::Method(_)) { + let _ = operands.remove(0); + self.gen.gen.c_src.qualify(&namespace); + self.gen.gen.c_src.src.push_str("lookup_resource(self)->"); + } else { + if matches!(func.kind, FunctionKind::Constructor(_)) { + let _ = namespace.pop(); + } + self.gen.gen.c_src.qualify(&namespace); + } + self.gen.gen.c_src.src.push_str(&func_name_h); + self.push_str("("); + self.push_str(&operands.join(", ")); + self.push_str(");"); + } + abi::Instruction::Return { amt, func } => { + let import = !self.gen.gen.opts.host; + match amt { + 0 => {} + 1 => { + match &func.kind { + FunctionKind::Constructor(_) if import => { + // strange but works + self.gen.gen.c_src.src.push_str("this->handle = "); + } + _ => self.gen.gen.c_src.src.push_str("return "), + } + self.gen.gen.c_src.src.push_str(&operands[0]); + self.gen.gen.c_src.src.push_str(";\n"); + } + _ => todo!(), + } + } + abi::Instruction::Malloc { + realloc: _, + size: _, + align: _, + } => todo!(), + abi::Instruction::GuestDeallocate { size: _, align: _ } => todo!(), + abi::Instruction::GuestDeallocateString => todo!(), + abi::Instruction::GuestDeallocateList { element: _ } => todo!(), + abi::Instruction::GuestDeallocateVariant { blocks: _ } => todo!(), + } + } + + fn return_pointer(&mut self, size: usize, align: usize) -> Self::Operand { + let tmp = self.tmp(); + + // Imports get a per-function return area to facilitate using the + // stack whereas exports use a per-module return area to cut down on + // stack usage. Note that for imports this also facilitates "adapter + // modules" for components to not have data segments. + if self.gen.in_import { + self.import_return_pointer_area_size = self.import_return_pointer_area_size.max(size); + self.import_return_pointer_area_align = + self.import_return_pointer_area_align.max(align); + uwrite!( + self.gen.gen.c_src.src, + "int32_t ptr{tmp} = int32_t(&ret_area);" + ); + } else { + self.gen.return_pointer_area_size = self.gen.return_pointer_area_size.max(size); + self.gen.return_pointer_area_align = self.gen.return_pointer_area_align.max(align); + uwriteln!( + self.gen.gen.c_src.src, + "int32_t ptr{tmp} = int32_t(&RET_AREA);" + ); + } + format!("ptr{}", tmp) + } + + fn push_block(&mut self) { + uwriteln!(self.gen.gen.c_src.src, "// push_block()"); + } + + fn finish_block(&mut self, _operand: &mut Vec) { + uwriteln!(self.gen.gen.c_src.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, + } + } +} + +// fn wasm_type(ty: WasmType) -> &'static str { +// match ty { +// WasmType::I32 => "int32_t", +// WasmType::I64 => "int64_t", +// WasmType::F32 => "float", +// WasmType::F64 => "double", +// } +// } + +fn is_drop_method(func: &Function) -> bool { + matches!(func.kind, FunctionKind::Static(_)) && func.name.starts_with("[resource-drop]") +} + +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/lib_old.rs b/crates/cpp/src/lib_old.rs new file mode 100644 index 000000000..acc05812a --- /dev/null +++ b/crates/cpp/src/lib_old.rs @@ -0,0 +1,2628 @@ +use heck::*; +use std::collections::{BTreeMap, HashMap, HashSet}; +use std::fmt::Write as _; +use std::mem; +use wit_bindgen_core::abi::{self, AbiVariant, Bindgen, Instruction, LiftLower, WasmType}; +use wit_bindgen_core::{ + uwriteln, wit_parser::*, Files, InterfaceGenerator as _, Source, TypeInfo, Types, + WorldGenerator, +}; +use wit_bindgen_cpp_host::RESOURCE_BASE_CLASS_NAME; +use wit_bindgen_rust::{ + dealias, FnSig, Ownership, RustFlagsRepr, RustFunctionGenerator, RustGenerator, TypeMode, +}; + +#[derive(Default, Copy, Clone, PartialEq, Eq)] +enum Direction { + #[default] + Import, + Export, +} + +#[derive(Default)] +struct ResourceInfo { + direction: Direction, + owned: bool, + docs: Docs, +} + +#[derive(Default)] +struct Cpp { + types: Types, + src: Source, + opts: Opts, + import_modules: BTreeMap, Vec>, + export_modules: BTreeMap, Vec>, + skip: HashSet, + interface_names: HashMap, + resources: HashMap, + import_funcs_called: bool, + world: Option, +} + +#[cfg(feature = "clap")] +fn parse_map(s: &str) -> Result, String> { + if s.is_empty() { + Ok(HashMap::default()) + } else { + s.split(',') + .map(|entry| { + let (key, value) = entry.split_once('=').ok_or_else(|| { + format!("expected string of form `=[,=...]`; got `{s}`") + })?; + Ok((key.to_owned(), value.to_owned())) + }) + .collect() + } +} + +#[derive(Default, Debug, Clone)] +#[cfg_attr(feature = "clap", derive(clap::Args))] +pub struct Opts { + /// Names of functions to skip generating bindings for. + #[cfg_attr(feature = "clap", arg(long))] + pub skip: Vec, + + /// Name of the concrete type which implements the trait representing any + /// top-level functions exported by the world. + #[cfg_attr(feature = "clap", arg(long))] + pub world_exports: Option, + + /// Names of the concrete types which implement the traits representing any + /// interfaces exported by the world. + #[cfg_attr(feature = "clap", arg(long, value_parser = parse_map, default_value = ""))] + pub interface_exports: HashMap, + + /// Names of the concrete types which implement the traits representing any + /// resources exported by the world. + #[cfg_attr(feature = "clap", arg(long, value_parser = parse_map, default_value = ""))] + pub resource_exports: HashMap, + + /// If true, generate stub implementations for any exported functions, + /// interfaces, and/or resources. + #[cfg_attr(feature = "clap", arg(long))] + pub stubs: 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, + + /// 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, +} + +impl Opts { + pub fn build(self) -> Box { + let mut r = Cpp::new(); + r.skip = self.skip.iter().cloned().collect(); + r.opts = self; + Box::new(r) + } +} + +impl Cpp { + fn new() -> Cpp { + Cpp::default() + } + + fn interface<'a>( + &'a mut self, + identifier: Identifier<'a>, + wasm_import_module: Option<&'a str>, + resolve: &'a Resolve, + in_import: bool, + ) -> InterfaceGenerator<'a> { + let mut sizes = SizeAlign::default(); + sizes.fill(resolve); + + InterfaceGenerator { + identifier, + wasm_import_module, + src: Source::default(), + in_import, + gen: self, + sizes, + resolve, + return_pointer_area_size: 0, + return_pointer_area_align: 0, + } + } + + fn emit_modules(&mut self, modules: &BTreeMap, Vec>) { + let mut map = BTreeMap::new(); + for (pkg, modules) in modules { + match pkg { + Some(pkg) => { + let prev = map + .entry(&pkg.namespace) + .or_insert(BTreeMap::new()) + .insert(&pkg.name, modules); + assert!(prev.is_none()); + } + None => { + for module in modules { + uwriteln!(self.src, "{module}"); + } + } + } + } + for (ns, pkgs) in map { + uwriteln!(self.src, "namespace {} {{", ns.to_snake_case()); + for (pkg, modules) in pkgs { + uwriteln!(self.src, "namespace {} {{", pkg.to_snake_case()); + for module in modules { + uwriteln!(self.src, "{module}"); + } + uwriteln!(self.src, "}}"); + } + uwriteln!(self.src, "}}"); + } + } +} + +impl WorldGenerator for Cpp { + fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { + self.world = Some(world); + let version = env!("CARGO_PKG_VERSION"); + uwriteln!( + self.src, + "// Generated by `wit-bindgen` {version}. DO NOT EDIT!" + ); + uwriteln!( + self.src, + r#"#include "{}_cpp.h" + #include + + 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; + }} + + "#, + resolve.worlds[world].name.to_snake_case(), + ); + self.types.analyze(resolve); + } + + fn import_interface( + &mut self, + resolve: &Resolve, + name: &WorldKey, + id: InterfaceId, + _files: &mut Files, + ) { + let wasm_import_module = resolve.name_world_key(name); + let mut gen = self.interface( + Identifier::Interface(id, name), + Some(&wasm_import_module), + resolve, + true, + ); + let (snake, path_to_root, pkg) = gen.start_append_submodule(name); + gen.types(id); + + gen.generate_imports(resolve.interfaces[id].functions.values()); + + gen.finish_append_submodule(&snake, &path_to_root, pkg); + } + + fn import_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) { + self.import_funcs_called = true; + + let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true); + + gen.generate_imports(funcs.iter().map(|(_, func)| *func)); + + let src = gen.finish(); + self.src.push_str(&src); + } + + fn export_interface( + &mut self, + resolve: &Resolve, + name: &WorldKey, + id: InterfaceId, + _files: &mut Files, + ) -> std::result::Result<(), anyhow::Error> { + let (pkg, inner_name) = match name { + WorldKey::Name(name) => (None, name), + WorldKey::Interface(id) => { + let interface = &resolve.interfaces[*id]; + ( + Some(&resolve.packages[interface.package.unwrap()].name), + interface.name.as_ref().unwrap(), + ) + } + }; + let path = format!( + "{}{inner_name}", + if let Some(pkg) = pkg { + format!("{}::{}::", pkg.namespace, pkg.name) + } else { + String::new() + } + ); + let impl_name = self + .opts + .interface_exports + .get(&path) + .cloned() + .or_else(|| self.opts.stubs.then(|| "Stub".to_owned())) + .ok_or_else(|| format!("interface export implementation required for `{path}`")); + let mut gen = self.interface(Identifier::Interface(id, name), None, resolve, false); + let (snake, path_to_root, pkg) = gen.start_append_submodule(name); + gen.types(id); + gen.generate_exports( + &inner_name.to_upper_camel_case(), + Some(&path), + impl_name.as_deref(), + Some(name), + resolve.interfaces[id].functions.values(), + ); + gen.finish_append_submodule(&snake, &path_to_root, pkg); + Ok(()) + } + + fn export_funcs( + &mut self, + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], + _files: &mut Files, + ) -> std::result::Result<(), anyhow::Error> { + let world_name = &resolve.worlds[world].name; + let impl_name = self + .opts + .world_exports + .clone() + .or_else(|| self.opts.stubs.then(|| "Stub".to_owned())) + .ok_or_else(|| format!("world export implementation required")); + let trait_name = world_name.to_upper_camel_case(); + let mut gen = self.interface(Identifier::World(world), None, resolve, false); + gen.generate_exports( + &trait_name, + None, + impl_name.as_deref(), + None, + funcs.iter().map(|f| f.1), + ); + let src = gen.finish(); + self.src.push_str(&src); + Ok(()) + } + + fn import_types( + &mut self, + resolve: &Resolve, + world: WorldId, + types: &[(&str, TypeId)], + _files: &mut Files, + ) { + let mut gen = self.interface(Identifier::World(world), None, resolve, true); + for (name, ty) in types { + gen.define_type(name, *ty); + } + let src = gen.finish(); + self.src.push_str(&src); + } + + fn finish(&mut self, resolve: &Resolve, world: WorldId, files: &mut Files) { + if !self.import_funcs_called { + // We call `import_funcs` even if the world doesn't import any + // functions since one of the side effects of that method is to + // generate `struct`s for any imported resources. + self.import_funcs(resolve, world, &[], files); + } + + let name = &resolve.worlds[world].name; + let imports = mem::take(&mut self.import_modules); + self.emit_modules(&imports); + let _exports = mem::take(&mut self.export_modules); + // if !exports.is_empty() { + // self.src.push_str("pub mod exports {\n"); + // self.emit_modules(&exports); + // self.src.push_str("}\n"); + // } + + // The custom section name here must start with "component-type" but + // otherwise is attempted to be unique here to ensure that this doesn't get + // concatenated to other custom sections by LLD by accident since LLD will + // concatenate custom sections of the same name. + let mut producers = wasm_metadata::Producers::empty(); + producers.add( + "processed-by", + env!("CARGO_PKG_NAME"), + env!("CARGO_PKG_VERSION"), + ); + + let _component_type = wit_component::metadata::encode( + resolve, + world, + wit_component::StringEncoding::UTF8, + Some(&producers), + ) + .unwrap(); + + // if self.opts.stubs { + // self.src.push_str("\npub struct Stub;\n"); + // let world_id = world; + // let world = &resolve.worlds[world]; + // let mut funcs = Vec::new(); + // for (name, export) in world.exports.iter() { + // let (pkg, name) = match name { + // WorldKey::Name(name) => (None, name), + // WorldKey::Interface(id) => { + // let interface = &resolve.interfaces[*id]; + // ( + // Some(&resolve.packages[interface.package.unwrap()].name), + // interface.name.as_ref().unwrap(), + // ) + // } + // }; + // match export { + // WorldItem::Function(func) => { + // funcs.push(func); + // } + // WorldItem::Interface(id) => { + // for (resource, funcs) in + // group_by_resource(resolve.interfaces[*id].functions.values()) + // { + // let mut gen = + // self.interface(Identifier::World(world_id), None, resolve, false); + // gen.generate_stub(resource, pkg, name, true, &funcs); + // let stub = gen.finish(); + // self.src.push_str(&stub); + // } + // } + // WorldItem::Type(_) => unreachable!(), + // } + // } + + // for (resource, funcs) in group_by_resource(funcs.into_iter()) { + // let mut gen = self.interface(Identifier::World(world_id), None, resolve, false); + // gen.generate_stub(resource, None, &world.name, false, &funcs); + // let stub = gen.finish(); + // self.src.push_str(&stub); + // } + // } + + let src = mem::take(&mut self.src); + let module_name = name.to_snake_case(); + files.push(&format!("{module_name}.cpp"), src.as_bytes()); + } +} + +#[derive(Clone)] +enum Identifier<'a> { + World(WorldId), + Interface(InterfaceId, &'a WorldKey), +} + +struct InterfaceGenerator<'a> { + src: Source, + identifier: Identifier<'a>, + in_import: bool, + sizes: SizeAlign, + gen: &'a mut Cpp, + wasm_import_module: Option<&'a str>, + resolve: &'a Resolve, + return_pointer_area_size: usize, + return_pointer_area_align: usize, +} + +impl InterfaceGenerator<'_> { + fn generate_exports<'a>( + &mut self, + trait_name: &str, + path: Option<&str>, + impl_name: Result<&str, &String>, + interface_name: Option<&WorldKey>, + funcs: impl Iterator, + ) { + let mut by_resource = group_by_resource(funcs); + + // Make sure we generate code for resources with no methods: + match self.identifier { + Identifier::Interface(id, _) => { + for ty in self.resolve.interfaces[id].types.values() { + if let TypeDefKind::Resource = &self.resolve.types[*ty].kind { + by_resource.entry(Some(*ty)).or_default(); + } + } + } + Identifier::World(id) => { + let world = &self.resolve.worlds[id]; + for item in world.exports.values() { + if let WorldItem::Type(_) = item { + // As of this writing, there's no way this can be represented in WIT, but it should be easy + // to handle if that changes. + todo!() + } + } + } + } + + for (resource, funcs) in by_resource { + let trait_name = if let Some(ty) = resource { + self.resolve.types[ty] + .name + .as_deref() + .unwrap() + .to_upper_camel_case() + } else { + trait_name.to_owned() + }; + let mut saw_export = false; + uwriteln!(self.src, "pub trait {trait_name} {{"); + for &func in &funcs { + if self.gen.skip.contains(&func.name) { + continue; + } + saw_export = true; + let mut sig = FnSig::default(); + sig.use_item_name = true; + sig.private = true; + if let FunctionKind::Method(_) = &func.kind { + // sig.self_arg = Some("&self".into()); + // sig.self_is_first_param = true; + } + self.print_signature(func, TypeMode::Owned, &sig); + self.src.push_str(";\n"); + } + uwriteln!(self.src, "}}"); + + if saw_export || resource.is_some() { + let mut path_to_root = String::new(); + if let Some(key) = interface_name { + if !self.in_import { + path_to_root.push_str("super::"); + } + if let WorldKey::Interface(_) = key { + path_to_root.push_str("super::super::"); + } + path_to_root.push_str("super::"); + } + if let Some(ty) = resource { + let name = self.resolve.types[ty].name.as_deref().unwrap(); + let path = if let Some(path) = path { + format!("{path}::{name}") + } else { + name.to_owned() + }; + let impl_name = self + .gen + .opts + .resource_exports + .get(&path) + .cloned() + .or_else(|| self.gen.opts.stubs.then(|| "Stub".to_owned())) + .ok_or_else(|| { + format!("resource export implementation required for `{path}`") + }) + .unwrap(); + + uwriteln!( + self.src, + "use {path_to_root}{impl_name} as Rep{trait_name};" + ); + } else { + let impl_name = impl_name.unwrap(); + uwriteln!( + self.src, + "use {path_to_root}{impl_name} as {trait_name}Impl;" + ); + } + if saw_export { + self.src.push_str("const _: () = {\n"); + for &func in &funcs { + self.generate_guest_export(func, interface_name, &trait_name); + } + self.src.push_str("};\n"); + } + + if let Some(ty) = resource { + self.finish_resource_export(ty); + } + } + } + } + + fn make_export_name(input: &str) -> String { + input + .chars() + .map(|c| match c { + 'A'..='Z' | 'a'..='z' | '0'..='9' => c, + _ => '_', + }) + .collect() + } + + fn export_name2(module_name: &str, name: &str) -> String { + let mut res = Self::make_export_name(module_name); + res.push('_'); + res.push_str(&Self::make_export_name(name)); + res + } + + fn declare_import2( + module_name: &str, + name: &str, + args: &str, + result: &str, + ) -> (String, String) { + let extern_name = Self::export_name2(module_name, name); + let import = format!("extern __attribute__((import_module(\"{module_name}\")))\n __attribute__((import_name(\"{name}\")))\n {result} {extern_name}({args});\n"); + (extern_name, import) + } + + fn generate_imports<'a>(&mut self, funcs: impl Iterator) { + let wasm_import_module = self.wasm_import_module.unwrap(); + let mut by_resource = group_by_resource(funcs); + + // Make sure we generate code for resources with no methods: + match self.identifier { + Identifier::Interface(id, _) => { + for ty in self.resolve.interfaces[id].types.values() { + if let TypeDefKind::Resource = &self.resolve.types[*ty].kind { + by_resource.entry(Some(*ty)).or_default(); + } + } + } + Identifier::World(id) => { + let world = &self.resolve.worlds[id]; + for item in world.imports.values() { + if let WorldItem::Type(ty) = item { + if let TypeDefKind::Resource = &self.resolve.types[*ty].kind { + by_resource.entry(Some(*ty)).or_default(); + } + } + } + } + } + + for (resource, funcs) in by_resource { + if let Some(resource) = resource { + let name = self.resolve.types[resource].name.as_deref().unwrap(); + + let camel = name.to_upper_camel_case(); + + let (name_drop, code) = Self::declare_import2( + wasm_import_module, + &format!("[resource-drop]{name}"), + "int32_t", + "void", + ); + // destructor + uwriteln!( + self.src, + r#"{camel}::~{camel}() {{ + {code} + if (handle>=0) + {name_drop}(handle); + }} + "# + ); + // construct from handle (in binding) + let world = self + .gen + .world + .map(|w| &self.resolve.worlds[w].name) + .unwrap() + .to_snake_case(); + let base_name = format!("{world}::{RESOURCE_BASE_CLASS_NAME}"); + uwriteln!( + self.src, + r#"{camel}::{camel}({base_name}&& handle) : {base_name}(std::move(handle)) {{}}"# + ); + } + for func in funcs { + self.generate_guest_import(func); + } + if resource.is_some() { + self.src.push_str("}\n"); + } + } + } + + fn finish(&mut self) -> String { + // if self.return_pointer_area_align > 0 { + // uwrite!( + // self.src, + // " + // #[allow(unused_imports)] + // use wit_bindgen::rt::{{alloc, vec::Vec, string::String}}; + + // #[repr(align({align}))] + // struct _RetArea([u8; {size}]); + // static mut _RET_AREA: _RetArea = _RetArea([0; {size}]); + // ", + // align = self.return_pointer_area_align, + // size = self.return_pointer_area_size, + // ); + // } + + mem::take(&mut self.src).into() + } + + fn finish_resource_export(&mut self, id: TypeId) { + let _info = self.gen.resources.entry(id).or_default(); + let name = self.resolve.types[id].name.as_deref().unwrap(); + let _camel = name.to_upper_camel_case(); + let _snake = to_rust_ident(name); + let _export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); + let _interface_name = if let TypeOwner::Interface(id) = self.resolve.types[id].owner { + &self.gen.interface_names[&id] + } else { + unreachable!() + }; + } + fn start_append_submodule(&mut self, name: &WorldKey) -> (String, String, Option) { + let snake = match name { + WorldKey::Name(name) => to_rust_ident(name), + WorldKey::Interface(id) => { + to_rust_ident(self.resolve.interfaces[*id].name.as_ref().unwrap()) + } + }; + let mut path_to_root = String::from("super::"); + let pkg = match name { + WorldKey::Name(_) => None, + WorldKey::Interface(id) => { + let pkg = self.resolve.interfaces[*id].package.unwrap(); + Some(self.resolve.packages[pkg].name.clone()) + } + }; + if let Identifier::Interface(id, _) = self.identifier { + let mut path = String::new(); + if !self.in_import { + path.push_str("exports::"); + path_to_root.push_str("super::"); + } + if let Some(name) = &pkg { + path.push_str(&format!( + "{}::{}::", + name.namespace.to_snake_case(), + name.name.to_snake_case() + )); + path_to_root.push_str("super::super::"); + } + path.push_str(&snake); + self.gen.interface_names.insert(id, path); + } + (snake, path_to_root, pkg) + } + + fn finish_append_submodule( + mut self, + snake: &str, + _path_to_root: &str, + pkg: Option, + ) { + let module = self.finish(); + let module = format!( + " + namespace {snake} {{ + {module} + ", + ); + let map = if self.in_import { + &mut self.gen.import_modules + } else { + &mut self.gen.export_modules + }; + map.entry(pkg).or_insert(Vec::new()).push(module); + } + + // fn print_signature_cpp( + // &mut self, + // func: &Function, + // param_mode: TypeMode, + // sig: &FnSig, + // ) -> Vec { + // if !matches!(func.kind, FunctionKind::Constructor(_)) { + // self.print_results_cpp(&func.results, TypeMode::Owned); + // self.push_str(" "); + // } + // let params = self.print_docs_and_params_cpp(func, param_mode, &sig); + // params + // } + + // fn print_docs_and_params_cpp( + // &mut self, + // func: &Function, + // param_mode: TypeMode, + // sig: &FnSig, + // ) -> Vec { + // // self.rustdoc(&func.docs); + // // self.rustdoc_params(&func.params, "Parameters"); + // // TODO: re-add this when docs are back + // // self.rustdoc_params(&func.results, "Return"); + + // let object = match &func.kind { + // FunctionKind::Freestanding => None, + // FunctionKind::Method(i) => Some(i), + // FunctionKind::Static(i) => Some(i), + // FunctionKind::Constructor(i) => Some(i), + // } + // .map(|i| { + // self.resolve.types[*i] + // .name + // .as_ref() + // .unwrap() + // .to_pascal_case() + // }) + // .unwrap_or_default(); + // let func_name = if sig.use_item_name { + // if let FunctionKind::Constructor(i) = &func.kind { + // format!("{object}::{object}") + // } else { + // format!("{object}::{}", func.item_name().to_pascal_case()) + // } + // } else { + // func.name.to_pascal_case() + // }; + // self.push_str(&func_name); + // if let Some(generics) = &sig.generics { + // self.push_str(generics); + // } + // self.push_str("("); + // if let Some(arg) = &sig.self_arg { + // self.push_str(arg); + // self.push_str(","); + // } + // let mut params = Vec::new(); + // for (i, (name, param)) in func.params.iter().enumerate() { + // params.push(name.clone()); + // if i == 0 && sig.self_is_first_param { + // // params.push("self".to_string()); + // continue; + // } + // if i == 0 && name == "self" { + // continue; + // } + // let name = to_rust_ident(name); + // self.print_ty_cpp(param, param_mode); + // self.push_str(" "); + // self.push_str(&name); + // if i + 1 != func.params.len() { + // self.push_str(","); + // } + // } + // self.push_str(")"); + // params + // } + + // fn print_tyid_cpp(&mut self, id: TypeId, mode: TypeMode) { + // let info = self.info(id); + // let lt = self.lifetime_for(&info, mode); + // let ty = &RustGenerator::resolve(self).types[id]; + // if ty.name.is_some() { + // // If this type has a list internally, no lifetime is being printed, + // // but we're in a borrowed mode, then that means we're in a borrowed + // // context and don't want ownership of the type but we're using an + // // owned type definition. Inject a `&` in front to indicate that, at + // // the API level, ownership isn't required. + // if info.has_list && lt.is_none() { + // if let TypeMode::AllBorrowed(lt) | TypeMode::LeafBorrowed(lt) = mode { + // self.push_str("&"); + // if lt != "'_" { + // self.push_str(lt); + // self.push_str(" "); + // } + // } + // } + // let name = self.type_path(id, lt.is_none()); + // self.push_str(&name); + + // // If the type recursively owns data and it's a + // // variant/record/list, then we need to place the + // // lifetime parameter on the type as well. + // if info.has_list && needs_generics(RustGenerator::resolve(self), &ty.kind) { + // self.print_generics(lt); + // } + + // return; + + // fn needs_generics(resolve: &Resolve, ty: &TypeDefKind) -> bool { + // match ty { + // TypeDefKind::Variant(_) + // | TypeDefKind::Record(_) + // | TypeDefKind::Option(_) + // | TypeDefKind::Result(_) + // | TypeDefKind::Future(_) + // | TypeDefKind::Stream(_) + // | TypeDefKind::List(_) + // | TypeDefKind::Flags(_) + // | TypeDefKind::Enum(_) + // | TypeDefKind::Tuple(_) + // | TypeDefKind::Union(_) => true, + // TypeDefKind::Type(Type::Id(t)) => { + // needs_generics(resolve, &resolve.types[*t].kind) + // } + // TypeDefKind::Type(Type::String) => true, + // TypeDefKind::Resource | TypeDefKind::Handle(_) | TypeDefKind::Type(_) => false, + // TypeDefKind::Unknown => unreachable!(), + // } + // } + // } + + // match &ty.kind { + // TypeDefKind::List(t) => self.print_list(t, mode), + + // TypeDefKind::Option(t) => { + // self.push_str("Option<"); + // self.print_ty(t, mode); + // self.push_str(">"); + // } + + // TypeDefKind::Result(r) => { + // self.push_str("Result<"); + // self.print_optional_ty(r.ok.as_ref(), mode); + // self.push_str(","); + // self.print_optional_ty(r.err.as_ref(), mode); + // self.push_str(">"); + // } + + // TypeDefKind::Variant(_) => panic!("unsupported anonymous variant"), + + // // Tuple-like records are mapped directly to Rust tuples of + // // types. Note the trailing comma after each member to + // // appropriately handle 1-tuples. + // TypeDefKind::Tuple(t) => { + // self.push_str("("); + // for ty in t.types.iter() { + // self.print_ty(ty, mode); + // self.push_str(","); + // } + // self.push_str(")"); + // } + // TypeDefKind::Resource => { + // panic!("unsupported anonymous type reference: resource") + // } + // TypeDefKind::Record(_) => { + // panic!("unsupported anonymous type reference: record") + // } + // TypeDefKind::Flags(_) => { + // panic!("unsupported anonymous type reference: flags") + // } + // TypeDefKind::Enum(_) => { + // panic!("unsupported anonymous type reference: enum") + // } + // TypeDefKind::Union(_) => { + // panic!("unsupported anonymous type reference: union") + // } + // TypeDefKind::Future(ty) => { + // self.push_str("Future<"); + // self.print_optional_ty(ty.as_ref(), mode); + // self.push_str(">"); + // } + // TypeDefKind::Stream(stream) => { + // self.push_str("Stream<"); + // self.print_optional_ty(stream.element.as_ref(), mode); + // self.push_str(","); + // self.print_optional_ty(stream.end.as_ref(), mode); + // self.push_str(">"); + // } + + // TypeDefKind::Handle(Handle::Own(ty)) => { + // self.mark_resource_owned(*ty); + // self.print_ty(&Type::Id(*ty), mode); + // } + + // TypeDefKind::Handle(Handle::Borrow(ty)) => { + // self.push_str("&"); + // self.print_ty(&Type::Id(*ty), mode); + // } + + // TypeDefKind::Type(t) => self.print_ty(t, mode), + + // TypeDefKind::Resource => { + // todo!("implement resources") + // } + + // TypeDefKind::Unknown => unreachable!(), + // } + // } + + // fn print_ty_cpp(&mut self, ty: &Type, mode: TypeMode) { + // match ty { + // Type::Id(t) => self.print_tyid_cpp(*t, mode), + // Type::Bool => self.push_str("bool"), + // Type::U8 => self.push_str("uint8_t"), + // Type::U16 => self.push_str("uint16_t"), + // Type::U32 => self.push_str("uint32_t"), + // Type::U64 => self.push_str("uint64_t"), + // Type::S8 => self.push_str("int8_t"), + // Type::S16 => self.push_str("int16_t"), + // Type::S32 => self.push_str("int32_t"), + // Type::S64 => self.push_str("int64_t"), + // Type::Float32 => self.push_str("float"), + // Type::Float64 => self.push_str("double"), + // Type::Char => self.push_str("int32_t"), + // Type::String => match mode { + // TypeMode::AllBorrowed(lt) | TypeMode::LeafBorrowed(lt) => { + // self.push_str("std::string_view"); + // } + // TypeMode::Owned => { + // self.push_str("std::string"); + // } + // }, + // } + // } + + // fn print_option_ty_cpp(&mut self, ty: Option<&Type>, mode: TypeMode) { + // match ty { + // Some(ty) => self.print_ty_cpp(ty, mode), + // None => self.push_str("void"), + // } + // } + + // fn print_results_cpp(&mut self, results: &Results, mode: TypeMode) { + // match results.len() { + // 0 | 1 => self.print_option_ty_cpp(results.iter_types().next(), mode), + // _ => todo!(), + // } + // } + + fn generate_guest_import(&mut self, func: &Function) { + if self.gen.skip.contains(&func.name) { + return; + } + + let mut sig = FnSig::default(); + let param_mode = TypeMode::AllBorrowed("'_"); + match &func.kind { + FunctionKind::Freestanding => {} + FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::Constructor(_) => { + sig.use_item_name = true; + // if let FunctionKind::Method(_) = &func.kind { + // sig.self_arg = Some("&self".into()); + // sig.self_is_first_param = true; + // } + } + } + // self.src.push_str("#[allow(clippy::all)]\n"); + let params = self.print_signature(func, param_mode, &sig); + if matches!(func.kind, FunctionKind::Method(_)) { + self.src.push_str("const"); + } + self.src.push_str("{\n"); + + let mut f = FunctionBindgen::new(self, params, None); + abi::call( + f.gen.resolve, + AbiVariant::GuestImport, + LiftLower::LowerArgsLiftResults, + func, + &mut f, + ); + let FunctionBindgen { + // needs_cleanup_list, + src, + import_return_pointer_area_size, + import_return_pointer_area_align, + .. + } = f; + + // if needs_cleanup_list { + // self.src.push_str("let mut cleanup_list = Vec::new();\n"); + // } + if import_return_pointer_area_size > 0 { + let align = import_return_pointer_area_align.max(4); + let elems = (import_return_pointer_area_size + (align - 1)) / align; + let tp = match align { + 4 => "uint32_t", + 8 => "uint64_t", + _ => todo!(), + }; + uwriteln!(self.src, " {tp} ret_area[{elems}];") + } + // uwrite!( + // self.src, + // " + // #[repr(align({import_return_pointer_area_align}))] + // struct RetArea([u8; {import_return_pointer_area_size}]); + // let mut ret_area = ::core::mem::MaybeUninit::::uninit(); + // ", + // ); + // } + self.src.push_str(&String::from(src)); + + // self.src.push_str("}\n"); + self.src.push_str("}\n"); + } + + fn generate_guest_export( + &mut self, + func: &Function, + _interface_name: Option<&WorldKey>, + _trait_name: &str, + ) { + if self.gen.skip.contains(&func.name) { + return; + } + todo!(); + + // let name_snake = func.name.to_snake_case().replace('.', "_"); + // let wasm_module_export_name = interface_name.map(|k| self.resolve.name_world_key(k)); + // let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); + // let export_name = func.core_export_name(wasm_module_export_name.as_deref()); + // uwrite!( + // self.src, + // " + // #[doc(hidden)] + // #[export_name = \"{export_prefix}{export_name}\"] + // #[allow(non_snake_case)] + // unsafe extern \"C\" fn __export_{name_snake}(\ + // ", + // ); + + // let sig = self.resolve.wasm_signature(AbiVariant::GuestExport, func); + // let mut params = Vec::new(); + // for (i, param) in sig.params.iter().enumerate() { + // let name = format!("arg{}", i); + // uwrite!(self.src, "{name}: {},", wasm_type(*param)); + // params.push(name); + // } + // self.src.push_str(")"); + + // match sig.results.len() { + // 0 => {} + // 1 => { + // uwrite!(self.src, " -> {}", wasm_type(sig.results[0])); + // } + // _ => unimplemented!(), + // } + + // self.push_str(" {"); + + // let mut f = FunctionBindgen::new(self, params, Some(trait_name)); + // f.gen.resolve.call( + // AbiVariant::GuestExport, + // LiftLower::LiftArgsLowerResults, + // func, + // &mut f, + // ); + // let FunctionBindgen { + // needs_cleanup_list, + // src, + // .. + // } = f; + // assert!(!needs_cleanup_list); + // self.src.push_str(&String::from(src)); + // self.src.push_str("}\n"); + + // if self.resolve.guest_export_needs_post_return(func) { + // let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); + // let mut params = Vec::new(); + // for (i, result) in sig.results.iter().enumerate() { + // let name = format!("arg{}", i); + // uwrite!(self.src, "{name}: {},", wasm_type(*result)); + // params.push(name); + // } + // self.src.push_str(") {\n"); + + // let mut f = FunctionBindgen::new(self, params, Some(trait_name)); + // f.gen.resolve.post_return(func, &mut f); + // let FunctionBindgen { + // needs_cleanup_list, + // src, + // .. + // } = f; + // assert!(!needs_cleanup_list); + // self.src.push_str(&String::from(src)); + // self.src.push_str("}\n"); + // self.src.push_str("};\n"); + // } + } + + // fn generate_stub( + // &mut self, + // resource: Option, + // pkg: Option<&PackageName>, + // name: &str, + // in_interface: bool, + // funcs: &[&Function], + // ) { + // let path = if let Some(pkg) = pkg { + // format!( + // "{}::{}::{}", + // to_rust_ident(&pkg.namespace), + // to_rust_ident(&pkg.name), + // to_rust_ident(name), + // ) + // } else { + // to_rust_ident(name) + // }; + + // let name = resource + // .map(|ty| { + // self.resolve.types[ty] + // .name + // .as_deref() + // .unwrap() + // .to_upper_camel_case() + // }) + // .unwrap_or_else(|| name.to_upper_camel_case()); + + // let qualified_name = if in_interface { + // format!("exports::{path}::{name}") + // } else { + // name + // }; + + // uwriteln!(self.src, "impl {qualified_name} for Stub {{"); + + // for &func in funcs { + // if self.gen.skip.contains(&func.name) { + // continue; + // } + // let mut sig = FnSig::default(); + // sig.use_item_name = true; + // sig.private = true; + // if let FunctionKind::Method(_) = &func.kind { + // // sig.self_arg = Some("&self".into()); + // // sig.self_is_first_param = true; + // } + // self.print_signature(func, TypeMode::Owned, &sig); + // self.src.push_str("{ unreachable!() }\n"); + // } + + // self.src.push_str("}\n"); + // } +} + +impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { + fn resolve(&self) -> &'a Resolve { + self.resolve + } + + fn ownership(&self) -> Ownership { + self.gen.opts.ownership + } + + fn path_to_interface(&self, interface: InterfaceId) -> Option { + let mut path = String::new(); + if let Identifier::Interface(cur, name) = self.identifier { + if cur == interface { + return None; + } + if !self.in_import { + //path.push_str("super::"); + } + match name { + WorldKey::Name(_) => { + //path.push_str("super::"); + } + WorldKey::Interface(_) => { + //path.push_str("super::super::super::"); + } + } + } + let name = &self.gen.interface_names[&interface]; + path.push_str(&name); + Some(path) + } + + fn is_exported_resource(&self, ty: TypeId) -> bool { + matches!( + self.gen + .resources + .get(&dealias(self.resolve, ty)) + .map(|info| info.direction), + Some(Direction::Export) + ) + } + + // fn add_own(&mut self, resource: TypeId, handle: TypeId) { + // self.gen + // .resources + // .entry(dealias(self.resolve, resource)) + // .or_default() + // .own = Some(handle); + // } + + fn push_str(&mut self, s: &str) { + self.src.push_str(s); + } + + fn info(&self, ty: TypeId) -> TypeInfo { + self.gen.types.get(ty) + } + + fn types_mut(&mut self) -> &mut Types { + &mut self.gen.types + } + + fn print_borrowed_slice( + &mut self, + mutbl: bool, + ty: &Type, + lifetime: &'static str, + mode: TypeMode, + ) { + self.print_rust_slice(mutbl, ty, lifetime, mode); + } + + fn print_borrowed_str(&mut self, _lifetime: &'static str) { + self.push_str("&"); + // if self.gen.opts.raw_strings { + // self.push_str("[u8]"); + // } else { + self.push_str("str"); + // } + } + + fn push_vec_name(&mut self) { + self.push_str("std::vector"); + } + + fn push_string_name(&mut self) { + self.push_str("std::string"); + } + + fn mark_resource_owned(&mut self, resource: TypeId) { + self.gen + .resources + .entry(dealias(self.resolve, resource)) + .or_default() + .owned = true; + } + + fn print_signature( + &mut self, + func: &Function, + param_mode: TypeMode, + sig: &FnSig, + ) -> Vec { + if !matches!(func.kind, FunctionKind::Constructor(_)) { + self.print_results(&func.results, TypeMode::Owned); + self.push_str(" "); + } + let params = self.print_docs_and_params(func, param_mode, &sig); + params + } + + fn print_docs_and_params( + &mut self, + func: &Function, + param_mode: TypeMode, + sig: &FnSig, + ) -> Vec { + // self.rustdoc(&func.docs); + // self.rustdoc_params(&func.params, "Parameters"); + // TODO: re-add this when docs are back + // self.rustdoc_params(&func.results, "Return"); + + let object = match &func.kind { + FunctionKind::Freestanding => None, + FunctionKind::Method(i) => Some(i), + FunctionKind::Static(i) => Some(i), + FunctionKind::Constructor(i) => Some(i), + } + .map(|i| { + self.resolve.types[*i] + .name + .as_ref() + .unwrap() + .to_pascal_case() + }) + .unwrap_or_default(); + let func_name = if sig.use_item_name { + if let FunctionKind::Constructor(_i) = &func.kind { + format!("{object}::{object}") + } else { + format!("{object}::{}", func.item_name().to_pascal_case()) + } + } else { + func.name.to_pascal_case() + }; + self.push_str(&func_name); + if let Some(generics) = &sig.generics { + self.push_str(generics); + } + self.push_str("("); + if let Some(arg) = &sig.self_arg { + self.push_str(arg); + self.push_str(","); + } + let mut params = Vec::new(); + for (i, (name, param)) in func.params.iter().enumerate() { + params.push(to_rust_ident(name)); + if i == 0 && sig.self_is_first_param { + // params.push("self".to_string()); + continue; + } + if i == 0 && name == "self" { + continue; + } + let name = to_rust_ident(name); + self.print_ty(param, param_mode); + self.push_str(" "); + self.push_str(&name); + if i + 1 != func.params.len() { + self.push_str(","); + } + } + self.push_str(")"); + params + } + + fn print_tyid(&mut self, id: TypeId, mode: TypeMode) { + let info = self.info(id); + let lt = self.lifetime_for(&info, mode); + let ty = &RustGenerator::resolve(self).types[id]; + if ty.name.is_some() { + // If this type has a list internally, no lifetime is being printed, + // but we're in a borrowed mode, then that means we're in a borrowed + // context and don't want ownership of the type but we're using an + // owned type definition. Inject a `&` in front to indicate that, at + // the API level, ownership isn't required. + if info.has_list && lt.is_none() { + if let TypeMode::AllBorrowed(lt) | TypeMode::LeafBorrowed(lt) = mode { + self.push_str("&"); + if lt != "'_" { + self.push_str(lt); + self.push_str(" "); + } + } + } + let name = self.type_path(id, lt.is_none()); + self.push_str(&name); + + // If the type recursively owns data and it's a + // variant/record/list, then we need to place the + // lifetime parameter on the type as well. + if info.has_list && needs_generics(RustGenerator::resolve(self), &ty.kind) { + self.print_generics(lt); + } + + return; + + fn needs_generics(resolve: &Resolve, ty: &TypeDefKind) -> bool { + match ty { + TypeDefKind::Variant(_) + | TypeDefKind::Record(_) + | TypeDefKind::Option(_) + | TypeDefKind::Result(_) + | TypeDefKind::Future(_) + | TypeDefKind::Stream(_) + | TypeDefKind::List(_) + | TypeDefKind::Flags(_) + | TypeDefKind::Enum(_) + | TypeDefKind::Tuple(_) => true, + TypeDefKind::Type(Type::Id(t)) => { + needs_generics(resolve, &resolve.types[*t].kind) + } + TypeDefKind::Type(Type::String) => true, + TypeDefKind::Resource | TypeDefKind::Handle(_) | TypeDefKind::Type(_) => false, + TypeDefKind::Unknown => unreachable!(), + } + } + } + + match &ty.kind { + TypeDefKind::List(t) => self.print_list(t, mode), + + TypeDefKind::Option(t) => { + self.push_str("std::option<"); + self.print_ty(t, mode); + self.push_str(">"); + } + + TypeDefKind::Result(r) => { + self.push_str("std::expected<"); + self.print_optional_ty(r.ok.as_ref(), mode); + self.push_str(","); + self.print_optional_ty(r.err.as_ref(), mode); + self.push_str(">"); + } + + TypeDefKind::Variant(_) => panic!("unsupported anonymous variant"), + + // Tuple-like records are mapped directly to Rust tuples of + // types. Note the trailing comma after each member to + // appropriately handle 1-tuples. + TypeDefKind::Tuple(t) => { + self.push_str("("); + for ty in t.types.iter() { + self.print_ty(ty, mode); + self.push_str(","); + } + self.push_str(")"); + } + TypeDefKind::Resource => { + panic!("unsupported anonymous type reference: resource") + } + TypeDefKind::Record(_) => { + panic!("unsupported anonymous type reference: record") + } + TypeDefKind::Flags(_) => { + panic!("unsupported anonymous type reference: flags") + } + TypeDefKind::Enum(_) => { + panic!("unsupported anonymous type reference: enum") + } + TypeDefKind::Future(ty) => { + self.push_str("Future<"); + self.print_optional_ty(ty.as_ref(), mode); + self.push_str(">"); + } + TypeDefKind::Stream(stream) => { + self.push_str("Stream<"); + self.print_optional_ty(stream.element.as_ref(), mode); + self.push_str(","); + self.print_optional_ty(stream.end.as_ref(), mode); + self.push_str(">"); + } + + TypeDefKind::Handle(Handle::Own(ty)) => { + self.mark_resource_owned(*ty); + self.print_ty(&Type::Id(*ty), mode); + } + + TypeDefKind::Handle(Handle::Borrow(ty)) => { + self.push_str("&"); + self.print_ty(&Type::Id(*ty), mode); + } + + TypeDefKind::Type(t) => self.print_ty(t, mode), + + // TypeDefKind::Resource => { + // todo!("implement resources") + // } + TypeDefKind::Unknown => unreachable!(), + } + } + + fn print_ty(&mut self, ty: &Type, mode: TypeMode) { + match ty { + Type::Id(t) => self.print_tyid(*t, mode), + Type::Bool => self.push_str("bool"), + Type::U8 => self.push_str("uint8_t"), + Type::U16 => self.push_str("uint16_t"), + Type::U32 => self.push_str("uint32_t"), + Type::U64 => self.push_str("uint64_t"), + Type::S8 => self.push_str("int8_t"), + Type::S16 => self.push_str("int16_t"), + Type::S32 => self.push_str("int32_t"), + Type::S64 => self.push_str("int64_t"), + Type::Float32 => self.push_str("float"), + Type::Float64 => self.push_str("double"), + Type::Char => self.push_str("int32_t"), + Type::String => match mode { + TypeMode::AllBorrowed(_lt) | TypeMode::LeafBorrowed(_lt) => { + self.push_str("std::string_view"); + } + TypeMode::Owned => { + self.push_str("std::string"); + } + TypeMode::HandlesBorrowed(_) => todo!(), + }, + } + } + + fn print_optional_ty(&mut self, ty: Option<&Type>, mode: TypeMode) { + match ty { + Some(ty) => self.print_ty(ty, mode), + None => self.push_str("void"), + } + } + + fn print_results(&mut self, results: &Results, mode: TypeMode) { + match results.len() { + 0 | 1 => self.print_optional_ty(results.iter_types().next(), mode), + _ => todo!(), + } + } + + fn wasm_type(&mut self, ty: WasmType) { + self.push_str(wasm_type(ty)); + } + + fn print_list(&mut self, ty: &Type, mode: TypeMode) { + let next_mode = if matches!(self.ownership(), Ownership::Owning) { + TypeMode::Owned + } else { + mode + }; + match mode { + TypeMode::AllBorrowed(lt) => { + self.print_borrowed_slice(false, ty, lt, next_mode); + } + TypeMode::LeafBorrowed(lt) => { + if RustGenerator::resolve(self).all_bits_valid(ty) { + self.print_borrowed_slice(false, ty, lt, next_mode); + } else { + self.push_vec_name(); + self.push_str("<"); + self.print_ty(ty, next_mode); + self.push_str(">"); + } + } + TypeMode::Owned => { + self.push_vec_name(); + self.push_str("<"); + self.print_ty(ty, next_mode); + self.push_str(">"); + } + TypeMode::HandlesBorrowed(_) => todo!(), + } + } + + fn print_rust_slice( + &mut self, + mutbl: bool, + ty: &Type, + _lifetime: &'static str, + mode: TypeMode, + ) { + self.push_str("std::vector<"); + self.print_ty(ty, mode); + self.push_str(">"); + if !mutbl { + self.push_str(" const "); + } + self.push_str("&"); + } +} + +fn wasm_type(ty: WasmType) -> &'static str { + match ty { + WasmType::I32 => "int32_t", + WasmType::I64 => "int64_t", + WasmType::F32 => "float", + WasmType::F64 => "double", + } +} + +impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { + fn resolve(&self) -> &'a Resolve { + self.resolve + } + + fn type_record(&mut self, _id: TypeId, _name: &str, _record: &Record, _docs: &Docs) { + //self.print_typedef_record(id, record, docs, false); + } + + fn type_resource(&mut self, id: TypeId, _name: &str, docs: &Docs) { + let entry = self + .gen + .resources + .entry(dealias(self.resolve, id)) + .or_default(); + if !self.in_import { + entry.direction = Direction::Export; + } + entry.docs = docs.clone(); + } + + fn type_tuple(&mut self, _id: TypeId, _name: &str, _tuple: &Tuple, _docs: &Docs) { + //self.print_typedef_tuple(id, tuple, docs); + } + + fn type_flags(&mut self, _id: TypeId, _name: &str, _flags: &Flags, _docs: &Docs) { + // self.src.push_str("wit_bindgen::bitflags::bitflags! {\n"); + // self.rustdoc(docs); + // let repr = RustFlagsRepr::new(flags); + // self.src.push_str(&format!( + // "pub struct {}: {repr} {{\n", + // name.to_upper_camel_case(), + // )); + // for (i, flag) in flags.flags.iter().enumerate() { + // self.rustdoc(&flag.docs); + // self.src.push_str(&format!( + // "const {} = 1 << {};\n", + // flag.name.to_shouty_snake_case(), + // i, + // )); + // } + // self.src.push_str("}\n"); + // self.src.push_str("}\n"); + } + + fn type_variant(&mut self, _id: TypeId, _name: &str, _variant: &Variant, _docs: &Docs) { + //self.print_typedef_variant(id, variant, docs, false); + } + + fn type_option(&mut self, _id: TypeId, _name: &str, _payload: &Type, _docs: &Docs) { + //self.print_typedef_option(id, payload, docs); + } + + fn type_result(&mut self, _id: TypeId, _name: &str, _result: &Result_, _docs: &Docs) { + //self.print_typedef_result(id, result, docs); + } + + fn type_enum(&mut self, _id: TypeId, _name: &str, _enum_: &Enum, _docs: &Docs) { + //self.print_typedef_enum(id, name, enum_, docs, &[], Box::new(|_| String::new())); + } + + fn type_alias(&mut self, _id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) { + //self.print_typedef_alias(id, ty, docs); + } + + fn type_list(&mut self, _id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) { + //self.print_type_list(id, ty, docs); + } + + fn type_builtin(&mut self, _id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) {} +} + +struct FunctionBindgen<'a, 'b> { + gen: &'b mut InterfaceGenerator<'a>, + params: Vec, + trait_name: Option<&'b str>, + src: Source, + blocks: Vec, + block_storage: Vec<(Source, Vec<(String, String)>)>, + tmp: usize, + needs_cleanup_list: bool, + cleanup: Vec<(String, String)>, + import_return_pointer_area_size: usize, + import_return_pointer_area_align: usize, +} + +impl<'a, 'b> FunctionBindgen<'a, 'b> { + fn new( + gen: &'b mut InterfaceGenerator<'a>, + params: Vec, + trait_name: Option<&'b str>, + ) -> FunctionBindgen<'a, 'b> { + FunctionBindgen { + gen, + params, + trait_name, + src: Default::default(), + blocks: Vec::new(), + block_storage: Vec::new(), + tmp: 0, + needs_cleanup_list: false, + cleanup: Vec::new(), + import_return_pointer_area_size: 0, + import_return_pointer_area_align: 0, + } + } + + fn emit_cleanup(&mut self) { + // for (ptr, layout) in mem::take(&mut self.cleanup) { + // self.push_str(&format!( + // "if {layout}.size() != 0 {{\nalloc::dealloc({ptr}, {layout});\n}}\n" + // )); + // } + // if self.needs_cleanup_list { + // self.push_str( + // "for (ptr, layout) in cleanup_list {\n + // if layout.size() != 0 {\n + // alloc::dealloc(ptr, layout);\n + // }\n + // }\n", + // ); + // } + } + + fn wasm_type_cpp(ty: WasmType) -> &'static str { + wit_bindgen_c::wasm_type(ty) + // match ty { + // WasmType::I32 => "int32_t", + // WasmType::I64 => "int64_t", + // WasmType::F32 => "float", + // WasmType::F64 => "double", + // } + } + + 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::wasm_type_cpp(*param)); + if n + 1 != params.len() { + args.push_str(", "); + } + } + let result = if results.is_empty() { + "void" + } else { + Self::wasm_type_cpp(results[0]) + }; + let (name, code) = InterfaceGenerator::declare_import2(module_name, name, &args, result); + self.src.push_str(&code); + name + // Define the actual function we're calling inline + //todo!(); + // let mut sig = "(".to_owned(); + // for param in params.iter() { + // sig.push_str("_: "); + // sig.push_str(wasm_type(*param)); + // sig.push_str(", "); + // } + // sig.push_str(")"); + // assert!(results.len() < 2); + // for result in results.iter() { + // sig.push_str(" -> "); + // sig.push_str(wasm_type(*result)); + // } + // uwriteln!( + // self.src, + // " + // #[cfg(target_arch = \"wasm32\")] + // #[link(wasm_import_module = \"{module_name}\")] + // extern \"C\" {{ + // #[link_name = \"{name}\"] + // fn wit_import{sig}; + // }} + + // #[cfg(not(target_arch = \"wasm32\"))] + // fn wit_import{sig} {{ unreachable!() }} + // " + // ); + // "wit_import".to_string() + } +} + +impl RustFunctionGenerator for FunctionBindgen<'_, '_> { + fn push_str(&mut self, s: &str) { + self.src.push_str(s); + } + + fn tmp(&mut self) -> usize { + let ret = self.tmp; + self.tmp += 1; + ret + } + + fn rust_gen(&self) -> &dyn RustGenerator { + self.gen + } + + fn lift_lower(&self) -> LiftLower { + if self.gen.in_import { + LiftLower::LowerArgsLiftResults + } else { + LiftLower::LiftArgsLowerResults + } + } +} + +impl Bindgen for FunctionBindgen<'_, '_> { + type Operand = String; + + fn push_block(&mut self) { + let prev_src = mem::take(&mut self.src); + let prev_cleanup = mem::take(&mut self.cleanup); + self.block_storage.push((prev_src, prev_cleanup)); + } + + fn finish_block(&mut self, operands: &mut Vec) { + if self.cleanup.len() > 0 { + self.needs_cleanup_list = true; + self.push_str("cleanup_list.extend_from_slice(&["); + for (ptr, layout) in mem::take(&mut self.cleanup) { + self.push_str("("); + self.push_str(&ptr); + self.push_str(", "); + self.push_str(&layout); + self.push_str("),"); + } + self.push_str("]);\n"); + } + let (prev_src, prev_cleanup) = self.block_storage.pop().unwrap(); + let src = mem::replace(&mut self.src, prev_src); + self.cleanup = prev_cleanup; + let expr = match operands.len() { + 0 => "()".to_string(), + 1 => operands[0].clone(), + _ => format!("({})", operands.join(", ")), + }; + if src.is_empty() { + self.blocks.push(expr); + } else if operands.is_empty() { + self.blocks.push(format!("{{\n{}\n}}", &src[..])); + } else { + self.blocks.push(format!("{{\n{}\n{}\n}}", &src[..], expr)); + } + } + + fn return_pointer(&mut self, size: usize, align: usize) -> String { + let tmp = self.tmp(); + + // Imports get a per-function return area to facilitate using the + // stack whereas exports use a per-module return area to cut down on + // stack usage. Note that for imports this also facilitates "adapter + // modules" for components to not have data segments. + if self.gen.in_import { + self.import_return_pointer_area_size = self.import_return_pointer_area_size.max(size); + self.import_return_pointer_area_align = + self.import_return_pointer_area_align.max(align); + uwriteln!(self.src, "auto ptr{tmp} = (int32_t)&ret_area;"); + } else { + todo!(); + // self.gen.return_pointer_area_size = self.gen.return_pointer_area_size.max(size); + // self.gen.return_pointer_area_align = self.gen.return_pointer_area_align.max(align); + // uwriteln!(self.src, "auto ptr{tmp} = _RET_AREA.0.as_mut_ptr() as i32;"); + } + format!("ptr{}", tmp) + } + + fn sizes(&self) -> &SizeAlign { + &self.gen.sizes + } + + fn is_list_canonical(&self, resolve: &Resolve, ty: &Type) -> bool { + resolve.all_bits_valid(ty) + } + + fn emit( + &mut self, + resolve: &Resolve, + inst: &Instruction<'_>, + operands: &mut Vec, + results: &mut Vec, + ) { + let mut top_as = |cvt: &str| { + results.push(format!("({cvt})({})", operands.pop().unwrap())); + }; + + // work around the fact that some functions only push + fn print_to_result<'a, 'b, 'c, T: FnOnce(&mut InterfaceGenerator<'a>)>( + slf: &'a mut FunctionBindgen<'b, 'c>, + resolve: &'a Resolve, + f: T, + ) -> String { + let mut sizes = SizeAlign::default(); + sizes.fill(resolve); + let mut gen = InterfaceGenerator { + identifier: slf.gen.identifier.clone(), + wasm_import_module: slf.gen.wasm_import_module.clone(), + src: Source::default(), + in_import: slf.gen.in_import.clone(), + gen: slf.gen.gen, + sizes, + resolve, + return_pointer_area_size: 0, + return_pointer_area_align: 0, + }; + f(&mut gen); + //gen.print_optional_ty(result.ok.as_ref(), TypeMode::Owned); + let mut ok_type = String::default(); + std::mem::swap(gen.src.as_mut_string(), &mut ok_type); + ok_type + } + + match inst { + Instruction::GetArg { nth } => results.push(self.params[*nth].clone()), + Instruction::I32Const { val } => results.push(format!("(int32_t){}", val)), + 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()), + } + } + } + + Instruction::I64FromU64 | Instruction::I64FromS64 => { + let s = operands.pop().unwrap(); + results.push(format!("(int64_t)({})", s)); + } + Instruction::I32FromChar + | Instruction::I32FromU8 + | Instruction::I32FromS8 + | Instruction::I32FromU16 + | Instruction::I32FromS16 + | Instruction::I32FromU32 + | Instruction::I32FromS32 => { + let s = operands.pop().unwrap(); + results.push(format!("(int32_t)({})", s)); + } + + Instruction::F32FromFloat32 => { + let s = operands.pop().unwrap(); + results.push(format!("(float)({})", s)); + } + Instruction::F64FromFloat64 => { + let s = operands.pop().unwrap(); + results.push(format!("(double)({})", s)); + } + Instruction::Float32FromF32 + | Instruction::Float64FromF64 + | Instruction::S32FromI32 + | Instruction::S64FromI64 => { + results.push(operands.pop().unwrap()); + } + Instruction::S8FromI32 => top_as("int8_t"), + Instruction::U8FromI32 => top_as("uint8_t"), + Instruction::S16FromI32 => top_as("int16_t"), + Instruction::U16FromI32 => top_as("uint16_t"), + Instruction::U32FromI32 => top_as("uint32_t"), + Instruction::U64FromI64 => top_as("uint64_t"), + Instruction::CharFromI32 => { + todo!(); + // results.push(format!( + // "{{ + // #[cfg(not(debug_assertions))] + // {{ ::core::char::from_u32_unchecked({} as u32) }} + // #[cfg(debug_assertions)] + // {{ ::core::char::from_u32({} as u32).unwrap() }} + // }}", + // operands[0], operands[0] + // )); + } + + Instruction::Bitcasts { casts } => { + wit_bindgen_rust_lib::bitcast(casts, operands, results) + } + + Instruction::I32FromBool => { + results.push(format!("(int32_t)({})", operands[0])); + } + Instruction::BoolFromI32 => { + results.push(format!("{}!=0", operands[0])); + } + + Instruction::FlagsLower { flags, .. } => { + let tmp = self.tmp(); + self.push_str(&format!("auto flags{} = {};\n", tmp, operands[0])); + for i in 0..flags.repr().count() { + results.push(format!("(flags{}.bits() >> {}) as i32", tmp, i * 32)); + } + } + Instruction::FlagsLift { flags, ty, .. } => { + let repr = RustFlagsRepr::new(flags); + let name = self.gen.type_path(*ty, true); + let mut result = format!("{name}::empty()"); + for (i, op) in operands.iter().enumerate() { + result.push_str(&format!( + " | {name}::from_bits_retain((({op} as {repr}) << {}) as _)", + i * 32 + )); + } + results.push(result); + } + + Instruction::HandleLower { + handle: Handle::Own(_), + .. + } => { + let op = &operands[0]; + results.push(format!("({op}).into_handle()")) + } + + Instruction::HandleLower { + handle: Handle::Borrow(_), + .. + } => { + let op = &operands[0]; + if op == "self" { + results.push("this->handle".into()); + } else { + results.push(format!("({op}).handle")); + } + } + + Instruction::HandleLift { handle, .. } => { + let op = &operands[0]; + let (prefix, resource, _owned) = match handle { + Handle::Borrow(resource) => ("&", resource, false), + Handle::Own(resource) => ("", resource, true), + }; + let resource = dealias(resolve, *resource); + + results.push( + if let Direction::Export = self.gen.gen.resources[&resource].direction { + match handle { + Handle::Borrow(_) => { + let name = resolve.types[resource] + .name + .as_deref() + .unwrap() + .to_upper_camel_case(); + format!( + "::core::mem::transmute::\ + ({op}.try_into().unwrap())" + ) + } + Handle::Own(_) => { + let name = self.gen.type_path(resource, true); + format!("{name}::from_handle({op})") + } + } + } else { + op.clone() + // let name = self.gen.type_path(resource, true); + // let world = self.gen.gen.world.map(|w| &resolve.worlds[w].name).unwrap(); + // format!("{prefix}{name}{{std::move({world}::{RESOURCE_BASE_CLASS_NAME}({op}))}}") + }, + ); + } + + Instruction::RecordLower { ty, record, .. } => { + self.record_lower(*ty, record, &operands[0], results); + } + Instruction::RecordLift { ty, record, .. } => { + let mut result = self.typename_lift(*ty); + result.push_str("{"); + for (_field, val) in record.fields.iter().zip(operands) { + // result.push_str(&to_rust_ident(&field.name)); + // result.push_str(":"); + result.push_str(&val); + result.push_str(", "); + } + result.push_str("}"); + results.push(result); + } + + Instruction::TupleLower { tuple, .. } => { + self.tuple_lower(tuple, &operands[0], results); + } + Instruction::TupleLift { .. } => { + self.tuple_lift(operands, results); + } + + Instruction::VariantPayloadName => results.push("e".to_string()), + + Instruction::VariantLower { + variant: _, + results: _, + ty, + .. + } => { + let name = self.typename_lower(*ty); + let op0 = &operands[0]; + self.push_str(&format!("({name}){op0}")); + } + + 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::>(); + 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)]"); + // } + 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); + } + + Instruction::OptionLower { + results: _result_types, + .. + } => { + todo!(); + // let some = self.blocks.pop().unwrap(); + // let none = self.blocks.pop().unwrap(); + // self.let_results(result_types.len(), results); + // let operand = &operands[0]; + // self.push_str(&format!( + // "match {operand} {{ + // Some(e) => {some}, + // None => {{\n{none}\n}}, + // }};" + // )); + } + + Instruction::OptionLift { .. } => { + let some = self.blocks.pop().unwrap(); + let none = self.blocks.pop().unwrap(); + assert_eq!(none, "()"); + let operand = &operands[0]; + results.push(format!( + "{operand}==1 ? std::optional<>(std::move({some})) : std::optional()" + )); + } + + Instruction::ResultLower { + results: _result_types, + // result, + .. + } => { + todo!(); + // let err = self.blocks.pop().unwrap(); + // let ok = self.blocks.pop().unwrap(); + // self.let_results(result_types.len(), results); + // let operand = &operands[0]; + // let ok_binding = if result.ok.is_some() { "e" } else { "_" }; + // let err_binding = if result.err.is_some() { "e" } else { "_" }; + // self.push_str(&format!( + // "match {operand} {{ + // Ok({ok_binding}) => {{ {ok} }}, + // Err({err_binding}) => {{ {err} }}, + // }};" + // )); + } + + Instruction::ResultLift { result, .. } => { + let mut err = self.blocks.pop().unwrap(); + let mut ok = self.blocks.pop().unwrap(); + if result.ok.is_none() { + ok.clear(); + } else { + ok = format!("std::move({ok})"); + } + if result.err.is_none() { + err.clear(); + } else { + err = format!("std::move({err})"); + } + let ok_type = print_to_result(self, resolve, |gen| { + gen.print_optional_ty(result.ok.as_ref(), TypeMode::Owned) + }); + let err_type = print_to_result(self, resolve, |gen| { + gen.print_optional_ty(result.err.as_ref(), TypeMode::Owned) + }); + let type_name = format!("std::expected<{ok_type}, {err_type}>",); + let err_type = "std::unexpected"; + let operand = &operands[0]; + results.push(format!( + "{operand}==0 \n? {type_name}({ok}) \n: {type_name}({err_type}({err}))" + )); + } + + Instruction::EnumLower { enum_: _, ty, .. } => { + let name = self.typename_lower(*ty); + let op0 = &operands[0]; + let result = format!("({name}){op0}"); + results.push(result); + } + + Instruction::EnumLift { + enum_: _, + ty: _, + name, + } => { + results.push(format!("({name}){}", &operands[0])); + } + + Instruction::ListCanonLower { realloc, .. } => { + let tmp = self.tmp(); + let val = format!("vec{}", tmp); + let ptr = format!("ptr{}", tmp); + let len = format!("len{}", tmp); + // if realloc.is_none() { + self.push_str(&format!("auto& {} = {};\n", val, operands[0])); + // } else { + // let op0 = operands.pop().unwrap(); + // self.push_str(&format!("auto {} = ({}).into_boxed_slice();\n", val, op0)); + // } + self.push_str(&format!("auto {} = (int32_t)({}.data());\n", ptr, val)); + self.push_str(&format!("auto {} = (int32_t)({}.size());\n", len, val)); + if realloc.is_some() { + todo!(); + // self.push_str(&format!("::core::mem::forget({});\n", val)); + } + results.push(ptr); + results.push(len); + } + + Instruction::ListCanonLift { .. } => { + let tmp = self.tmp(); + let len = format!("len{}", tmp); + self.push_str(&format!("auto {} = {};\n", len, operands[1])); + let result = format!("std::vector((?*)({}), {len})", operands[0]); + results.push(result); + } + + Instruction::StringLower { realloc } => { + let tmp = self.tmp(); + let val = format!("vec{}", tmp); + let ptr = format!("ptr{}", tmp); + let len = format!("len{}", tmp); + if realloc.is_none() { + self.push_str(&format!("auto {} = {};\n", val, operands[0])); + } else { + todo!(); + // let op0 = format!("{}.into_bytes()", operands[0]); + // self.push_str(&format!("let {} = ({}).into_boxed_slice();\n", val, op0)); + } + self.push_str(&format!("auto {} = (int32_t)({}.data());\n", ptr, val)); + self.push_str(&format!("auto {} = (int32_t)({}.size());\n", len, val)); + if realloc.is_some() { + todo!(); + // self.push_str(&format!("::core::mem::forget({});\n", val)); + } + results.push(ptr); + results.push(len); + } + + Instruction::StringLift => { + let tmp = self.tmp(); + let len = format!("len{}", tmp); + self.push_str(&format!("auto {} = {};\n", len, operands[1])); + let result = format!("std::string((char const*)({}), {len})", operands[0]); + results.push(result); + } + + Instruction::ListLower { element, realloc } => { + let body = self.blocks.pop().unwrap(); + let tmp = self.tmp(); + let vec = format!("vec{tmp}"); + let result = format!("result{tmp}"); + let layout = format!("layout{tmp}"); + let len = format!("len{tmp}"); + self.push_str(&format!( + "let {vec} = {operand0};\n", + operand0 = operands[0] + )); + self.push_str(&format!("let {len} = {vec}.len() as i32;\n")); + let size = self.gen.sizes.size(element); + let align = self.gen.sizes.align(element); + self.push_str(&format!( + "let {layout} = alloc::Layout::from_size_align_unchecked({vec}.len() * {size}, {align});\n", + )); + self.push_str(&format!( + "let {result} = if {layout}.size() != 0\n{{\nlet ptr = alloc::alloc({layout});\n", + )); + self.push_str(&format!( + "if ptr.is_null()\n{{\nalloc::handle_alloc_error({layout});\n}}\nptr\n}}", + )); + self.push_str(&format!("else {{\n::core::ptr::null_mut()\n}};\n",)); + self.push_str(&format!("for (i, e) in {vec}.into_iter().enumerate() {{\n",)); + self.push_str(&format!( + "let base = {result} as i32 + (i as i32) * {size};\n", + )); + self.push_str(&body); + self.push_str("}\n"); + results.push(format!("{result} as i32")); + results.push(len); + + if realloc.is_none() { + // If an allocator isn't requested then we must clean up the + // allocation ourselves since our callee isn't taking + // ownership. + self.cleanup.push((result, layout)); + } + } + + 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 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] + )); + let elemtype = + print_to_result(self, resolve, |gen| gen.print_ty(element, TypeMode::Owned)); + self.push_str(&format!("auto {result} = std::vector<{elemtype}>();\n")); + self.push_str(&format!("{result}.reserve({len});\n")); + self.push_str(&format!("for (unsigned i=0;i<{len};++i) {{\n")); + self.push_str(&format!("auto base = {base} + i * {size};\n")); + self.push_str(&format!("{result}.push_back({body});\n")); + self.push_str("}\n"); + results.push(result); + self.push_str(&format!("free((void*){base});\n")); + } + + Instruction::IterElem { .. } => results.push("e".to_string()), + + Instruction::IterBasePointer => results.push("base".to_string()), + + Instruction::CallWasm { name, sig, .. } => { + let func = self.declare_import( + self.gen.wasm_import_module.unwrap(), + name, + &sig.params, + &sig.results, + ); + + // ... then call the function with all our operands + if sig.results.len() > 0 { + self.push_str("auto ret = "); + results.push("ret".to_string()); + } + self.push_str(&func); + self.push_str("("); + self.push_str(&operands.join(", ")); + self.push_str(");\n"); + } + + Instruction::CallInterface { func, .. } => { + self.let_results(func.results.len(), results); + match &func.kind { + FunctionKind::Freestanding => { + self.push_str(&format!( + "<{0}Impl as {0}>::{1}", + self.trait_name.unwrap(), + to_rust_ident(&func.name) + )); + } + FunctionKind::Method(ty) | FunctionKind::Static(ty) => { + self.push_str(&format!( + "::{1}", + resolve.types[*ty] + .name + .as_deref() + .unwrap() + .to_upper_camel_case(), + to_rust_ident(func.item_name()) + )); + } + FunctionKind::Constructor(ty) => { + self.push_str(&format!( + "Own{0}::new(::new", + resolve.types[*ty] + .name + .as_deref() + .unwrap() + .to_upper_camel_case() + )); + } + } + self.push_str("("); + self.push_str(&operands.join(", ")); + self.push_str(")"); + if let FunctionKind::Constructor(_) = &func.kind { + self.push_str(")"); + } + self.push_str(";\n"); + } + + Instruction::Return { amt, func, .. } => { + self.emit_cleanup(); + match amt { + 0 => {} + 1 => { + match &func.kind { + FunctionKind::Constructor(_) => { + // strange but works + self.push_str("this->handle = "); + } + _ => self.push_str("return "), + } + self.push_str(&operands[0]); + self.push_str(";\n"); + } + _ => todo!(), + } + } + + Instruction::I32Load { offset } => { + results.push(format!("*((int32_t const*)({} + {}))", operands[0], offset)); + } + Instruction::I32Load8U { offset } => { + results.push(format!( + "(int32_t)(*((uint8_t const*)({} + {})))", + operands[0], offset + )); + } + Instruction::I32Load8S { offset } => { + results.push(format!( + "(int32_t)(*((int8_t const*)({} + {})))", + operands[0], offset + )); + } + Instruction::I32Load16U { offset } => { + results.push(format!( + "(int32_t)(*((uint16_t const*)({} + {})))", + operands[0], offset + )); + } + Instruction::I32Load16S { offset } => { + results.push(format!( + "(int32_t)(*((int16_t const*)({} + {})))", + operands[0], offset + )); + } + Instruction::I64Load { offset } => { + results.push(format!("*((int64_t const*)({} + {}))", operands[0], offset)); + } + Instruction::F32Load { offset } => { + results.push(format!("*((float const*)({} + {}))", operands[0], offset)); + } + Instruction::F64Load { offset } => { + results.push(format!("*((double const*)({} + {}))", operands[0], offset)); + } + Instruction::I32Store { offset } => { + self.push_str(&format!( + "*((int32_t*)({} + {})) = {};\n", + operands[1], offset, operands[0] + )); + } + Instruction::I32Store8 { offset } => { + self.push_str(&format!( + "*((int8_t*)({} + {})) = int8_t({});\n", + operands[1], offset, operands[0] + )); + } + Instruction::I32Store16 { offset } => { + self.push_str(&format!( + "*((uint16_t*)({} + {})) = uint16_t({});\n", + operands[1], offset, operands[0] + )); + } + Instruction::I64Store { offset } => { + self.push_str(&format!( + "*((int64_t*)({} + {})) = {};\n", + operands[1], offset, operands[0] + )); + } + Instruction::F32Store { offset } => { + self.push_str(&format!( + "*((float*)({} + {})) = {};\n", + operands[1], offset, operands[0] + )); + } + Instruction::F64Store { offset } => { + self.push_str(&format!( + "*((double*)({} + {})) = {};\n", + operands[1], offset, operands[0] + )); + } + + Instruction::Malloc { .. } => unimplemented!(), + + Instruction::GuestDeallocate { size, align } => { + self.push_str(&format!( + "wit_bindgen::rt::dealloc({}, {}, {});\n", + operands[0], size, align + )); + } + + Instruction::GuestDeallocateString => { + self.push_str(&format!( + "wit_bindgen::rt::dealloc({}, ({}) as usize, 1);\n", + operands[0], operands[1], + )); + } + + Instruction::GuestDeallocateVariant { blocks } => { + let max = blocks - 1; + let blocks = self + .blocks + .drain(self.blocks.len() - blocks..) + .collect::>(); + let op0 = &operands[0]; + self.src.push_str(&format!("match {op0} {{\n")); + for (i, block) in blocks.into_iter().enumerate() { + let pat = if i == max { + String::from("_") + } else { + i.to_string() + }; + self.src.push_str(&format!("{pat} => {block},\n")); + } + self.src.push_str("}\n"); + } + + Instruction::GuestDeallocateList { 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 len = format!("len{tmp}"); + let base = format!("base{tmp}"); + self.push_str(&format!( + "let {base} = {operand0};\n", + operand0 = operands[0] + )); + self.push_str(&format!( + "let {len} = {operand1};\n", + operand1 = operands[1] + )); + + if body != "()" { + self.push_str("for i in 0.."); + self.push_str(&len); + self.push_str(" {\n"); + self.push_str("let base = "); + self.push_str(&base); + self.push_str(" + i *"); + self.push_str(&size.to_string()); + self.push_str(";\n"); + self.push_str(&body); + self.push_str("\n}\n"); + } + self.push_str(&format!( + "wit_bindgen::rt::dealloc({base}, ({len} as usize) * {size}, {align});\n", + )); + } + } + } +} + +fn group_by_resource<'a>( + funcs: impl Iterator, +) -> BTreeMap, Vec<&'a Function>> { + let mut by_resource = BTreeMap::<_, Vec<_>>::new(); + for func in funcs { + match &func.kind { + FunctionKind::Freestanding => by_resource.entry(None).or_default().push(func), + FunctionKind::Method(ty) | FunctionKind::Static(ty) | FunctionKind::Constructor(ty) => { + by_resource.entry(Some(*ty)).or_default().push(func); + } + } + } + by_resource +} + +fn to_rust_ident(name: &str) -> String { + match name { + // Escape C++ keywords. + // Source: https://doc.rust-lang.org/reference/keywords.html + "this" => "this_".into(), + _ => wit_bindgen_c::to_c_ident(name), + } +} diff --git a/crates/cpp/src/wamr.rs b/crates/cpp/src/wamr.rs new file mode 100644 index 000000000..f2257ed63 --- /dev/null +++ b/crates/cpp/src/wamr.rs @@ -0,0 +1,136 @@ +use wit_bindgen_core::wit_parser::{Function, Resolve, Results, 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::Float32 => { + params_str.push('f'); + } + Type::Float64 => { + 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) => { + todo!(); + } + 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'); + } + }, + } +} + +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::Float32 => { + sig.wamr_result = "f".into(); + } + Type::Float64 => { + 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(_) => todo!(), + TypeDefKind::Tuple(_) => todo!(), + TypeDefKind::Variant(_) => todo!(), + 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 => todo!(), + TypeDefKind::Handle(_h) => { + sig.wamr_result = "i".into(); + } + }, + } +} + +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.results { + Results::Named(p) => { + if !p.is_empty() { + dbg!(p); + todo!() + } + } + Results::Anon(e) => wamr_add_result(&mut result, resolve, e), + } + result +} diff --git a/crates/cpp/tests/codegen.rs b/crates/cpp/tests/codegen.rs new file mode 100644 index 000000000..462870a0b --- /dev/null +++ b/crates/cpp/tests/codegen.rs @@ -0,0 +1,39 @@ +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() { + test_helpers::run_world_codegen_test( + "cpp", + $test.as_ref(), + |resolve, world, files| { + wit_bindgen_cpp::Opts::default() + .build() + .generate(resolve, world, files) + }, + verify, + ); + } + }; +} + +test_helpers::codegen_tests!("*.wit"); + +fn verify(dir: &Path, name: &str) { + let path = PathBuf::from(env::var_os("WASI_SDK_PATH").unwrap()); + let mut cmd = Command::new(path.join("bin/clang++")); + cmd.arg(dir.join(format!("{}.cpp", name.to_snake_case()))); + cmd.arg("-I").arg(dir); + cmd.arg("-Wall") + .arg("-Wextra") + .arg("-Werror") + .arg("-Wno-unused-parameter"); + cmd.arg("-c"); + cmd.arg("-o").arg(dir.join("obj_host.o")); + + test_helpers::run_command(&mut cmd); +} diff --git a/crates/test-helpers/codegen-macro/Cargo.toml b/crates/test-helpers/codegen-macro/Cargo.toml index be6c834c0..fd653a1f3 100644 --- a/crates/test-helpers/codegen-macro/Cargo.toml +++ b/crates/test-helpers/codegen-macro/Cargo.toml @@ -12,4 +12,4 @@ test = false [dependencies] heck = { workspace = true } -quote = "1.0.32" +quote = "1.0.33" diff --git a/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs index 5eb78a1b5..befe35c39 100644 --- a/src/bin/wit-bindgen.rs +++ b/src/bin/wit-bindgen.rs @@ -38,6 +38,14 @@ enum Opt { #[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 TeaVM-based Java guest modules. #[cfg(feature = "teavm-java")] @@ -96,6 +104,12 @@ fn main() -> Result<()> { Opt::Markdown { opts, args } => (opts.build(), args), #[cfg(feature = "c")] Opt::C { opts, args } => (opts.build(), args), + #[cfg(feature = "c-host")] + Opt::CHost { opts, args } => (opts.build(), args), + #[cfg(feature = "cpp")] + Opt::Cpp { opts, args } => (opts.build(), args), + #[cfg(feature = "cpp-host")] + Opt::CppHost { opts, args } => (opts.build(), args), #[cfg(feature = "rust")] Opt::Rust { opts, args } => (opts.build(), args), #[cfg(feature = "teavm-java")] 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, + } +} + +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 + } + + initialize: func() -> result<_, error-code> + deinitialize: func() -> result<_, error-code> + + create-instance-specifier: func(spec: string) -> result +// instance-specifier: func() -> result +} + +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) -> list +} +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>; + + /// Get the POSIX-style arguments to the program. + get-arguments: func() -> list; + + /// Return a path that programs should use as their initial current working + /// directory, interpreting `.` as shorthand for this. + initial-cwd: func() -> option; +} 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; +} + +/// 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; +} + +/// 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; +} 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>; +} 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, + /// Last data modification timestamp. + /// + /// If the `option` is none, the platform doesn't maintain a + /// modification timestamp for this file. + data-modification-timestamp: option, + /// 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, + } + + /// 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; + + /// 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; + + /// 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; + + /// 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; + + /// 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; + + /// 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`. + /// + /// 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, 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`. + /// + /// Note: This is similar to `pwrite` in POSIX. + write: func( + /// Data to write + buffer: list, + /// The offset within the file at which to write. + offset: filesize, + ) -> result; + + /// 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; + + /// 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; + + /// 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; + + /// 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, + /// 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; + + /// 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; + + /// 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, + /// 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) -> 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; + + /// 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; + } + + /// A stream of directory entries. + resource directory-entry-stream { + /// Read a single directory entry from a `directory-entry-stream`. + read-directory-entry: func() -> result, 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) -> option; +} 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` and returns + /// `option`. + /// + /// 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` 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>) -> list; +} 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, 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, 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; + + /// 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; + + /// 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; + + /// 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 + ) -> 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 + ) -> 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, + /// The number of bytes to splice + len: u64, + ) -> result; + + /// 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, + /// The number of bytes to splice + len: u64, + ) -> result; + } +} 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; +} 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; + + /// 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; + + /// 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: + /// - + /// - + /// - + /// - + resolve-addresses: func(network: borrow, name: string) -> result; + + 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, 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; + type ipv6-address = tuple; + + 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 + /// - + /// - + /// - + /// - + create-tcp-socket: func(address-family: ip-address-family) -> result; +} 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 + /// - + /// - + /// - + /// - + start-bind: func(network: borrow, 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 + /// - + /// - + /// - + /// - + start-connect: func(network: borrow, remote-address: ip-socket-address) -> result<_, error-code>; + finish-connect: func() -> result, 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 + /// - + /// - + /// - + /// - + 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 + /// - + /// - + /// - + /// - + accept: func() -> result, 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 + /// - + /// - + /// - + /// - + local-address: func() -> result; + + /// Get the remote address. + /// + /// # Typical errors + /// - `invalid-state`: The socket is not connected to a remote address. (ENOTCONN) + /// + /// # References + /// - + /// - + /// - + /// - + remote-address: func() -> result; + + /// 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; + 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; + 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; + 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; + 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; + 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; + 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; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + 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 + /// - + /// - + /// - + /// - + 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: + /// - + /// - + /// - + /// - + create-udp-socket: func(address-family: ip-address-family) -> result; +} 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, + + /// 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, + + /// 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, + } + + + + /// 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 + /// - + /// - + /// - + /// - + start-bind: func(network: borrow, 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 + /// - + /// - + /// - + /// - + %stream: func(remote-address: option) -> result, 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 + /// - + /// - + /// - + /// - + local-address: func() -> result; + + /// 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 + /// - + /// - + /// - + /// - + remote-address: func() -> result; + + /// 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; + 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; + 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; + set-receive-buffer-size: func(value: u64) -> result<_, error-code>; + send-buffer-size: func() -> result; + 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 + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + receive: func(max-results: u64) -> result, 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; + + /// 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 + /// - + /// - + /// - + /// - + /// - + /// - + /// - + /// - + send: func(datagrams: list) -> result; + + /// 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 is the same length as the argument + /// list, and indicates the readiness of each corresponding + /// element in that / list, with true indicating ready. + poll-oneoff: func(in: list) -> list +} 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) + } + + resource abort-handle { + abort: static func(self: own) + } + + resource resume-handle { + resume: static func(self: own) + } +} + +interface radar-types { + record position { + x: s32, + y: s32, + z: s32, + } + record radar-objects { + active: bool, + object-vector: list, + } + 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, value: result) + } + resource promise-result-calibrate-output-error-code { + ready: static func(self: own, value: result) + } + resource promise-u32 { + ready: static func(self: own, value: u32) + } + resource promise-u16 { + ready: static func(self: own, value: u16) + } + + resource stream-instance-handle { + // returns the value if the queue is filled up (backpressure) + ready: func(value: option>, resume: resume-handle) -> option> + } + resource stream-sample-ptr { + ready: func(value: option>, resume: resume-handle) -> option> + } +} + +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) + + adjust: func (promise: promise-result-adjust-output-error-code, target-position: position) -> option + calibrate: func (promise: promise-result-calibrate-output-error-code, configuration: string, radar-variant: fusion-variant) -> option + echo: func (text: string) + + subscribe-brake-event: func (max-sample-count: u32, receiver: stream-sample-ptr) -> result + subscribe-parking-brake-event: func (max-sample-count: u32, receiver: stream-sample-ptr) -> result + + // field + get-update-rate: func(promise: promise-u32) -> option + set-update-rate: func(promise: promise-u32, value: u32) -> option + subscribe-update-rate: func(max-sample-count: u32, receiver: stream-sample-ptr) -> result + subscribe-front-object-distance: func(max-sample-count: u32, receiver: stream-sample-ptr) -> result + get-rear-object-distance: func(promise: promise-u16) -> option + set-object-detection-limit: func(promise: promise-u16, value: u16) -> option + } + + start-find-service: func(spec: borrow, receiver: stream-instance-handle) -> option +} + +// 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, spec: borrow, mode: method-call-processing-mode) -> abort-handle +// //stop-offer-service: func (service: borrow) +// } + +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/other/easy_test.cpp b/tests/other/easy_test.cpp new file mode 100644 index 000000000..399981fdb --- /dev/null +++ b/tests/other/easy_test.cpp @@ -0,0 +1,7 @@ +#include "my_world_cpp.h" + +int main() { + test::example::my_interface::MyObject o(42); + o.Set(o.Get()+1); + return 0; +} diff --git a/tests/other/expected b/tests/other/expected new file mode 100644 index 000000000..66b06b147 --- /dev/null +++ b/tests/other/expected @@ -0,0 +1,6 @@ +#include + +namespace std { + using ::tl::expected; + using ::tl::unexpected; +} diff --git a/tests/other/expected.hpp b/tests/other/expected.hpp new file mode 100755 index 000000000..afee404d4 --- /dev/null +++ b/tests/other/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 +// . +/// + +#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 +#include +#include +#include + +#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 +#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 +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign + +// 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 + +// 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 +struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible {}; +#ifdef _GLIBCXX_VECTOR +template +struct is_trivially_copy_constructible> : std::false_type {}; +#endif +} // namespace detail +} // namespace tl +#endif + +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#else +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#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 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 unexpected { +public: + static_assert(!std::is_same::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 ::value>::type * = nullptr> + constexpr explicit unexpected(Args &&...args) + : m_val(std::forward(args)...) {} + template < + class U, class... Args, + typename std::enable_if &, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(std::initializer_list l, Args &&...args) + : m_val(l, std::forward(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 unexpected(E) -> unexpected; +#endif + +template +constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() == rhs.value(); +} +template +constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() != rhs.value(); +} +template +constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() < rhs.value(); +} +template +constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() <= rhs.value(); +} +template +constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() > rhs.value(); +} +template +constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() >= rhs.value(); +} + +template +unexpected::type> make_unexpected(E &&e) { + return unexpected::type>(std::forward(e)); +} + +struct unexpect_t { + unexpect_t() = default; +}; +static constexpr unexpect_t unexpect{}; + +namespace detail { +template +[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward(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 using remove_const_t = typename std::remove_const::type; +template +using remove_reference_t = typename std::remove_reference::type; +template using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; + +// std::conjunction from C++17 +template struct conjunction : std::true_type {}; +template struct conjunction : B {}; +template +struct conjunction + : std::conditional, 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 +struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; + +template struct is_const_or_const_ref : std::false_type {}; +template struct is_const_or_const_ref : std::true_type {}; +template struct is_const_or_const_ref : 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::value && + is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, int = 0> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); +} + +template >::value>> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} + +// std::invoke_result from C++17 +template struct invoke_result_impl; + +template +struct invoke_result_impl< + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = + decltype(detail::invoke(std::declval(), std::declval()...)); +}; + +template +using invoke_result = invoke_result_impl; + +template +using invoke_result_t = typename invoke_result::type; + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template struct is_swappable : std::true_type {}; + +template 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 tag swap(T &, T &); +template 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 std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); + +template std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); + +template +struct is_std_swap_noexcept + : std::integral_constant::value && + std::is_nothrow_move_assignable::value> {}; + +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; + +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std( + 0))::value || + is_swappable::value)> {}; + +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; +#endif +#endif + +// Trait for checking if a type is a tl::expected +template struct is_expected_impl : std::false_type {}; +template +struct is_expected_impl> : std::true_type {}; +template using is_expected = is_expected_impl>; + +template +using expected_enable_forward_value = detail::enable_if_t< + std::is_constructible::value && + !std::is_same, in_place_t>::value && + !std::is_same, detail::decay_t>::value && + !std::is_same, detail::decay_t>::value>; + +template +using expected_enable_from_other = detail::enable_if_t< + std::is_constructible::value && + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; + +template +using is_void_or = conditional_t::value, std::true_type, U>; + +template +using is_copy_constructible_or_void = + is_void_or>; + +template +using is_move_constructible_or_void = + is_void_or>; + +template +using is_copy_assignable_or_void = is_void_or>; + +template +using is_move_assignable_or_void = is_void_or>; + +} // 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 ::value, + bool = std::is_trivially_destructible::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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } else { + m_unexpect.~unexpected(); + } + } + union { + T m_val; + unexpected 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 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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// T is trivial, E is not. +template struct expected_storage_base { + 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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// E is trivial, T is not. +template 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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is trivially-destructible +template struct expected_storage_base { + #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 ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + struct dummy {}; + union { + unexpected m_unexpect; + dummy m_val; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is not trivially-destructible +template struct expected_storage_base { + 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 ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + unexpected 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 +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct(Args &&...args) noexcept { + new (std::addressof(this->m_val)) T(std::forward(args)...); + this->m_has_val = true; + } + + template void construct_with(Rhs &&rhs) noexcept { + new (std::addressof(this->m_val)) T(std::forward(rhs).get()); + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(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 ::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + 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 ::value && + std::is_nothrow_move_constructible::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(); + 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 ::value && + !std::is_nothrow_move_constructible::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(); + +#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 ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); +#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(); + 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(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + +#endif + + // The common part of move/copy assigning + template void assign_common(Rhs &&rhs) { + if (this->m_has_val) { + if (rhs.m_has_val) { + get() = std::forward(rhs).get(); + } else { + destroy_val(); + construct_error(std::forward(rhs).geterr()); + } + } else { + if (!rhs.m_has_val) { + geterr() = std::forward(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 &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&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 +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template 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 void construct_with(Rhs &&) noexcept { + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + + template void assign(Rhs &&rhs) noexcept { + if (!this->m_has_val) { + if (rhs.m_has_val) { + geterr().~unexpected(); + construct(); + } else { + geterr() = std::forward(rhs).geterr(); + } + } else { + if (!rhs.m_has_val) { + construct_error(std::forward(rhs).geterr()); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&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 :: + value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; +}; + +// This specialization is for when T or E are not trivially copy constructible +template +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; + + expected_copy_base() = default; + expected_copy_base(const expected_copy_base &rhs) + : expected_operations_base(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 >::value + &&std::is_trivially_move_constructible::value> +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; +}; +#else +template struct expected_move_base; +#endif +template +struct expected_move_base : expected_copy_base { + using expected_copy_base::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::value) + : expected_copy_base(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 >::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 { + using expected_move_base::expected_move_base; +}; + +template +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::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 , + std::is_trivially_move_constructible, + std::is_trivially_move_assignable>>:: + value &&std::is_trivially_destructible::value + &&std::is_trivially_move_constructible::value + &&std::is_trivially_move_assignable::value> +struct expected_move_assign_base : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; +}; +#else +template struct expected_move_assign_base; +#endif + +template +struct expected_move_assign_base + : expected_copy_assign_base { + using expected_copy_assign_base::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::value + &&std::is_nothrow_move_assignable::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 ::value && + std::is_copy_constructible::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::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 +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 = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + 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 +struct expected_delete_ctor_base { + 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 ::value && + std::is_copy_constructible::value && + is_copy_assignable_or_void::value && + std::is_copy_assignable::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value && + is_move_assignable_or_void::value && + std::is_move_assignable::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 +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 = delete; +}; + +template +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 &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +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 &) = 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 ::value || std::is_void::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 struct expected_default_ctor_base { + 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 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` 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 expected : private detail::expected_move_assign_base, + private detail::expected_delete_ctor_base, + private detail::expected_delete_assign_base, + private detail::expected_default_ctor_base { + static_assert(!std::is_reference::value, "T must not be a reference"); + static_assert(!std::is_same::type>::value, + "T must not be in_place_t"); + static_assert(!std::is_same::type>::value, + "T must not be unexpect_t"); + static_assert( + !std::is_same>::type>::value, + "T must not be unexpected"); + static_assert(!std::is_reference::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 *errptr() { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { + return std::addressof(this->m_unexpect); + } + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &val() { + return this->m_val; + } + TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } + + template ::value> * = nullptr> + constexpr const U &val() const { + return this->m_val; + } + constexpr const unexpected &err() const { return this->m_unexpect; } + + using impl_base = detail::expected_move_assign_base; + using ctor_base = detail::expected_default_ctor_base; + +public: + typedef T value_type; + typedef E error_type; + typedef unexpected unexpected_type; + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { + return and_then_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + return and_then_impl(std::move(*this), std::forward(f)); + } + template constexpr auto and_then(F &&f) const & { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template constexpr auto and_then(F &&f) const && { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif + +#else + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) & -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) && -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + return or_else_impl(*this, std::forward(f)); + } + + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + return or_else_impl(std::move(*this), std::forward(f)); + } + + template expected constexpr or_else(F &&f) const & { + return or_else_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template expected constexpr or_else(F &&f) const && { + return or_else_impl(std::move(*this), std::forward(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 ::value> * = + nullptr> + constexpr expected(in_place_t, Args &&...args) + : impl_base(in_place, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected(in_place_t, std::initializer_list il, Args &&...args) + : impl_base(in_place, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit constexpr expected(const unexpected &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected const &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr> + constexpr explicit expected(unexpect_t, Args &&...args) + : impl_base(unexpect, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected(unexpect_t, std::initializer_list il, + Args &&...args) + : impl_base(unexpect, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(const expected &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::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&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::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(expected &&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::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + err().~unexpected(); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } + + return *this; + } + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; +#endif + } + + return *this; + } + + template ::value && + std::is_assignable::value> * = nullptr> + expected &operator=(const unexpected &rhs) { + if (!has_value()) { + err() = rhs; + } else { + this->destroy_val(); + ::new (errptr()) unexpected(rhs); + this->m_has_val = false; + } + + return *this; + } + + template ::value && + std::is_move_assignable::value> * = nullptr> + expected &operator=(unexpected &&rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } else { + this->destroy_val(); + ::new (errptr()) unexpected(std::move(rhs)); + this->m_has_val = false; + } + + return *this; + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + } else { + err().~unexpected(); + this->m_has_val = true; + } + ::new (valptr()) T(std::forward(args)...); + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + ::new (valptr()) T(std::forward(args)...); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; +#endif + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + err().~unexpected(); + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(il, std::forward(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::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::type{}, + typename std::is_nothrow_move_constructible::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 + detail::enable_if_t::value && + detail::is_swappable::value && + (std::is_nothrow_move_constructible::value || + std::is_nothrow_move_constructible::value)> + swap(expected &rhs) noexcept( + std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value + &&std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { + if (has_value() && rhs.has_value()) { + swap_where_both_have_value(rhs, typename std::is_void::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::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 ::value> * = nullptr> + constexpr const U &operator*() const & { + TL_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { + TL_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + constexpr const U &&operator*() const && { + TL_ASSERT(has_value()); + return std::move(val()); + } + template ::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 ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access(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 constexpr T value_or(U &&v) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy-constructible and convertible to from U&&"); + return bool(*this) ? **this : static_cast(std::forward(v)); + } + template TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move-constructible and convertible to from U&&"); + return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); + } +}; + +namespace detail { +template using exp_t = typename detail::decay_t::value_type; +template using err_t = typename detail::decay_t::error_type; +template using ret_t = expected>; + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#else +template struct TC; +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template ())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto or_else_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#else +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto or_else_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#endif +} // namespace detail + +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); +} +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : true); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() == rhs.error() : false); +} + +template +constexpr bool operator==(const expected &x, const U &v) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator==(const U &v, const expected &x) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator!=(const expected &x, const U &v) { + return x.has_value() ? *x != v : true; +} +template +constexpr bool operator!=(const U &v, const expected &x) { + return x.has_value() ? *x != v : true; +} + +template +constexpr bool operator==(const expected &x, const unexpected &e) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator==(const unexpected &e, const expected &x) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator!=(const expected &x, const unexpected &e) { + return x.has_value() ? true : x.error() != e.value(); +} +template +constexpr bool operator!=(const unexpected &e, const expected &x) { + return x.has_value() ? true : x.error() != e.value(); +} + +template ::value || + std::is_move_constructible::value) && + detail::is_swappable::value && + std::is_move_constructible::value && + detail::is_swappable::value> * = nullptr> +void swap(expected &lhs, + expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} +} // namespace tl + +#endif diff --git a/tests/other/fusion_cpp.h b/tests/other/fusion_cpp.h new file mode 100644 index 000000000..a0eb471de --- /dev/null +++ b/tests/other/fusion_cpp.h @@ -0,0 +1,242 @@ +// Generated by `wit-bindgen` 0.1.0. DO NOT EDIT! +#ifndef __CPP_GUEST_BINDINGS_FUSION_H +#define __CPP_GUEST_BINDINGS_FUSION_H +#include +#include +#include +#include +#include +#include +#include +namespace fusion { class ResourceBase{ + protected: + int32_t handle; + bool owned; + public: + ResourceBase(int32_t h=0, bool o=false) : handle(h), owned(o) {} + int32_t get_handle() const { return handle; } + }; } +namespace autosar { namespace ara { namespace types { using ErrorCodeType = int32_t; }}} +namespace autosar { namespace ara { namespace types { class ErrorDomain : fusion::ResourceBase { + public: + std::string Name(); + std::string Message(::autosar::ara::types::ErrorCodeType n); +// int32_t wasm_handle() const {return handle;} + ~ErrorDomain(); + ErrorDomain(fusion::ResourceBase&&); +}; }}} +namespace autosar { namespace ara { namespace types { + struct ErrorCode { + ::autosar::ara::types::ErrorCodeType value; + types::ErrorDomain domain; + }; +}}} +namespace autosar { namespace ara { namespace e2exf { + enum class ConfigurationFormat : uint8_t { + kJson = 0, + kXml = 1, + }; +}}} +namespace autosar { namespace ara { namespace core { using ErrorCode = ::autosar::ara::types::ErrorCode; }}} +namespace autosar { namespace ara { namespace com { using ConfigurationFormat = ::autosar::ara::e2exf::ConfigurationFormat; }}} +namespace autosar { namespace ara { namespace core { class InstanceSpecifier : fusion::ResourceBase { + public: + InstanceSpecifier(std::string_view const& spec); + std::string ToString(); + InstanceSpecifier Clone(); + ~InstanceSpecifier(); + InstanceSpecifier(fusion::ResourceBase&&); +}; }}} +namespace autosar { namespace ara { namespace com { using InstanceSpecifier = ::autosar::ara::core::InstanceSpecifier; }}} +namespace autosar { namespace ara { namespace exec { + enum class ExecutionState : uint8_t { + kRunning = 0, + }; +}}} +// 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). +namespace autosar { namespace ara { namespace poll { using Pollable = uint32_t; }}} +namespace autosar { namespace ara { namespace radar { using ErrorCode = ::autosar::ara::types::ErrorCode; }}} +namespace autosar { namespace ara { namespace radar { using InstanceSpecifier = ::autosar::ara::core::InstanceSpecifier; }}} +namespace autosar { namespace ara { namespace radar { using Pollable = ::autosar::ara::poll::Pollable; }}} +namespace autosar { namespace ara { namespace radar { + struct Position { + int32_t x; + int32_t y; + int32_t z; + }; +}}} +namespace autosar { namespace ara { namespace radar { + struct RadarObjects { + bool active; + std::vector object_vector; + }; +}}} +namespace autosar { namespace ara { namespace radar { + struct AdjustOutput { + bool success; + ::autosar::ara::radar::Position effective_position; + }; +}}} +namespace autosar { namespace ara { namespace radar { + enum class FusionVariant : uint8_t { + kChina = 0, + kUsa = 1, + kEurope = 2, + kRussia = 3, + }; +}}} +namespace autosar { namespace ara { namespace radar { + struct CalibrateOutput { + bool call_result; + }; +}}} +namespace autosar { namespace ara { namespace radar { class FutureResultAdjustOutputErrorCode : fusion::ResourceBase { + public: + ::autosar::ara::radar::Pollable Subscribe(); + std::expected Value(); + ~FutureResultAdjustOutputErrorCode(); + FutureResultAdjustOutputErrorCode(fusion::ResourceBase&&); +}; }}} +namespace autosar { namespace ara { namespace radar { class FutureResultCalibrateOutputErrorCode : fusion::ResourceBase { + public: + ::autosar::ara::radar::Pollable Subscribe(); + std::expected Value(); + ~FutureResultCalibrateOutputErrorCode(); + FutureResultCalibrateOutputErrorCode(fusion::ResourceBase&&); +}; }}} +namespace autosar { namespace ara { namespace radar { class FutureU32 : fusion::ResourceBase { + public: + ::autosar::ara::radar::Pollable Subscribe(); + uint32_t Value(); + ~FutureU32(); + FutureU32(fusion::ResourceBase&&); +}; }}} +namespace autosar { namespace ara { namespace radar { class StreamU32 : fusion::ResourceBase { + public: + ::autosar::ara::radar::Pollable Subscribe(); + std::optional Value(); + ~StreamU32(); + StreamU32(fusion::ResourceBase&&); +}; }}} +namespace autosar { namespace ara { namespace radar { class FutureU16 : fusion::ResourceBase { + public: + ::autosar::ara::radar::Pollable Subscribe(); + uint16_t Value(); + ~FutureU16(); + FutureU16(fusion::ResourceBase&&); +}; }}} +namespace autosar { namespace ara { namespace radar { class StreamU16 : fusion::ResourceBase { + public: + ::autosar::ara::radar::Pollable Subscribe(); + std::optional Value(); + ~StreamU16(); + StreamU16(fusion::ResourceBase&&); +}; }}} +namespace autosar { namespace ara { namespace radar { class ProxyHandle : fusion::ResourceBase { + public: +}; }}} +namespace autosar { namespace ara { namespace radar { class StreamProxyHandle : fusion::ResourceBase { + public: + ::autosar::ara::radar::Pollable Subscribe(); + std::optional Value(); + ~StreamProxyHandle(); + StreamProxyHandle(fusion::ResourceBase&&); +}; }}} +namespace autosar { namespace ara { namespace radar { class StreamRadarObjects : fusion::ResourceBase { + public: + ::autosar::ara::radar::Pollable Subscribe(); + std::optional Value(); + ~StreamRadarObjects(); + StreamRadarObjects(fusion::ResourceBase&&); +}; }}} +namespace autosar { namespace ara { namespace radar { class Proxy : fusion::ResourceBase { + public: + Proxy(radar::ProxyHandle handle); + FutureResultAdjustOutputErrorCode Adjust(::autosar::ara::radar::Position const& target_position); + FutureResultCalibrateOutputErrorCode Calibrate(std::string_view const& configuration, ::autosar::ara::radar::FusionVariant radar_variant); + void Echo(std::string_view const& text); + std::expected SubscribeBrakeEvent(uint32_t max_sample_count); + std::expected SubscribeParkingBrakeEvent(uint32_t max_sample_count); + FutureU32 GetUpdateRate(); + FutureU32 SetUpdateRate(uint32_t value); + std::expected SubscribeUpdateRate(uint32_t max_sample_count); + std::expected SubscribeFrontObjectDistance(uint32_t max_sample_count); + FutureU16 GetRearObjectDistance(); + FutureU16 SetObjectDetectionLimit(uint16_t value); + ~Proxy(); + Proxy(fusion::ResourceBase&&); +}; }}} +namespace autosar { namespace ara { namespace log { class Logger : fusion::ResourceBase { + public: + void Report(uint32_t level, std::string_view const& message); + Logger(std::string_view const& context, std::string_view const& description, uint32_t level); + ~Logger(); + Logger(fusion::ResourceBase&&); +}; }}} +namespace autosar { namespace ara { namespace com { class InstanceIdentifier : fusion::ResourceBase { + public: + InstanceIdentifier(std::string_view const& id); + std::string ToString(); + ~InstanceIdentifier(); + InstanceIdentifier(fusion::ResourceBase&&); +}; }}} +namespace autosar { namespace ara { namespace exec { class ExecutionClient : fusion::ResourceBase { + public: + ExecutionClient(void); + void ReportExecutionState(::autosar::ara::exec::ExecutionState state); + ~ExecutionClient(); + ExecutionClient(fusion::ResourceBase&&); +}; }}} +namespace autosar { namespace ara { namespace types { +}}} +namespace autosar { namespace ara { namespace core { + std::expected Initialize(void); + std::expected Deinitialize(void); + std::expected CreateInstanceSpecifier(std::string_view const& spec); +}}} +namespace autosar { namespace ara { namespace poll { + // Dispose of the specified `pollable`, after which it may no longer + // be used. + void DropPollable(::autosar::ara::poll::Pollable ths); + // 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 is the same length as the argument + // list, and indicates the readiness of each corresponding + // element in that / list, with true indicating ready. + std::vector PollOneoff(std::vector const& in); +}}} +namespace autosar { namespace ara { namespace radar { + StreamProxyHandle StartFindService(radar::InstanceSpecifier spec); +}}} +namespace autosar { namespace ara { namespace log { +}}} +namespace autosar { namespace ara { namespace e2exf { +}}} +namespace autosar { namespace ara { namespace com { + bool StatusHandlerConfigure(std::string_view const& binding_configuration, ::autosar::ara::com::ConfigurationFormat binding_format, std::string_view const& e2exf_configuration, ::autosar::ara::com::ConfigurationFormat e2exf_format); + std::vector ResolveInstanceIds(com::InstanceSpecifier spec); +}}} +namespace autosar { namespace ara { namespace exec { +}}} + +#endif diff --git a/tests/other/result_resource.wit b/tests/other/result_resource.wit new file mode 100644 index 000000000..92121134e --- /dev/null +++ b/tests/other/result_resource.wit @@ -0,0 +1,12 @@ +package test:example + +interface my-interface { + resource r + + function1: func() -> option + function2: func() -> result +} + +world test-world { + import my-interface +} diff --git a/tests/other/simple.wit b/tests/other/simple.wit new file mode 100644 index 000000000..68fcec8ef --- /dev/null +++ b/tests/other/simple.wit @@ -0,0 +1,13 @@ +package test:example + +interface my-interface { + resource my-object { + constructor(a: u32) + set: func(v: u32) + get: func() -> u32 + } +} + +world my-world { + import my-interface +} diff --git a/tests/other/simple2.wit b/tests/other/simple2.wit new file mode 100644 index 000000000..09b4c6837 --- /dev/null +++ b/tests/other/simple2.wit @@ -0,0 +1,25 @@ +package test:example + +interface my-interface { + resource pollable + + resource my-object { + constructor(a: u32) + set: func(v: u32) + get: func() -> u32 + //static consume: func(obj: own) + } + + resource stream-u16 { + subscribe: func() -> pollable + value: func() -> option + } + + resource container { + create: func() -> result + } +} + +world my-world2 { + import my-interface +} diff --git a/tests/other/wasm_export.h b/tests/other/wasm_export.h new file mode 100644 index 000000000..b5597de2a --- /dev/null +++ b/tests/other/wasm_export.h @@ -0,0 +1,11 @@ +typedef void* wasm_exec_env_t; +typedef void* wasm_module_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); From 4ce1ce6bca42216d8737f5e6cefd05a9074b26df Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 27 Nov 2023 20:43:14 +0100 Subject: [PATCH 002/672] exported functions don't use self as the first argument name (yet) --- crates/cpp/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index ed57f6826..943960b6b 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -887,7 +887,7 @@ impl CppInterfaceGenerator<'_> { let mut namespace = namespace(self.resolve, &owner.owner); namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); self.gen.c_src.qualify(&namespace); - self.gen.c_src.src.push_str("remove_resource(self);\n"); + uwriteln!(self.gen.c_src.src, "remove_resource({});", params[0]); } LiftLower::LowerArgsLiftResults => { let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); @@ -1748,9 +1748,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.let_results(func.results.len(), results); let (mut namespace, func_name_h) = self.gen.func_namespace_name(func); if matches!(func.kind, FunctionKind::Method(_)) { - let _ = operands.remove(0); + let this = operands.remove(0); self.gen.gen.c_src.qualify(&namespace); - self.gen.gen.c_src.src.push_str("lookup_resource(self)->"); + uwrite!(self.gen.gen.c_src.src, "lookup_resource({this})->"); } else { if matches!(func.kind, FunctionKind::Constructor(_)) { let _ = namespace.pop(); From d401ec9b0deb2b3a46aaa6b153ac5a3d1126ce69 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 27 Nov 2023 22:03:28 +0100 Subject: [PATCH 003/672] support user defined classes without private impl --- crates/cpp/src/lib.rs | 129 +++++++++++++++++++++++------------------- 1 file changed, 70 insertions(+), 59 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 943960b6b..446884198 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -74,6 +74,7 @@ struct Cpp { world: String, world_id: Option, imported_interfaces: HashSet, + user_class_files: HashMap, } #[derive(Default, Debug, Clone)] @@ -132,6 +133,29 @@ impl Cpp { wasm_import_module, } } + + fn clang_format(code: &mut Source) { + 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.as_mut_string().truncate(0); + child + .stdout + .take() + .unwrap() + .read_to_string(code.as_mut_string()) + .unwrap(); + let status = child.wait().unwrap(); + assert!(status.success()); + } } impl WorldGenerator for Cpp { @@ -413,7 +437,7 @@ impl WorldGenerator for Cpp { uwriteln!( c_str.src, " static NativeSymbol {}_funs[] = {{", - i.0.replace(&[':','.','-','+'], "_").to_snake_case() + i.0.replace(&[':', '.', '-', '+'], "_").to_snake_case() ); for f in i.1.iter() { uwriteln!( @@ -439,46 +463,8 @@ impl WorldGenerator for Cpp { ); if self.opts.format { - 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(c_str.src.as_bytes()) - .unwrap(); - c_str.src.as_mut_string().truncate(0); - child - .stdout - .take() - .unwrap() - .read_to_string(c_str.src.as_mut_string()) - .unwrap(); - let status = child.wait().unwrap(); - assert!(status.success()); - child = Command::new("clang-format") - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - .expect("failed to spawn `clang-format`"); - child - .stdin - .take() - .unwrap() - .write_all(h_str.src.as_bytes()) - .unwrap(); - h_str.src.as_mut_string().truncate(0); - child - .stdout - .take() - .unwrap() - .read_to_string(h_str.src.as_mut_string()) - .unwrap(); - let status = child.wait().unwrap(); - assert!(status.success()); + Self::clang_format(&mut c_str.src); + Self::clang_format(&mut h_str.src); } if !self.opts.host { @@ -488,6 +474,9 @@ impl WorldGenerator for Cpp { 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() { + files.push(&name, content.as_bytes()); + } } } @@ -628,7 +617,7 @@ impl CppInterfaceGenerator<'_> { } // print the signature of the lowered (wasm) function calling into highlevel - fn export_signature(&mut self, func: &Function) -> Vec { + fn print_export_signature(&mut self, func: &Function) -> Vec { let is_drop = is_drop_method(func); let signature = if is_drop { WasmSignature { @@ -655,6 +644,9 @@ impl CppInterfaceGenerator<'_> { let export_name = CppInterfaceGenerator::export_name2(&module_name, &func.name); self.gen.c_src.src.push_str(&export_name); self.gen.c_src.src.push_str("("); + if self.gen.opts.host { + self.gen.c_src.src.push_str("wasm_exec_env_t exec_env, "); + } let mut params = Vec::new(); for (n, ty) in signature.params.iter().enumerate() { let name = format!("arg{n}"); @@ -749,7 +741,7 @@ impl CppInterfaceGenerator<'_> { // we want to separate the lowered signature (wasm) and the high level signature if !import { - return self.export_signature(func); + return self.print_export_signature(func); } // self.rustdoc(&func.docs); @@ -1111,12 +1103,22 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> let import = self.gen.imported_interfaces.contains(&intf) ^ self.gen.opts.host; let mut world_name = self.gen.world.to_snake_case(); world_name.push_str("::"); - let funcs = self.resolve.interfaces[intf].functions.values(); + let mut headerfile = SourceWithState::default(); let namespc = namespace(self.resolve, &type_.owner); + let pascal = name.to_upper_camel_case(); + let user_filename = namespc.join("-") + "-" + &pascal + ".h"; + if !import { + 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 {user_filename}.template. + */"# + ); + } self.gen.h_src.change_namespace(&namespc); self.gen.dependencies.needs_resources = true; - let pascal = name.to_upper_camel_case(); if !import { uwriteln!(self.gen.c_src.src, "template std::map {world_name}{RESOURCE_BASE_CLASS_NAME}::resources;"); @@ -1129,21 +1131,21 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> }; let derive = format!(" : public {world_name}{RESOURCE_BASE_CLASS_NAME}{base_type}"); uwriteln!(self.gen.h_src.src, "class {pascal}{derive} {{\n"); - if !import { - // TODO: Replace with virtual functions - uwriteln!( - self.gen.h_src.src, - " // private implementation data\n struct pImpl;\n pImpl * p_impl;\n" - ); - } + // if !import { + // // TODO: Replace with virtual functions + // uwriteln!( + // self.gen.h_src.src, + // " // private implementation data\n struct pImpl;\n pImpl * p_impl;\n" + // ); + // } uwriteln!(self.gen.h_src.src, "public:\n"); - if !import { - // because of pimpl - uwriteln!( - self.gen.h_src.src, - " {pascal}({pascal}&&);\n{pascal}(const {pascal}&) = delete;\nvoid operator=({pascal}&&);\nvoid operator=(const {pascal}&) = delete;" - ); - } + // if !import { + // // because of pimpl + // uwriteln!( + // self.gen.h_src.src, + // " {pascal}({pascal}&&);\n{pascal}(const {pascal}&) = delete;\nvoid operator=({pascal}&&);\nvoid operator=(const {pascal}&) = delete;" + // ); + // } // destructor { let name = "[resource-drop]".to_string() + &name; @@ -1156,6 +1158,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> }; self.generate_guest_import(&func); } + let funcs = self.resolve.interfaces[intf].functions.values(); for func in funcs { // Some(name), self.generate_guest_import(func); @@ -1170,6 +1173,14 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> uwriteln!(self.gen.h_src.src, "{pascal}({pascal}&&) = default;\n"); } uwriteln!(self.gen.h_src.src, "}};\n"); + if !import { + // 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 + ".template", headerfile.src.to_string()); + } } } From 1894640661008a8e2a8792c6db198f880b09e272 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 27 Nov 2023 22:37:30 +0100 Subject: [PATCH 004/672] clean up --- crates/cpp/src/lib.rs | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 446884198..df35d75a6 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1108,6 +1108,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> let pascal = name.to_upper_camel_case(); let user_filename = namespc.join("-") + "-" + &pascal + ".h"; if !import { + // 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, @@ -1131,21 +1132,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> }; let derive = format!(" : public {world_name}{RESOURCE_BASE_CLASS_NAME}{base_type}"); uwriteln!(self.gen.h_src.src, "class {pascal}{derive} {{\n"); - // if !import { - // // TODO: Replace with virtual functions - // uwriteln!( - // self.gen.h_src.src, - // " // private implementation data\n struct pImpl;\n pImpl * p_impl;\n" - // ); - // } uwriteln!(self.gen.h_src.src, "public:\n"); - // if !import { - // // because of pimpl - // uwriteln!( - // self.gen.h_src.src, - // " {pascal}({pascal}&&);\n{pascal}(const {pascal}&) = delete;\nvoid operator=({pascal}&&);\nvoid operator=(const {pascal}&) = delete;" - // ); - // } // destructor { let name = "[resource-drop]".to_string() + &name; @@ -1160,7 +1147,6 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } let funcs = self.resolve.interfaces[intf].functions.values(); for func in funcs { - // Some(name), self.generate_guest_import(func); } @@ -1178,8 +1164,12 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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 + ".template", headerfile.src.to_string()); + if self.gen.opts.format { + Cpp::clang_format(&mut headerfile.src); + } + self.gen + .user_class_files + .insert(user_filename + ".template", headerfile.src.to_string()); } } } From 4d28ee4cca2c93ff0ef963905193bc53aee1a13e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 27 Nov 2023 22:59:17 +0100 Subject: [PATCH 005/672] re-enabled records --- crates/cpp/src/lib.rs | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index df35d75a6..1d8d2702d 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1075,6 +1075,16 @@ impl CppInterfaceGenerator<'_> { self.gen.c_src.src.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> { @@ -1084,12 +1094,24 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> fn type_record( &mut self, - _id: TypeId, + id: TypeId, name: &str, - _record: &wit_bindgen_core::wit_parser::Record, - _docs: &wit_bindgen_core::wit_parser::Docs, + record: &wit_bindgen_core::wit_parser::Record, + docs: &wit_bindgen_core::wit_parser::Docs, ) { - uwriteln!(self.gen.h_src.src, "// type_record({name})"); + let ty = &self.resolve.types[id]; + let namespc = namespace(self.resolve, &ty.owner); + 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); + let fname = field.name.to_lower_camel_case(); + uwriteln!(self.gen.h_src.src, "{typename} {fname};"); + } + uwriteln!(self.gen.h_src.src, "}};"); } fn type_resource( From 71c9458a6f30a54171cae45966323aa50dbb354f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 27 Nov 2023 23:21:01 +0100 Subject: [PATCH 006/672] type alias --- crates/cpp/src/lib.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 1d8d2702d..7438a786f 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1258,12 +1258,18 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> fn type_alias( &mut self, - _id: TypeId, + id: TypeId, name: &str, - _ty: &wit_bindgen_core::wit_parser::Type, - _docs: &wit_bindgen_core::wit_parser::Docs, + alias_type: &wit_bindgen_core::wit_parser::Type, + docs: &wit_bindgen_core::wit_parser::Docs, ) { - uwriteln!(self.gen.h_src.src, "// type_alias({name})"); + let ty = &self.resolve.types[id]; + let namespc = namespace(self.resolve, &ty.owner); + 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); + uwriteln!(self.gen.h_src.src, "using {pascal} = {typename};"); } fn type_list( From de187d39f09099e415589bd4a8d433466e095dc8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 28 Nov 2023 00:04:16 +0100 Subject: [PATCH 007/672] support relative type namespace --- crates/cpp/src/lib.rs | 88 +++++++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 24 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7438a786f..f4a3cb79e 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -675,7 +675,12 @@ impl CppInterfaceGenerator<'_> { params } - fn high_level_signature(&mut self, func: &Function, import: bool) -> HighlevelSignature { + fn high_level_signature( + &mut self, + func: &Function, + import: bool, + from_namespace: &Vec, + ) -> HighlevelSignature { let mut res = HighlevelSignature::default(); let (namespace, func_name_h) = self.func_namespace_name(func); @@ -694,7 +699,7 @@ impl CppInterfaceGenerator<'_> { } } wit_bindgen_core::wit_parser::Results::Anon(ty) => { - res.result = self.type_name(ty); + res.result = self.type_name(ty, from_namespace); } } } @@ -705,7 +710,8 @@ impl CppInterfaceGenerator<'_> { if i == 0 && name == "self" { continue; } - res.arguments.push((name.clone(), self.type_name(param))); + res.arguments + .push((name.clone(), self.type_name(param, &res.namespace))); } // default to non-const when exporting a method if matches!(func.kind, FunctionKind::Method(_)) && import { @@ -715,7 +721,8 @@ impl CppInterfaceGenerator<'_> { } fn print_signature(&mut self, func: &Function, import: bool) -> Vec { - let cpp_sig = self.high_level_signature(func, import); + let from_namespace = self.gen.h_src.namespace.clone(); + let cpp_sig = self.high_level_signature(func, import, &from_namespace); if cpp_sig.static_member { self.gen.h_src.src.push_str("static "); } @@ -768,7 +775,8 @@ impl CppInterfaceGenerator<'_> { sig.push_str("void"); result_ptr = Some(ty.clone()); } else { - sig.push_str(&self.type_name(ty)); + let from_namespace = self.gen.c_src.namespace.clone(); + sig.push_str(&self.type_name(ty, &from_namespace)); } } } @@ -818,7 +826,7 @@ impl CppInterfaceGenerator<'_> { for (i, (name, param)) in func.params.iter().enumerate() { if is_arg_by_pointer(self.resolve, param) { params.push(name.clone() + "_ptr"); - sig.push_str(&self.type_name(param)); + sig.push_str(&self.type_name(param, &namespace)); sig.push_str("* "); sig.push_str(&(name.clone() + "_ptr")); } else { @@ -833,7 +841,7 @@ impl CppInterfaceGenerator<'_> { } continue; } - sig.push_str(&self.type_name(param)); + sig.push_str(&self.type_name(param, &namespace)); sig.push_str(" "); sig.push_str(&name); } @@ -843,7 +851,7 @@ impl CppInterfaceGenerator<'_> { } if let Some(result_ptr) = &result_ptr { params.push("result_ptr".into()); - sig.push_str(&self.type_name(result_ptr)); + sig.push_str(&self.type_name(result_ptr, &namespace)); sig.push_str("* "); sig.push_str("result_ptr"); } @@ -956,7 +964,7 @@ impl CppInterfaceGenerator<'_> { } } - fn type_name(&mut self, ty: &Type) -> String { + fn type_name(&mut self, ty: &Type, from_namespace: &Vec) -> String { match ty { Type::Bool => "bool".into(), Type::Char => "uint32_t".into(), @@ -976,12 +984,21 @@ impl CppInterfaceGenerator<'_> { } Type::Id(id) => match &self.resolve.types[*id].kind { TypeDefKind::Record(_r) => { - format!("record.{}", self.resolve.types[*id].name.as_ref().unwrap()) + let ty = &self.resolve.types[*id]; + let namespc = namespace(self.resolve, &ty.owner); + 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()) } TypeDefKind::Resource => self.resolve.types[*id].name.as_ref().cloned().unwrap(), - TypeDefKind::Handle(Handle::Own(id)) => self.type_name(&Type::Id(*id)), + TypeDefKind::Handle(Handle::Own(id)) => { + self.type_name(&Type::Id(*id), from_namespace) + } TypeDefKind::Handle(Handle::Borrow(id)) => { - "std::reference_wrapper<".to_string() + &self.type_name(&Type::Id(*id)) + ">" + "std::reference_wrapper<".to_string() + + &self.type_name(&Type::Id(*id), from_namespace) + + ">" } TypeDefKind::Flags(_) => "Flags".to_string(), TypeDefKind::Tuple(_) => "Tuple".to_string(), @@ -991,7 +1008,7 @@ impl CppInterfaceGenerator<'_> { result += &case .ty .as_ref() - .map_or("void".to_string(), |ty| self.type_name(ty)); + .map_or("void".to_string(), |ty| self.type_name(ty, from_namespace)); if n + 1 != v.cases.len() { result += ", "; } @@ -1000,21 +1017,27 @@ impl CppInterfaceGenerator<'_> { result } TypeDefKind::Enum(_e) => "Enum".to_string(), - TypeDefKind::Option(o) => "std::optional<".to_string() + &self.type_name(o) + ">", + TypeDefKind::Option(o) => { + "std::optional<".to_string() + &self.type_name(o, from_namespace) + ">" + } TypeDefKind::Result(r) => { "std::expected<".to_string() - + &r.ok.as_ref().map_or("void".into(), |t| self.type_name(t)) + + &r.ok + .as_ref() + .map_or("void".into(), |t| self.type_name(t, from_namespace)) + ", " - + &r.err.as_ref().map_or("void".into(), |t| self.type_name(t)) + + &r.err + .as_ref() + .map_or("void".into(), |t| self.type_name(t, from_namespace)) + ">" } TypeDefKind::List(ty) => { self.gen.dependencies.needs_vector = true; - "std::vector<".to_string() + &self.type_name(ty) + ">" + "std::vector<".to_string() + &self.type_name(ty, from_namespace) + ">" } TypeDefKind::Future(_) => todo!(), TypeDefKind::Stream(_) => todo!(), - TypeDefKind::Type(ty) => self.type_name(ty), + TypeDefKind::Type(ty) => self.type_name(ty, from_namespace), TypeDefKind::Unknown => todo!(), }, } @@ -1107,7 +1130,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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); + let typename = self.type_name(&field.ty, &namespc); let fname = field.name.to_lower_camel_case(); uwriteln!(self.gen.h_src.src, "{typename} {fname};"); } @@ -1130,6 +1153,8 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> let pascal = name.to_upper_camel_case(); let user_filename = namespc.join("-") + "-" + &pascal + ".h"; if !import { + // 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!( @@ -1248,12 +1273,27 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> fn type_enum( &mut self, - _id: TypeId, + id: TypeId, name: &str, - _enum_: &wit_bindgen_core::wit_parser::Enum, - _docs: &wit_bindgen_core::wit_parser::Docs, + enum_: &wit_bindgen_core::wit_parser::Enum, + docs: &wit_bindgen_core::wit_parser::Docs, ) { - uwriteln!(self.gen.h_src.src, "// type_enum({name})"); + let ty = &self.resolve.types[id]; + let namespc = namespace(self.resolve, &ty.owner); + 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( @@ -1268,7 +1308,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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); + let typename = self.type_name(alias_type, &namespc); uwriteln!(self.gen.h_src.src, "using {pascal} = {typename};"); } From 655e76539b9f741551a65998a0a069b62c90fa93 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 28 Nov 2023 00:14:58 +0100 Subject: [PATCH 008/672] more load and store commands --- crates/cpp/src/lib.rs | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index f4a3cb79e..768987c0c 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1385,11 +1385,11 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { results.push(format!("*(({}*) ({} + {}))", ty, operands[0], offset)); } - // fn load_ext(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec) { - // self.load(ty, offset, operands, results); - // let result = results.pop().unwrap(); - // results.push(format!("(int32_t) ({})", result)); - // } + fn load_ext(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec) { + self.load(ty, offset, operands, results); + let result = results.pop().unwrap(); + results.push(format!("(int32_t) ({})", result)); + } fn store(&mut self, ty: &str, offset: i32, operands: &[String]) { uwriteln!( @@ -1473,23 +1473,26 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { results.push(format!("l{tmp}")); } abi::Instruction::I32Load8U { offset } => { - results.push(format!( - "(int32_t)(*((uint8_t const*)({} + {})))", - operands[0], 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::I32Load8S { offset: _ } => todo!(), - abi::Instruction::I32Load16U { offset: _ } => todo!(), - abi::Instruction::I32Load16S { offset: _ } => todo!(), - abi::Instruction::I64Load { offset: _ } => todo!(), - abi::Instruction::F32Load { offset: _ } => todo!(), - abi::Instruction::F64Load { offset: _ } => todo!(), + 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("int32_t", *offset, operands), abi::Instruction::I32Store16 { offset } => self.store("int32_t", *offset, operands), abi::Instruction::I64Store { offset } => self.store("int64_t", *offset, operands), - abi::Instruction::F32Store { offset: _ } => todo!(), - abi::Instruction::F64Store { offset: _ } => todo!(), + 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 From 0c7d5a4cd5e9b42e58b40f19e4585dc6064b5e12 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 28 Nov 2023 23:06:28 +0100 Subject: [PATCH 009/672] fix signature in more complex case --- crates/cpp/src/lib.rs | 147 +++++++++--------------------------------- 1 file changed, 32 insertions(+), 115 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 768987c0c..c7f5ddb31 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -34,6 +34,7 @@ struct HighlevelSignature { arguments: Vec<(String, CppType)>, name: String, namespace: Vec, + implicit_self: bool, } // follows https://google.github.io/styleguide/cppguide.html @@ -708,10 +709,11 @@ impl CppInterfaceGenerator<'_> { } for (i, (name, param)) in func.params.iter().enumerate() { if i == 0 && name == "self" { + res.implicit_self = true; continue; } res.arguments - .push((name.clone(), self.type_name(param, &res.namespace))); + .push((name.to_snake_case(), self.type_name(param, &res.namespace))); } // default to non-const when exporting a method if matches!(func.kind, FunctionKind::Method(_)) && import { @@ -748,124 +750,33 @@ impl CppInterfaceGenerator<'_> { // we want to separate the lowered signature (wasm) and the high level signature if !import { - return self.print_export_signature(func); - } - - // self.rustdoc(&func.docs); - // self.rustdoc_params(&func.params, "Parameters"); - // TODO: re-add this when docs are back - // self.rustdoc_params(&func.results, "Return"); - - let (namespace, func_name_h) = self.func_namespace_name(func); - let is_drop = is_drop_method(func); - // we might want to separate c_sig and h_sig - let mut sig = String::new(); - let mut result_ptr: Option = None; - if !matches!(&func.kind, FunctionKind::Constructor(_)) && !is_drop { - match &func.results { - wit_bindgen_core::wit_parser::Results::Named(n) => { - if n.len() == 0 { - sig.push_str("void"); - } else { - todo!(); - } - } - wit_bindgen_core::wit_parser::Results::Anon(ty) => { - if is_arg_by_pointer(self.resolve, ty) { - sig.push_str("void"); - result_ptr = Some(ty.clone()); - } else { - let from_namespace = self.gen.c_src.namespace.clone(); - sig.push_str(&self.type_name(ty, &from_namespace)); - } - } - } - sig.push_str(" "); - } - if import { - self.gen.c_src.src.push_str(&sig); - self.gen.c_src.qualify(&namespace); - self.gen.c_src.src.push_str(&func_name_h); + self.print_export_signature(func) } else { - self.gen.c_src.src.push_str("static "); - if matches!(&func.kind, FunctionKind::Constructor(_)) { - self.gen.c_src.src.push_str("int32_t "); - } else if is_drop { - self.gen.c_src.src.push_str("void "); - } else { - self.gen.c_src.src.push_str(&sig); + let mut params = Vec::new(); + self.gen.c_src.src.push_str(&cpp_sig.result); + if !cpp_sig.result.is_empty() { + self.gen.c_src.src.push_str(" "); } - let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); - let full_name = "host_".to_string() + &Self::export_name2(&module_name, &func.name); - self.gen.c_src.src.push_str(&full_name); - if self.gen.opts.host { - let signature = wamr::wamr_signature(self.resolve, func); - let remember = HostFunction { - wasm_name: func.name.clone(), - wamr_signature: signature.to_string(), - host_name: full_name, - }; - self.gen - .host_functions - .entry(module_name) - .and_modify(|v| v.push(remember.clone())) - .or_insert(vec![remember]); - } - } - sig.push_str(&func_name_h); - //self.gen.h_src.src.push_str(&sig); - sig.clear(); - self.gen.c_src.src.push_str("("); - if self.gen.opts.host { - self.gen.c_src.src.push_str("wasm_exec_env_t exec_env"); - if func.params.len() > 0 { - self.gen.c_src.src.push_str(", "); - } - } - let mut params = Vec::new(); - for (i, (name, param)) in func.params.iter().enumerate() { - if is_arg_by_pointer(self.resolve, param) { - params.push(name.clone() + "_ptr"); - sig.push_str(&self.type_name(param, &namespace)); - sig.push_str("* "); - sig.push_str(&(name.clone() + "_ptr")); - } else { - params.push(name.clone()); - if i == 0 && name == "self" { - if !import { - self.gen.c_src.src.push_str("int32_t "); - self.gen.c_src.src.push_str(&name); - if i + 1 != func.params.len() { - self.gen.c_src.src.push_str(", "); - } - } - continue; + 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 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(", "); } - sig.push_str(&self.type_name(param, &namespace)); - sig.push_str(" "); - sig.push_str(&name); + 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()); } - if i + 1 != func.params.len() { - sig.push_str(","); + 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 } - if let Some(result_ptr) = &result_ptr { - params.push("result_ptr".into()); - sig.push_str(&self.type_name(result_ptr, &namespace)); - sig.push_str("* "); - sig.push_str("result_ptr"); - } - sig.push_str(")"); - // default to non-const when exporting a method - if matches!(func.kind, FunctionKind::Method(_)) && import { - sig.push_str("const"); - } - self.gen.c_src.src.push_str(&sig); - self.gen.c_src.src.push_str("\n"); - // self.gen.h_src.src.push_str("("); - // sig.push_str(";\n"); - // self.gen.h_src.src.push_str(&sig); - params } fn generate_guest_import(&mut self, func: &Function) { @@ -989,7 +900,11 @@ impl CppInterfaceGenerator<'_> { 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()) + format!( + "{}{}", + relative.src.to_string(), + ty.name.as_ref().unwrap().to_pascal_case() + ) } TypeDefKind::Resource => self.resolve.types[*id].name.as_ref().cloned().unwrap(), TypeDefKind::Handle(Handle::Own(id)) => { @@ -1484,7 +1399,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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::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), From 3f97ab3f9a36a746e835b8240035e55925b5e71b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 28 Nov 2023 23:27:36 +0100 Subject: [PATCH 010/672] return pointer proposal --- crates/cpp/src/lib.rs | 55 ++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index c7f5ddb31..d2edfd1fb 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -760,7 +760,9 @@ impl CppInterfaceGenerator<'_> { 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 cpp_sig.implicit_self { params.push("(*this)".into()); } + 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(", "); @@ -1447,10 +1449,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let len = format!("len{}", tmp); let result = format!("result{}", tmp); if realloc.is_none() { - self.push_str(&format!("auto {} = {};\n", val, operands[0])); + self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); self.push_str(&format!("auto {} = (int32_t)({}.data());\n", ptr, val)); self.push_str(&format!("auto {} = (int32_t)({}.size());\n", len, val)); - self.push_str("// is this correct?\n"); + // TODO: allocate ret_area + // self.push_str("// is this correct?\n"); } else { self.gen.gen.dependencies.needs_guest_alloc = true; uwriteln!( @@ -1783,27 +1786,41 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { fn return_pointer(&mut self, size: usize, align: usize) -> Self::Operand { let tmp = self.tmp(); + let elems = (size + (align - 1)) / align; + let tp = match align { + 1 => "uint8_t", + 2 => "uint16_t", + 4 => "uint32_t", + 8 => "uint64_t", + _ => todo!(), + }; + uwriteln!(self.gen.gen.c_src.src, " {tp} ret_area[{elems}];"); + + uwrite!( + self.gen.gen.c_src.src, + "int32_t ptr{tmp} = int32_t(&ret_area);" + ); // Imports get a per-function return area to facilitate using the // stack whereas exports use a per-module return area to cut down on // stack usage. Note that for imports this also facilitates "adapter // modules" for components to not have data segments. - if self.gen.in_import { - self.import_return_pointer_area_size = self.import_return_pointer_area_size.max(size); - self.import_return_pointer_area_align = - self.import_return_pointer_area_align.max(align); - uwrite!( - self.gen.gen.c_src.src, - "int32_t ptr{tmp} = int32_t(&ret_area);" - ); - } else { - self.gen.return_pointer_area_size = self.gen.return_pointer_area_size.max(size); - self.gen.return_pointer_area_align = self.gen.return_pointer_area_align.max(align); - uwriteln!( - self.gen.gen.c_src.src, - "int32_t ptr{tmp} = int32_t(&RET_AREA);" - ); - } + // if self.gen.in_import { + // self.import_return_pointer_area_size = self.import_return_pointer_area_size.max(size); + // self.import_return_pointer_area_align = + // self.import_return_pointer_area_align.max(align); + // uwrite!( + // self.gen.gen.c_src.src, + // "int32_t ptr{tmp} = int32_t(&ret_area);" + // ); + // } else { + // self.gen.return_pointer_area_size = self.gen.return_pointer_area_size.max(size); + // self.gen.return_pointer_area_align = self.gen.return_pointer_area_align.max(align); + // uwriteln!( + // self.gen.gen.c_src.src, + // "int32_t ptr{tmp} = int32_t(&RET_AREA);" + // ); + // } format!("ptr{}", tmp) } From c8eb335f57f50e006e65b78b1b94a7483f1110b0 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 28 Nov 2023 23:41:55 +0100 Subject: [PATCH 011/672] proper enum name --- crates/cpp/src/lib.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index d2edfd1fb..4770da341 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -933,7 +933,18 @@ impl CppInterfaceGenerator<'_> { result += ">"; result } - TypeDefKind::Enum(_e) => "Enum".to_string(), + TypeDefKind::Enum(_e) => { + let ty = &self.resolve.types[*id]; + let namespc = namespace(self.resolve, &ty.owner); + 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() + ) + } TypeDefKind::Option(o) => { "std::optional<".to_string() + &self.type_name(o, from_namespace) + ">" } @@ -1452,8 +1463,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); self.push_str(&format!("auto {} = (int32_t)({}.data());\n", ptr, val)); self.push_str(&format!("auto {} = (int32_t)({}.size());\n", len, val)); - // TODO: allocate ret_area - // self.push_str("// is this correct?\n"); + results.push(ptr); } else { self.gen.gen.dependencies.needs_guest_alloc = true; uwriteln!( @@ -1461,8 +1471,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { "int32_t {result} = guest_alloc(exec_env, {len});" ); uwriteln!(self.gen.gen.c_src.src, "memcpy(wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), {result}), {ptr}, {len});"); + results.push(result); } - results.push(result); results.push(len); } abi::Instruction::ListLower { From 8ae441538078b4147f797ff9532a219ccc2cbbbe Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 29 Nov 2023 00:15:29 +0100 Subject: [PATCH 012/672] fix guest example --- crates/cpp/src/lib.rs | 51 ++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 4770da341..dd819a66b 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -747,11 +747,15 @@ impl CppInterfaceGenerator<'_> { self.gen.h_src.src.push_str(" const"); } 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.print_export_signature(func) } else { + // recalulate with c file namespace + let c_namespace = self.gen.c_src.namespace.clone(); + let cpp_sig = self.high_level_signature(func, import, &c_namespace); let mut params = Vec::new(); self.gen.c_src.src.push_str(&cpp_sig.result); if !cpp_sig.result.is_empty() { @@ -814,7 +818,17 @@ impl CppInterfaceGenerator<'_> { } } } else { + let owner = &self.resolve.types[match &func.kind { + FunctionKind::Static(id) => *id, + FunctionKind::Constructor(id) => *id, + FunctionKind::Method(id) => *id, + FunctionKind::Freestanding => todo!(), + }] + .clone(); + let mut namespace = namespace(self.resolve, &owner.owner); + namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); let mut f = FunctionBindgen::new(self, params); + f.namespace = namespace; abi::call( f.gen.resolve, AbiVariant::GuestImport, @@ -1265,8 +1279,9 @@ struct FunctionBindgen<'a, 'b> { gen: &'b mut CppInterfaceGenerator<'a>, params: Vec, tmp: usize, - import_return_pointer_area_size: usize, - import_return_pointer_area_align: usize, + // import_return_pointer_area_size: usize, + // import_return_pointer_area_align: usize, + namespace: Vec, } impl<'a, 'b> FunctionBindgen<'a, 'b> { @@ -1275,8 +1290,9 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { gen, params, tmp: 0, - import_return_pointer_area_size: 0, - import_return_pointer_area_align: 0, + // import_return_pointer_area_size: 0, + // import_return_pointer_area_align: 0, + namespace: Default::default(), } } @@ -1432,19 +1448,19 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { | abi::Instruction::I32FromU32 | abi::Instruction::I32FromS32 => top_as("int32_t"), abi::Instruction::I64FromU64 | abi::Instruction::I64FromS64 => top_as("int64_t"), - abi::Instruction::F32FromFloat32 => todo!(), - abi::Instruction::F64FromFloat64 => todo!(), - abi::Instruction::S8FromI32 => todo!(), - abi::Instruction::U8FromI32 => todo!(), - abi::Instruction::S16FromI32 => todo!(), - abi::Instruction::U16FromI32 => todo!(), + abi::Instruction::F32FromFloat32 => top_as("float"), + abi::Instruction::F64FromFloat64 => 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 => todo!(), + abi::Instruction::S64FromI64 => top_as("int64_t"), abi::Instruction::U64FromI64 => top_as("uint64_t"), - abi::Instruction::CharFromI32 => todo!(), - abi::Instruction::Float32FromF32 => todo!(), - abi::Instruction::Float64FromF64 => todo!(), + abi::Instruction::CharFromI32 => top_as("uint32_t"), + abi::Instruction::Float32FromF32 => top_as("float"), + abi::Instruction::Float64FromF64 => top_as("double"), abi::Instruction::BoolFromI32 => top_as("bool"), abi::Instruction::ListCanonLower { element: _, @@ -1683,10 +1699,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } => results.push(format!("int32_t({})", operands[0])), abi::Instruction::EnumLift { enum_: _, - name, - ty: _, + name: _, + ty, } => { - results.push(format!("({name}){}", &operands[0])); + let typename = self.gen.type_name(&Type::Id(*ty), &self.namespace); + results.push(format!("({typename}){}", &operands[0])); } abi::Instruction::OptionLower { payload: _, From a4397475fa972667ad77e98be578d4037d65cf79 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 30 Nov 2023 22:21:10 +0100 Subject: [PATCH 013/672] handle free functions --- crates/cpp/src/lib.rs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index dd819a66b..aa6c1716f 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -207,7 +207,7 @@ impl WorldGenerator for Cpp { for (_name, func) in resolve.interfaces[id].functions.iter() { if matches!(func.kind, FunctionKind::Freestanding) { - gen.generate_guest_import(func); + gen.generate_guest_import(func, id); } } // gen.finish(); @@ -785,7 +785,7 @@ impl CppInterfaceGenerator<'_> { } } - fn generate_guest_import(&mut self, func: &Function) { + fn generate_guest_import(&mut self, func: &Function, interface: InterfaceId) { let params = self.print_signature(func, !self.gen.opts.host); self.gen.c_src.src.push_str("{\n"); let lift_lower = if self.gen.opts.host { @@ -818,15 +818,20 @@ impl CppInterfaceGenerator<'_> { } } } else { - let owner = &self.resolve.types[match &func.kind { - FunctionKind::Static(id) => *id, - FunctionKind::Constructor(id) => *id, - FunctionKind::Method(id) => *id, - FunctionKind::Freestanding => todo!(), - }] - .clone(); - let mut namespace = namespace(self.resolve, &owner.owner); - namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); + let namespace = if matches!(func.kind, FunctionKind::Freestanding) { + namespace(self.resolve, &TypeOwner::Interface(interface)) + } else { + let owner = &self.resolve.types[match &func.kind { + FunctionKind::Static(id) => *id, + FunctionKind::Constructor(id) => *id, + FunctionKind::Method(id) => *id, + FunctionKind::Freestanding => unreachable!(), + }] + .clone(); + let mut namespace = namespace(self.resolve, &owner.owner); + namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); + namespace + }; let mut f = FunctionBindgen::new(self, params); f.namespace = namespace; abi::call( @@ -1132,11 +1137,11 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> results: Results::Named(vec![]), docs: Docs::default(), }; - self.generate_guest_import(&func); + self.generate_guest_import(&func, intf); } let funcs = self.resolve.interfaces[intf].functions.values(); for func in funcs { - self.generate_guest_import(func); + self.generate_guest_import(func, intf); } if import { From 47e1fd9e8c4473f73ca0a1924e80b69d09e46976 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 30 Nov 2023 23:24:58 +0100 Subject: [PATCH 014/672] many more implementations --- crates/cpp/src/lib.rs | 172 +++++++++++++++++++++++++++++++----------- 1 file changed, 126 insertions(+), 46 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index aa6c1716f..29263690a 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -841,6 +841,8 @@ impl CppInterfaceGenerator<'_> { func, &mut f, ); + let code = String::from(f.src); + self.gen.c_src.src.push_str(&code); } self.gen.c_src.src.push_str("}\n"); } @@ -1287,6 +1289,10 @@ struct FunctionBindgen<'a, 'b> { // import_return_pointer_area_size: usize, // import_return_pointer_area_align: usize, namespace: Vec, + src: Source, + block_storage: Vec, + blocks: Vec<(String, Vec)>, + payloads: Vec, } impl<'a, 'b> FunctionBindgen<'a, 'b> { @@ -1298,6 +1304,10 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { // 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(), } } @@ -1308,7 +1318,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } fn push_str(&mut self, s: &str) { - self.gen.gen.c_src.src.push_str(s); + self.src.push_str(s); } fn typename_lift(&self, id: TypeId) -> String { @@ -1342,7 +1352,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { fn store(&mut self, ty: &str, offset: i32, operands: &[String]) { uwriteln!( - self.gen.gen.c_src.src, + self.src, "*(({}*)({} + {})) = {};", ty, operands[1], @@ -1415,7 +1425,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::I32Load { offset } => { let tmp = self.tmp(); uwriteln!( - self.gen.gen.c_src.src, + self.src, "int32_t l{tmp} = *((int32_t const*)({} + {offset}));", operands[0] ); @@ -1487,11 +1497,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { results.push(ptr); } else { self.gen.gen.dependencies.needs_guest_alloc = true; - uwriteln!( - self.gen.gen.c_src.src, - "int32_t {result} = guest_alloc(exec_env, {len});" - ); - uwriteln!(self.gen.gen.c_src.src, "memcpy(wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), {result}), {ptr}, {len});"); + uwriteln!(self.src, "int32_t {result} = guest_alloc(exec_env, {len});"); + uwriteln!(self.src, "memcpy(wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), {result}), {ptr}, {len});"); results.push(result); } results.push(len); @@ -1513,7 +1520,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::StringLift => { let tmp = self.tmp(); let len = format!("len{}", tmp); - uwriteln!(self.gen.gen.c_src.src, "auto {} = {};\n", len, operands[1]); + uwriteln!(self.src, "auto {} = {};\n", len, operands[1]); let result = format!("std::string((char const*)({}), {len})", operands[0]); results.push(result); } @@ -1539,14 +1546,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { "#, )); - uwriteln!( - self.gen.gen.c_src.src, - "for (unsigned i=0; i<{len}; ++i) {{" - ); - uwriteln!(self.gen.gen.c_src.src, "auto base = {base} + i * {size};"); - uwriteln!(self.gen.gen.c_src.src, "auto e{tmp} = todo();"); - uwriteln!(self.gen.gen.c_src.src, "{result}.push_back(e{tmp});"); - uwriteln!(self.gen.gen.c_src.src, "}}"); + uwriteln!(self.src, "for (unsigned i=0; i<{len}; ++i) {{"); + uwriteln!(self.src, "auto base = {base} + i * {size};"); + uwriteln!(self.src, "auto e{tmp} = todo();"); + uwriteln!(self.src, "{result}.push_back(e{tmp});"); + uwriteln!(self.src, "}}"); results.push(result); // self.push_str(&format!( // "{rt}::dealloc({base}, ({len} as usize) * {size}, {align});\n", @@ -1607,7 +1611,22 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { results.push("TupleLower1".into()); results.push("TupleLower2".into()); } - abi::Instruction::TupleLift { tuple: _, ty: _ } => todo!(), + abi::Instruction::TupleLift { tuple, ty: _ } => { + 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))) + .collect::>() + .join(", "), + ); + self.src.push_str(">("); + self.src.push_str(&operands.join(", ")); + self.src.push_str(");\n"); + results.push(name); + } abi::Instruction::FlagsLower { flags, name: _, @@ -1624,16 +1643,68 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { name: _, ty: _, } => results.push("FlagsLift".to_string()), - abi::Instruction::VariantPayloadName => results.push("e".to_string()), + abi::Instruction::VariantPayloadName => { + let name = format!("payload{}", self.tmp()); + results.push(format!("*{}", name)); + self.payloads.push(name); + } abi::Instruction::VariantLower { - variant: _, - name, + variant, + name: _, ty: _, - results: _, + results: result_types, } => { //let name = self.gen.type_name(*ty); - let op0 = &operands[0]; - self.push_str(&format!("({name}){op0}")); + // let op0 = &operands[0]; + // self.push_str(&format!("({name}){op0}")); + let blocks = self + .blocks + .drain(self.blocks.len() - variant.cases.len()..) + .collect::>(); + let payloads = self + .payloads + .drain(self.payloads.len() - variant.cases.len()..) + .collect::>(); + + 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(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); + 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, @@ -1715,7 +1786,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { ty: _, results: _, } => self.push_str("OptionLower"), - abi::Instruction::OptionLift { payload: _, ty: _ } => todo!(), + abi::Instruction::OptionLift { payload, ty: _ } => { + let mut result: String = "std::optional<".into(); + result.push_str(&self.gen.type_name(*payload, &self.namespace)); + result.push_str(">("); + result.push_str(&operands[0]); + result.push(')'); + results.push(result); + } abi::Instruction::ResultLower { result: _, ty: _, @@ -1759,13 +1837,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // ... then call the function with all our operands if sig.results.len() > 0 { - self.gen.gen.c_src.src.push_str("auto ret = "); + self.src.push_str("auto ret = "); results.push("ret".to_string()); } - self.gen.gen.c_src.src.push_str(&func); - self.gen.gen.c_src.src.push_str("("); - self.gen.gen.c_src.src.push_str(&operands.join(", ")); - self.gen.gen.c_src.src.push_str(");\n"); + 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); @@ -1774,14 +1852,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if matches!(func.kind, FunctionKind::Method(_)) { let this = operands.remove(0); self.gen.gen.c_src.qualify(&namespace); - uwrite!(self.gen.gen.c_src.src, "lookup_resource({this})->"); + uwrite!(self.src, "lookup_resource({this})->"); } else { if matches!(func.kind, FunctionKind::Constructor(_)) { let _ = namespace.pop(); } self.gen.gen.c_src.qualify(&namespace); } - self.gen.gen.c_src.src.push_str(&func_name_h); + self.src.push_str(&func_name_h); self.push_str("("); self.push_str(&operands.join(", ")); self.push_str(");"); @@ -1794,12 +1872,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { match &func.kind { FunctionKind::Constructor(_) if import => { // strange but works - self.gen.gen.c_src.src.push_str("this->handle = "); + self.src.push_str("this->handle = "); } - _ => self.gen.gen.c_src.src.push_str("return "), + _ => self.src.push_str("return "), } - self.gen.gen.c_src.src.push_str(&operands[0]); - self.gen.gen.c_src.src.push_str(";\n"); + self.src.push_str(&operands[0]); + self.src.push_str(";\n"); } _ => todo!(), } @@ -1826,12 +1904,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 8 => "uint64_t", _ => todo!(), }; - uwriteln!(self.gen.gen.c_src.src, " {tp} ret_area[{elems}];"); + uwriteln!(self.src, " {tp} ret_area[{elems}];"); - uwrite!( - self.gen.gen.c_src.src, - "int32_t ptr{tmp} = int32_t(&ret_area);" - ); + uwrite!(self.src, "int32_t ptr{tmp} = int32_t(&ret_area);"); // Imports get a per-function return area to facilitate using the // stack whereas exports use a per-module return area to cut down on @@ -1842,14 +1917,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // self.import_return_pointer_area_align = // self.import_return_pointer_area_align.max(align); // uwrite!( - // self.gen.gen.c_src.src, + // self.src, // "int32_t ptr{tmp} = int32_t(&ret_area);" // ); // } else { // self.gen.return_pointer_area_size = self.gen.return_pointer_area_size.max(size); // self.gen.return_pointer_area_align = self.gen.return_pointer_area_align.max(align); // uwriteln!( - // self.gen.gen.c_src.src, + // self.src, // "int32_t ptr{tmp} = int32_t(&RET_AREA);" // ); // } @@ -1857,11 +1932,16 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } fn push_block(&mut self) { - uwriteln!(self.gen.gen.c_src.src, "// push_block()"); + let prev = core::mem::take(&mut self.src); + self.block_storage.push(prev); + // uwriteln!(self.src, "// push_block()"); } - fn finish_block(&mut self, _operand: &mut Vec) { - uwriteln!(self.gen.gen.c_src.src, "// finish_block()"); + fn finish_block(&mut self, operands: &mut Vec) { + 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 { From 7184e362f9d07e2afbab578966a272da074873bc Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 1 Dec 2023 00:56:35 +0100 Subject: [PATCH 015/672] proper namespace --- crates/cpp/src/lib.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 29263690a..e36868efa 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1851,13 +1851,20 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let (mut namespace, func_name_h) = self.gen.func_namespace_name(func); if matches!(func.kind, FunctionKind::Method(_)) { let this = operands.remove(0); - self.gen.gen.c_src.qualify(&namespace); - uwrite!(self.src, "lookup_resource({this})->"); + //self.gen.gen.c_src.qualify(&namespace); + let mut relative = SourceWithState::default(); + // relative.namespace = self.namespace.clone(); + relative.qualify(&namespace); + uwrite!(self.src, "{}lookup_resource({this})->", relative.src.to_string()); } else { if matches!(func.kind, FunctionKind::Constructor(_)) { let _ = namespace.pop(); } - self.gen.gen.c_src.qualify(&namespace); + 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); self.push_str("("); From 3edcac736486c95093b00d72bfb251212ffbe7ae Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Dec 2023 12:51:05 +0100 Subject: [PATCH 016/672] remove warnings --- crates/cpp/src/lib.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index e36868efa..7017dbfc1 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -129,8 +129,8 @@ impl Cpp { // public_anonymous_types: BTreeSet::new(), in_import, // export_funcs: Vec::new(), - return_pointer_area_size: 0, - return_pointer_area_align: 0, + // return_pointer_area_size: 0, + // return_pointer_area_align: 0, wasm_import_module, } } @@ -552,8 +552,8 @@ struct CppInterfaceGenerator<'a> { _name: &'a Option<&'a WorldKey>, sizes: SizeAlign, in_import: bool, - return_pointer_area_size: usize, - return_pointer_area_align: usize, + // return_pointer_area_size: usize, + // return_pointer_area_align: usize, pub wasm_import_module: Option, } @@ -1983,14 +1983,14 @@ fn is_drop_method(func: &Function) -> bool { matches!(func.kind, FunctionKind::Static(_)) && func.name.starts_with("[resource-drop]") } -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), - } -} +// 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), +// } +// } From bde2089262ffd42e16131c420e0b0c24d244324c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Dec 2023 15:07:08 +0100 Subject: [PATCH 017/672] guest wasi code no longer crashes --- crates/cpp/src/lib.rs | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7017dbfc1..bbbad1522 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1797,11 +1797,21 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::ResultLower { result: _, ty: _, - results: _, - } => self.push_str("ResultLower"), + results: result_types, + } => { + let err = self.blocks.pop().unwrap().0; + let ok = self.blocks.pop().unwrap().0; + self.let_results(result_types.len(), results); + let operand = &operands[0]; + self.push_str(&format!( + "{operand}.has_value() + ? ({ok}) + : ({err});" + )); + } abi::Instruction::ResultLift { result, ty: _ } => { - let mut err = String::default(); //self.blocks.pop().unwrap(); - let mut ok = String::default(); //self.blocks.pop().unwrap(); + let mut err = self.blocks.pop().unwrap().0; + let mut ok = self.blocks.pop().unwrap().0; if result.ok.is_none() { ok.clear(); } else { @@ -1855,7 +1865,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let mut relative = SourceWithState::default(); // relative.namespace = self.namespace.clone(); relative.qualify(&namespace); - uwrite!(self.src, "{}lookup_resource({this})->", relative.src.to_string()); + uwrite!( + self.src, + "{}lookup_resource({this})->", + relative.src.to_string() + ); } else { if matches!(func.kind, FunctionKind::Constructor(_)) { let _ = namespace.pop(); @@ -1864,7 +1878,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // relative.namespace = self.namespace.clone(); relative.qualify(&namespace); self.push_str(&relative.src); - // self.gen.gen.c_src.qualify(&namespace); + // self.gen.gen.c_src.qualify(&namespace); } self.src.push_str(&func_name_h); self.push_str("("); From e600adc22ff8518bd5c720327e532f8aec320459 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Dec 2023 15:45:35 +0100 Subject: [PATCH 018/672] mask a crash for now --- crates/cpp/src/wamr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/src/wamr.rs b/crates/cpp/src/wamr.rs index f2257ed63..804ccc455 100644 --- a/crates/cpp/src/wamr.rs +++ b/crates/cpp/src/wamr.rs @@ -39,7 +39,7 @@ fn push_wamr(ty: &Type, resolve: &Resolve, params_str: &mut String) { Type::Id(id) => match &resolve.types[*id].kind { TypeDefKind::Type(t) => push_wamr(t, resolve, params_str), TypeDefKind::Record(_r) => { - todo!(); + params_str.push_str("R"); } TypeDefKind::Flags(_) => params_str.push_str("L"), TypeDefKind::Tuple(_) => params_str.push_str("T"), From 72cb9ca8d48cdcf44885c106b9ed148699abc736 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Dec 2023 15:50:01 +0100 Subject: [PATCH 019/672] improve readability by using wildcards --- crates/cpp/src/lib.rs | 92 +++++++++++-------------------------------- 1 file changed, 24 insertions(+), 68 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index bbbad1522..dce9048c3 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1411,7 +1411,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } abi::Instruction::I32Const { val } => results.push(format!("(int32_t({}))", val)), - abi::Instruction::Bitcasts { casts: _ } => todo!(), + abi::Instruction::Bitcasts { .. } => todo!(), abi::Instruction::ConstZero { tys } => { for ty in tys.iter() { match ty { @@ -1477,10 +1477,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::Float32FromF32 => top_as("float"), abi::Instruction::Float64FromF64 => top_as("double"), abi::Instruction::BoolFromI32 => top_as("bool"), - abi::Instruction::ListCanonLower { - element: _, - realloc: _, - } => { + abi::Instruction::ListCanonLower { .. } => { results.push("ListCanonLower.addr".into()); results.push("ListCanonLower.len".into()); } @@ -1503,14 +1500,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } results.push(len); } - abi::Instruction::ListLower { - element: _, - realloc: _, - } => { + abi::Instruction::ListLower { .. } => { results.push("ListLower1".into()); results.push("ListLower2".into()); } - abi::Instruction::ListCanonLift { element: _, ty: _ } => { + abi::Instruction::ListCanonLift { .. } => { let tmp = self.tmp(); let len = format!("len{}", tmp); self.push_str(&format!("let {} = {};\n", len, operands[1])); @@ -1524,7 +1518,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let result = format!("std::string((char const*)({}), {len})", operands[0]); results.push(result); } - abi::Instruction::ListLift { element, ty: _ } => { + abi::Instruction::ListLift { element, .. } => { // let body = self.blocks.pop().unwrap(); let tmp = self.tmp(); let size = self.gen.sizes.size(element); @@ -1557,7 +1551,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // rt = self.gen.gen.runtime_path(), // )); } - abi::Instruction::IterElem { element: _ } => results.push("IterElem".to_string()), + abi::Instruction::IterElem { .. } => results.push("IterElem".to_string()), abi::Instruction::IterBasePointer => results.push("base".to_string()), abi::Instruction::RecordLower { record, .. } => { let op = &operands[0]; @@ -1565,11 +1559,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { results.push(format!("({}).{}", op, to_c_ident(&f.name))); } } - abi::Instruction::RecordLift { - record, - name: _, - ty, - } => { + abi::Instruction::RecordLift { record, ty, .. } => { let mut result = self.typename_lift(*ty); result.push_str("{"); for (_field, val) in record.fields.iter().zip(operands) { @@ -1599,19 +1589,15 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let op = &operands[0]; results.push(format!("{op}.get_handle()")); } - abi::Instruction::HandleLift { - handle: _, - name: _, - ty: _, - } => { + abi::Instruction::HandleLift { .. } => { let op = &operands[0]; results.push(op.clone()); } - abi::Instruction::TupleLower { tuple: _, ty: _ } => { + abi::Instruction::TupleLower { .. } => { results.push("TupleLower1".into()); results.push("TupleLower2".into()); } - abi::Instruction::TupleLift { tuple, ty: _ } => { + abi::Instruction::TupleLift { tuple, .. } => { let name = format!("tuple{}", self.tmp()); uwrite!(self.src, "auto {name} std::tuple<"); self.src.push_str( @@ -1627,22 +1613,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src.push_str(");\n"); results.push(name); } - abi::Instruction::FlagsLower { - flags, - name: _, - ty: _, - } => { + abi::Instruction::FlagsLower { flags, .. } => { let tmp = self.tmp(); self.push_str(&format!("auto flags{} = {};\n", tmp, operands[0])); for i in 0..flags.repr().count() { results.push(format!("((flags{} >> {})&1)!=0", tmp, i * 32)); } } - abi::Instruction::FlagsLift { - flags: _, - name: _, - ty: _, - } => results.push("FlagsLift".to_string()), + abi::Instruction::FlagsLift { .. } => results.push("FlagsLift".to_string()), abi::Instruction::VariantPayloadName => { let name = format!("payload{}", self.tmp()); results.push(format!("*{}", name)); @@ -1650,9 +1628,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } abi::Instruction::VariantLower { variant, - name: _, - ty: _, results: result_types, + .. } => { //let name = self.gen.type_name(*ty); // let op0 = &operands[0]; @@ -1706,11 +1683,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } self.src.push_str("}\n"); } - abi::Instruction::VariantLift { - variant, - name: _, - ty, - } => { + abi::Instruction::VariantLift { variant, ty, .. } => { let mut result = String::new(); result.push_str("{"); @@ -1768,25 +1741,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { result.push_str("}"); results.push(result); } - abi::Instruction::EnumLower { - enum_: _, - name: _, - ty: _, - } => results.push(format!("int32_t({})", operands[0])), - abi::Instruction::EnumLift { - enum_: _, - name: _, - ty, - } => { + 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); results.push(format!("({typename}){}", &operands[0])); } - abi::Instruction::OptionLower { - payload: _, - ty: _, - results: _, - } => self.push_str("OptionLower"), - abi::Instruction::OptionLift { payload, ty: _ } => { + abi::Instruction::OptionLower { .. } => self.push_str("OptionLower"), + abi::Instruction::OptionLift { payload, .. } => { let mut result: String = "std::optional<".into(); result.push_str(&self.gen.type_name(*payload, &self.namespace)); result.push_str(">("); @@ -1795,9 +1756,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { results.push(result); } abi::Instruction::ResultLower { - result: _, - ty: _, results: result_types, + .. } => { let err = self.blocks.pop().unwrap().0; let ok = self.blocks.pop().unwrap().0; @@ -1809,7 +1769,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { : ({err});" )); } - abi::Instruction::ResultLift { result, ty: _ } => { + abi::Instruction::ResultLift { result, .. } => { let mut err = self.blocks.pop().unwrap().0; let mut ok = self.blocks.pop().unwrap().0; if result.ok.is_none() { @@ -1903,15 +1863,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { _ => todo!(), } } - abi::Instruction::Malloc { - realloc: _, - size: _, - align: _, - } => todo!(), - abi::Instruction::GuestDeallocate { size: _, align: _ } => todo!(), + abi::Instruction::Malloc { .. } => todo!(), + abi::Instruction::GuestDeallocate { .. } => todo!(), abi::Instruction::GuestDeallocateString => todo!(), - abi::Instruction::GuestDeallocateList { element: _ } => todo!(), - abi::Instruction::GuestDeallocateVariant { blocks: _ } => todo!(), + abi::Instruction::GuestDeallocateList { .. } => todo!(), + abi::Instruction::GuestDeallocateVariant { .. } => todo!(), } } From e88ea0a1664659f09c672c4a08c78c14e85ba3a6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Dec 2023 16:00:17 +0100 Subject: [PATCH 020/672] simultaneously allow imported and exported resources --- crates/cpp/src/lib.rs | 63 ++++++++++++++++++++++++------------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index dce9048c3..a5c46f764 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -19,7 +19,8 @@ use wit_bindgen_core::{ mod wamr; -pub const RESOURCE_BASE_CLASS_NAME: &str = "ResourceBase"; +pub const RESOURCE_IMPORT_BASE_CLASS_NAME: &str = "ResourceImportBase"; +pub const RESOURCE_EXPORT_BASE_CLASS_NAME: &str = "ResourceExportBase"; pub const OWNED_CLASS_NAME: &str = "Owned"; type CppType = String; @@ -48,7 +49,8 @@ struct Includes { needs_optional: bool, needs_cstring: bool, needs_guest_alloc: bool, - needs_resources: bool, + needs_imported_resources: bool, + needs_exported_resources: bool, } #[derive(Clone)] @@ -303,10 +305,10 @@ impl WorldGenerator for Cpp { if self.dependencies.needs_cstring { self.include(""); } - if !self.opts.host && self.dependencies.needs_resources { + if !self.opts.host && self.dependencies.needs_imported_resources { self.include(""); } - if self.opts.host && self.dependencies.needs_resources { + if self.opts.host && self.dependencies.needs_exported_resources { self.include(""); } @@ -340,15 +342,14 @@ impl WorldGenerator for Cpp { } } - if self.dependencies.needs_resources { + if self.dependencies.needs_exported_resources { let namespace = namespace(resolve, &TypeOwner::World(world_id)); h_str.change_namespace(&namespace); // this is export, not host - if self.opts.host { - uwriteln!( - h_str.src, - "template - class {RESOURCE_BASE_CLASS_NAME} {{ + uwriteln!( + h_str.src, + "template + class {RESOURCE_EXPORT_BASE_CLASS_NAME} {{ static std::map resources; public: static R* lookup_resource(int32_t id) {{ @@ -368,23 +369,26 @@ impl WorldGenerator for Cpp { template struct {OWNED_CLASS_NAME} {{ T *ptr; }};" - ); - } else { - // somehow spaces get removed, newlines remain (problem occurs before const&) - // TODO: should into_handle become && ??? - uwriteln!( + ); + } + if self.dependencies.needs_imported_resources { + // somehow spaces get removed, newlines remain (problem occurs before const&) + // TODO: should into_handle become && ??? + let namespace = namespace(resolve, &TypeOwner::World(world_id)); + h_str.change_namespace(&namespace); + uwriteln!( h_str.src, - "class {RESOURCE_BASE_CLASS_NAME} {{ + "class {RESOURCE_IMPORT_BASE_CLASS_NAME} {{ static const int32_t invalid = -1; protected: int32_t handle; public: - {RESOURCE_BASE_CLASS_NAME}(int32_t h=invalid) : handle(h) {{}} - {RESOURCE_BASE_CLASS_NAME}({RESOURCE_BASE_CLASS_NAME}&&r) + {RESOURCE_IMPORT_BASE_CLASS_NAME}(int32_t h=invalid) : handle(h) {{}} + {RESOURCE_IMPORT_BASE_CLASS_NAME}({RESOURCE_IMPORT_BASE_CLASS_NAME}&&r) : handle(r.handle) {{ r.handle=invalid; }} - {RESOURCE_BASE_CLASS_NAME}({RESOURCE_BASE_CLASS_NAME} + {RESOURCE_IMPORT_BASE_CLASS_NAME}({RESOURCE_IMPORT_BASE_CLASS_NAME} const&) = delete; void set_handle(int32_t h) {{ handle=h; }} int32_t get_handle() const {{ return handle; }} @@ -393,17 +397,16 @@ impl WorldGenerator for Cpp { handle= invalid; return h; }} - {RESOURCE_BASE_CLASS_NAME}& operator=({RESOURCE_BASE_CLASS_NAME}&&r) {{ + {RESOURCE_IMPORT_BASE_CLASS_NAME}& operator=({RESOURCE_IMPORT_BASE_CLASS_NAME}&&r) {{ assert(handle<0); handle= r.handle; r.handle= invalid; return *this; }} - {RESOURCE_BASE_CLASS_NAME}& operator=({RESOURCE_BASE_CLASS_NAME} + {RESOURCE_IMPORT_BASE_CLASS_NAME}& operator=({RESOURCE_IMPORT_BASE_CLASS_NAME} const&r) = delete; }};" ); - } } h_str.change_namespace(&Vec::default()); @@ -1115,18 +1118,22 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } self.gen.h_src.change_namespace(&namespc); - self.gen.dependencies.needs_resources = true; + if import { + self.gen.dependencies.needs_imported_resources = true; + } else { + self.gen.dependencies.needs_exported_resources = true; + } if !import { - uwriteln!(self.gen.c_src.src, "template std::map {world_name}{RESOURCE_BASE_CLASS_NAME}::resources;"); + uwriteln!(self.gen.c_src.src, "template std::map {world_name}{RESOURCE_EXPORT_BASE_CLASS_NAME}::resources;"); } let base_type = if !import { - format!("<{pascal}>") + format!("{RESOURCE_EXPORT_BASE_CLASS_NAME}<{pascal}>") } else { - String::default() + RESOURCE_IMPORT_BASE_CLASS_NAME.into() }; - let derive = format!(" : public {world_name}{RESOURCE_BASE_CLASS_NAME}{base_type}"); + let derive = format!(" : public {world_name}{base_type}"); uwriteln!(self.gen.h_src.src, "class {pascal}{derive} {{\n"); uwriteln!(self.gen.h_src.src, "public:\n"); // destructor @@ -1150,7 +1157,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> // consuming constructor from handle (bindings) uwriteln!( self.gen.h_src.src, - "{pascal}({world_name}{RESOURCE_BASE_CLASS_NAME}&&);\n" + "{pascal}({world_name}{RESOURCE_IMPORT_BASE_CLASS_NAME}&&);\n" ); uwriteln!(self.gen.h_src.src, "{pascal}({pascal}&&) = default;\n"); } From 60f8211415e4810acdfc5502514446b1afc9c91d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Dec 2023 16:19:42 +0100 Subject: [PATCH 021/672] improve docs --- crates/cpp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index a5c46f764..3d56efe25 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -86,7 +86,7 @@ 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 + /// Generate code for directly linking to guest code (WIP) #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))] pub short_cut: bool, /// Call clang-format on the generated code From 26ab9040eed7c22db5315d878a4ba2e73e0b8092 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Dec 2023 16:53:01 +0100 Subject: [PATCH 022/672] properly restrict member functions --- crates/cpp/src/lib.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 3d56efe25..77f7061ff 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1150,7 +1150,14 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } let funcs = self.resolve.interfaces[intf].functions.values(); for func in funcs { - self.generate_guest_import(func, intf); + if match &func.kind { + FunctionKind::Freestanding => false, + FunctionKind::Method(mid) => *mid == id, + FunctionKind::Static(mid) => *mid == id, + FunctionKind::Constructor(mid) => *mid == id, + } { + self.generate_guest_import(func, intf); + } } if import { From fee8d973590fd6ec0540d91fe56256a6c08acb62 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Dec 2023 17:19:43 +0100 Subject: [PATCH 023/672] properly handle type namespaces --- crates/cpp/src/lib.rs | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 77f7061ff..a1bf8a896 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -901,6 +901,19 @@ impl CppInterfaceGenerator<'_> { } } + fn scoped_type_name(&self, id: TypeId, from_namespace: &Vec) -> String { + let ty = &self.resolve.types[id]; + let namespc = namespace(self.resolve, &ty.owner); + 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 { match ty { Type::Bool => "bool".into(), @@ -920,19 +933,8 @@ impl CppInterfaceGenerator<'_> { "std::string".into() } Type::Id(id) => match &self.resolve.types[*id].kind { - TypeDefKind::Record(_r) => { - let ty = &self.resolve.types[*id]; - let namespc = namespace(self.resolve, &ty.owner); - 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() - ) - } - TypeDefKind::Resource => self.resolve.types[*id].name.as_ref().cloned().unwrap(), + TypeDefKind::Record(_r) => self.scoped_type_name(*id, from_namespace), + TypeDefKind::Resource => self.scoped_type_name(*id, from_namespace), TypeDefKind::Handle(Handle::Own(id)) => { self.type_name(&Type::Id(*id), from_namespace) } @@ -957,22 +959,13 @@ impl CppInterfaceGenerator<'_> { result += ">"; result } - TypeDefKind::Enum(_e) => { - let ty = &self.resolve.types[*id]; - let namespc = namespace(self.resolve, &ty.owner); - 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() - ) - } + TypeDefKind::Enum(_e) => self.scoped_type_name(*id, from_namespace), TypeDefKind::Option(o) => { + self.gen.dependencies.needs_optional = true; "std::optional<".to_string() + &self.type_name(o, from_namespace) + ">" } TypeDefKind::Result(r) => { + self.gen.dependencies.needs_expected = true; "std::expected<".to_string() + &r.ok .as_ref() From c421712bbb48c32177eb6f3e6212b7a1a9e5acb9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Dec 2023 17:34:48 +0100 Subject: [PATCH 024/672] factor out optional_type --- crates/cpp/src/lib.rs | 51 ++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index a1bf8a896..639a74317 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -365,7 +365,7 @@ impl WorldGenerator for Cpp { static void remove_resource(int32_t id) {{ resources.erase(id); }} - }}; + }}; template struct {OWNED_CLASS_NAME} {{ T *ptr; }};" @@ -384,11 +384,11 @@ impl WorldGenerator for Cpp { int32_t handle; public: {RESOURCE_IMPORT_BASE_CLASS_NAME}(int32_t h=invalid) : handle(h) {{}} - {RESOURCE_IMPORT_BASE_CLASS_NAME}({RESOURCE_IMPORT_BASE_CLASS_NAME}&&r) - : handle(r.handle) {{ - r.handle=invalid; + {RESOURCE_IMPORT_BASE_CLASS_NAME}({RESOURCE_IMPORT_BASE_CLASS_NAME}&&r) + : handle(r.handle) {{ + r.handle=invalid; }} - {RESOURCE_IMPORT_BASE_CLASS_NAME}({RESOURCE_IMPORT_BASE_CLASS_NAME} + {RESOURCE_IMPORT_BASE_CLASS_NAME}({RESOURCE_IMPORT_BASE_CLASS_NAME} const&) = delete; void set_handle(int32_t h) {{ handle=h; }} int32_t get_handle() const {{ return handle; }} @@ -403,7 +403,7 @@ impl WorldGenerator for Cpp { r.handle= invalid; return *this; }} - {RESOURCE_IMPORT_BASE_CLASS_NAME}& operator=({RESOURCE_IMPORT_BASE_CLASS_NAME} + {RESOURCE_IMPORT_BASE_CLASS_NAME}& operator=({RESOURCE_IMPORT_BASE_CLASS_NAME} const&r) = delete; }};" ); @@ -894,10 +894,11 @@ impl CppInterfaceGenerator<'_> { .to_upper_camel_case() } - fn print_optional_ty(&mut self, ty: Option<&Type>, out: &mut String) { + // in C this is print_optional_ty + fn optional_type_name(&mut self, ty: Option<&Type>, from_namespace: &Vec) -> String { match ty { - Some(ty) => self.push_ty_name(ty, out), - None => out.push_str("void"), + Some(ty) => self.type_name(ty, from_namespace), + None => "void".into(), } } @@ -948,10 +949,7 @@ impl CppInterfaceGenerator<'_> { TypeDefKind::Variant(v) => { let mut result = "std::variant<".to_string(); for (n, case) in v.cases.iter().enumerate() { - result += &case - .ty - .as_ref() - .map_or("void".to_string(), |ty| self.type_name(ty, from_namespace)); + result += &self.optional_type_name(case.ty.as_ref(), from_namespace); if n + 1 != v.cases.len() { result += ", "; } @@ -967,13 +965,9 @@ impl CppInterfaceGenerator<'_> { TypeDefKind::Result(r) => { self.gen.dependencies.needs_expected = true; "std::expected<".to_string() - + &r.ok - .as_ref() - .map_or("void".into(), |t| self.type_name(t, from_namespace)) + + &self.optional_type_name(r.ok.as_ref(), from_namespace) + ", " - + &r.err - .as_ref() - .map_or("void".into(), |t| self.type_name(t, from_namespace)) + + &self.optional_type_name(r.err.as_ref(), from_namespace) + ">" } TypeDefKind::List(ty) => { @@ -988,10 +982,6 @@ impl CppInterfaceGenerator<'_> { } } - fn push_ty_name(&mut self, ty: &Type, out: &mut String) { - wit_bindgen_c::push_ty_name(self.resolve, ty, out); - } - fn make_export_name(input: &str) -> String { input .chars() @@ -1104,7 +1094,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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 + r#"/* User class definition file, autogenerated once, then user modified * Updated versions of this file are generated into {user_filename}.template. */"# ); @@ -1771,7 +1761,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.let_results(result_types.len(), results); let operand = &operands[0]; self.push_str(&format!( - "{operand}.has_value() + "{operand}.has_value() ? ({ok}) : ({err});" )); @@ -1789,11 +1779,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } else { err = format!("std::move({err})"); } - let mut ok_type = String::default(); - self.gen.print_optional_ty(result.ok.as_ref(), &mut ok_type); - let mut err_type = String::default(); - self.gen - .print_optional_ty(result.err.as_ref(), &mut err_type); + let ok_type = self + .gen + .optional_type_name(result.ok.as_ref(), &self.namespace); + let err_type = self + .gen + .optional_type_name(result.err.as_ref(), &self.namespace); let type_name = format!("std::expected<{ok_type}, {err_type}>",); let err_type = "std::unexpected"; let operand = &operands[0]; From aa35773cb1b8ebaef00fe3ae5f0467ef518afb8e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 3 Dec 2023 10:39:41 +0100 Subject: [PATCH 025/672] correct inclusion logic --- crates/cpp/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 639a74317..7b1961af4 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -305,10 +305,10 @@ impl WorldGenerator for Cpp { if self.dependencies.needs_cstring { self.include(""); } - if !self.opts.host && self.dependencies.needs_imported_resources { + if self.dependencies.needs_imported_resources { self.include(""); } - if self.opts.host && self.dependencies.needs_exported_resources { + if self.dependencies.needs_exported_resources { self.include(""); } From 34a2693d01903bbce6c9e92612c5f8b97a44867f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Dec 2023 18:09:21 +0100 Subject: [PATCH 026/672] initial exoort prototype --- crates/cpp/src/lib.rs | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7b1961af4..d4f93478f 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -209,7 +209,7 @@ impl WorldGenerator for Cpp { for (_name, func) in resolve.interfaces[id].functions.iter() { if matches!(func.kind, FunctionKind::Freestanding) { - gen.generate_guest_import(func, id); + gen.generate_guest_import(func, id, false); } } // gen.finish(); @@ -217,14 +217,25 @@ impl WorldGenerator for Cpp { fn export_interface( &mut self, - _resolve: &Resolve, + resolve: &Resolve, name: &WorldKey, - _iface: InterfaceId, + id: InterfaceId, _files: &mut Files, ) -> anyhow::Result<()> { self.h_src .src .push_str(&format!("// export_interface {name:?}\n")); + 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); + + for (_name, func) in resolve.interfaces[id].functions.iter() { + if matches!(func.kind, FunctionKind::Freestanding) { + gen.generate_guest_import(func, id, true); + } + } Ok(()) } @@ -788,10 +799,10 @@ impl CppInterfaceGenerator<'_> { } } - fn generate_guest_import(&mut self, func: &Function, interface: InterfaceId) { - let params = self.print_signature(func, !self.gen.opts.host); + fn generate_guest_import(&mut self, func: &Function, interface: InterfaceId, export: bool) { + let params = self.print_signature(func, !(self.gen.opts.host ^export)); self.gen.c_src.src.push_str("{\n"); - let lift_lower = if self.gen.opts.host { + let lift_lower = if export ^ self.gen.opts.host { LiftLower::LiftArgsLowerResults } else { LiftLower::LowerArgsLiftResults @@ -839,7 +850,7 @@ impl CppInterfaceGenerator<'_> { f.namespace = namespace; abi::call( f.gen.resolve, - AbiVariant::GuestImport, + if export {AbiVariant::GuestExport} else {AbiVariant::GuestImport}, lift_lower, func, &mut f, @@ -1129,7 +1140,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> results: Results::Named(vec![]), docs: Docs::default(), }; - self.generate_guest_import(&func, intf); + self.generate_guest_import(&func, intf, !import); } let funcs = self.resolve.interfaces[intf].functions.values(); for func in funcs { @@ -1139,7 +1150,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> FunctionKind::Static(mid) => *mid == id, FunctionKind::Constructor(mid) => *mid == id, } { - self.generate_guest_import(func, intf); + self.generate_guest_import(func, intf, !import); } } From e69ff9c40c8f82abb99e6d4dc2f03af77af6872e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 3 Dec 2023 14:22:53 +0100 Subject: [PATCH 027/672] proper namespacing for freestanding functions --- crates/cpp/src/lib.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index d4f93478f..ee4d96e15 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -206,9 +206,11 @@ impl WorldGenerator for Cpp { // if self.gen.interfaces_with_types_printed.insert(id) { gen.types(id); // } + let namespace = namespace(resolve, &TypeOwner::Interface(id)); for (_name, func) in resolve.interfaces[id].functions.iter() { if matches!(func.kind, FunctionKind::Freestanding) { + gen.gen.h_src.change_namespace(&namespace); gen.generate_guest_import(func, id, false); } } @@ -230,9 +232,11 @@ impl WorldGenerator for Cpp { 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)); for (_name, func) in resolve.interfaces[id].functions.iter() { if matches!(func.kind, FunctionKind::Freestanding) { + gen.gen.h_src.change_namespace(&namespace); gen.generate_guest_import(func, id, true); } } @@ -612,7 +616,9 @@ impl CppInterfaceGenerator<'_> { }) .unwrap_or(( Default::default(), - TypeOwner::World(self.gen.world_id.unwrap()), + self.interface + .map(|id| TypeOwner::Interface(id)) + .unwrap_or(TypeOwner::World(self.gen.world_id.unwrap())), )); let mut namespace = namespace(self.resolve, &owner); let is_drop = is_drop_method(func); @@ -800,7 +806,7 @@ impl CppInterfaceGenerator<'_> { } fn generate_guest_import(&mut self, func: &Function, interface: InterfaceId, export: bool) { - let params = self.print_signature(func, !(self.gen.opts.host ^export)); + let params = self.print_signature(func, !(self.gen.opts.host ^ export)); self.gen.c_src.src.push_str("{\n"); let lift_lower = if export ^ self.gen.opts.host { LiftLower::LiftArgsLowerResults @@ -850,7 +856,11 @@ impl CppInterfaceGenerator<'_> { f.namespace = namespace; abi::call( f.gen.resolve, - if export {AbiVariant::GuestExport} else {AbiVariant::GuestImport}, + if export { + AbiVariant::GuestExport + } else { + AbiVariant::GuestImport + }, lift_lower, func, &mut f, From 643ef72f49fb7732fedbdd8fc219739231c50417 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 7 Dec 2023 23:21:57 +0100 Subject: [PATCH 028/672] correct guest code exporting resources --- crates/cpp/src/lib.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index ee4d96e15..85561529f 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -649,9 +649,15 @@ impl CppInterfaceGenerator<'_> { } } else { // TODO perhaps remember better names for the arguments - self.resolve.wasm_signature(AbiVariant::GuestImport, func) + self.resolve.wasm_signature(AbiVariant::GuestExport, func) }; - self.gen.c_src.src.push_str("static "); + let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + if self.gen.opts.host { + self.gen.c_src.src.push_str("static "); + } else { + let func_name = &func.name; + uwriteln!(self.gen.c_src.src, r#"__attribute__((__export_name__("{module_name}#{func_name}")))"#); + } self.gen .c_src .src @@ -661,7 +667,6 @@ impl CppInterfaceGenerator<'_> { wasm_type(signature.results[0]) }); self.gen.c_src.src.push_str(" "); - let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); let export_name = CppInterfaceGenerator::export_name2(&module_name, &func.name); self.gen.c_src.src.push_str(&export_name); self.gen.c_src.src.push_str("("); From 922f53be5111863447dd80e62a5ad6c034f91c2a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 9 Dec 2023 16:32:15 +0100 Subject: [PATCH 029/672] fix result pointer (host) --- crates/cpp/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 85561529f..5d2bc01b6 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -684,6 +684,10 @@ impl CppInterfaceGenerator<'_> { self.gen.c_src.src.push_str(", "); } } + if signature.retptr { + self.gen.c_src.src.push_str(", int32_t resultptr"); + params.push("resultptr".into()); + } self.gen.c_src.src.push_str(")\n"); if self.gen.opts.host { let signature = wamr::wamr_signature(self.resolve, func); From 8128312c980017f5235a7c1950e528444bd9446b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 10 Dec 2023 09:53:17 +0100 Subject: [PATCH 030/672] we need a more elaborate variant implementation --- crates/cpp/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 5d2bc01b6..74444bcbf 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -51,6 +51,7 @@ struct Includes { needs_guest_alloc: bool, needs_imported_resources: bool, needs_exported_resources: bool, + needs_variant: bool, } #[derive(Clone)] @@ -326,6 +327,9 @@ impl WorldGenerator for Cpp { if self.dependencies.needs_exported_resources { self.include(""); } + if self.dependencies.needs_variant { + self.include(""); + } for include in self.includes.iter() { uwriteln!(h_str.src, "#include {include}"); @@ -977,6 +981,7 @@ impl CppInterfaceGenerator<'_> { TypeDefKind::Flags(_) => "Flags".to_string(), TypeDefKind::Tuple(_) => "Tuple".to_string(), TypeDefKind::Variant(v) => { + self.gen.dependencies.needs_variant = true; let mut result = "std::variant<".to_string(); for (n, case) in v.cases.iter().enumerate() { result += &self.optional_type_name(case.ty.as_ref(), from_namespace); From 93404a2a41af65192383e47abf396701acc2d7ab Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 10 Dec 2023 22:59:05 +0100 Subject: [PATCH 031/672] variant support (header) --- crates/cpp/src/lib.rs | 49 ++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 74444bcbf..1227d06c0 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -660,7 +660,10 @@ impl CppInterfaceGenerator<'_> { self.gen.c_src.src.push_str("static "); } else { let func_name = &func.name; - uwriteln!(self.gen.c_src.src, r#"__attribute__((__export_name__("{module_name}#{func_name}")))"#); + uwriteln!( + self.gen.c_src.src, + r#"__attribute__((__export_name__("{module_name}#{func_name}")))"# + ); } self.gen .c_src @@ -980,18 +983,7 @@ impl CppInterfaceGenerator<'_> { } TypeDefKind::Flags(_) => "Flags".to_string(), TypeDefKind::Tuple(_) => "Tuple".to_string(), - TypeDefKind::Variant(v) => { - self.gen.dependencies.needs_variant = true; - let mut result = "std::variant<".to_string(); - for (n, case) in v.cases.iter().enumerate() { - result += &self.optional_type_name(case.ty.as_ref(), from_namespace); - if n + 1 != v.cases.len() { - result += ", "; - } - } - result += ">"; - result - } + TypeDefKind::Variant(_v) => self.scoped_type_name(*id, from_namespace), TypeDefKind::Enum(_e) => self.scoped_type_name(*id, from_namespace), TypeDefKind::Option(o) => { self.gen.dependencies.needs_optional = true; @@ -1224,12 +1216,35 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> fn type_variant( &mut self, - _id: TypeId, + id: TypeId, name: &str, - _variant: &wit_bindgen_core::wit_parser::Variant, - _docs: &wit_bindgen_core::wit_parser::Docs, + variant: &wit_bindgen_core::wit_parser::Variant, + docs: &wit_bindgen_core::wit_parser::Docs, ) { - uwriteln!(self.gen.h_src.src, "// type_variant({name})"); + let ty = &self.resolve.types[id]; + let namespc = namespace(self.resolve, &ty.owner); + 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); + 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( From bce77602836a931543f5d4139984da66ad8808ef Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 11 Dec 2023 21:11:01 +0100 Subject: [PATCH 032/672] Flags support --- crates/cpp/src/lib.rs | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 1227d06c0..72eff164c 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -981,7 +981,7 @@ impl CppInterfaceGenerator<'_> { + &self.type_name(&Type::Id(*id), from_namespace) + ">" } - TypeDefKind::Flags(_) => "Flags".to_string(), + TypeDefKind::Flags(_f) => self.scoped_type_name(*id, from_namespace), TypeDefKind::Tuple(_) => "Tuple".to_string(), TypeDefKind::Variant(_v) => self.scoped_type_name(*id, from_namespace), TypeDefKind::Enum(_e) => self.scoped_type_name(*id, from_namespace), @@ -1064,7 +1064,7 @@ impl CppInterfaceGenerator<'_> { 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("/// "); src.push_str(line); src.push_str("\n"); } @@ -1196,12 +1196,30 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> fn type_flags( &mut self, - _id: TypeId, + id: TypeId, name: &str, - _flags: &wit_bindgen_core::wit_parser::Flags, - _docs: &wit_bindgen_core::wit_parser::Docs, + flags: &wit_bindgen_core::wit_parser::Flags, + docs: &wit_bindgen_core::wit_parser::Docs, ) { - uwriteln!(self.gen.h_src.src, "// type_flags({name})"); + let ty = &self.resolve.types[id]; + let namespc = namespace(self.resolve, &ty.owner); + 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} = (1<<{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( From a120dca6bb85a1db525b30b1c9d17cb206203abd Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 11 Dec 2023 21:25:46 +0100 Subject: [PATCH 033/672] tuple support --- crates/cpp/src/lib.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 72eff164c..66997ce53 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -52,6 +52,7 @@ struct Includes { needs_imported_resources: bool, needs_exported_resources: bool, needs_variant: bool, + needs_tuple: bool, } #[derive(Clone)] @@ -330,6 +331,9 @@ impl WorldGenerator for Cpp { if self.dependencies.needs_variant { self.include(""); } + if self.dependencies.needs_tuple { + self.include(""); + } for include in self.includes.iter() { uwriteln!(h_str.src, "#include {include}"); @@ -982,7 +986,16 @@ impl CppInterfaceGenerator<'_> { + ">" } TypeDefKind::Flags(_f) => self.scoped_type_name(*id, from_namespace), - TypeDefKind::Tuple(_) => "Tuple".to_string(), + 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) + }); + self.gen.dependencies.needs_tuple = true; + String::from("std::tuple<") + &types + ">" + } TypeDefKind::Variant(_v) => self.scoped_type_name(*id, from_namespace), TypeDefKind::Enum(_e) => self.scoped_type_name(*id, from_namespace), TypeDefKind::Option(o) => { From 295f42b571cc38b44e4c9d7c0832291c5ef11346 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 11 Dec 2023 22:02:47 +0100 Subject: [PATCH 034/672] signature, store and load corrections --- crates/cpp/src/lib.rs | 57 +++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 66997ce53..591ccc598 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -672,7 +672,7 @@ impl CppInterfaceGenerator<'_> { self.gen .c_src .src - .push_str(if signature.results.is_empty() { + .push_str(if signature.results.is_empty() || signature.retptr { "void" } else { wasm_type(signature.results[0]) @@ -681,22 +681,32 @@ impl CppInterfaceGenerator<'_> { let export_name = CppInterfaceGenerator::export_name2(&module_name, &func.name); 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, "); + 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(wasm_type(*ty)); self.gen.c_src.src.push_str(" "); self.gen.c_src.src.push_str(&name); params.push(name); - if n + 1 != signature.params.len() { - self.gen.c_src.src.push_str(", "); - } } if signature.retptr { - self.gen.c_src.src.push_str(", int32_t resultptr"); + if !first_arg { + self.gen.c_src.src.push_str(", "); + } + // else { + // first_arg = false; + // } + self.gen.c_src.src.push_str("int32_t resultptr"); params.push("resultptr".into()); } self.gen.c_src.src.push_str(")\n"); @@ -1419,7 +1429,11 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } fn load(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec) { - results.push(format!("*(({}*) ({} + {}))", ty, operands[0], offset)); + 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)); + } else { + results.push(format!("*(({}*) ({} + {}))", ty, operands[0], offset)); + } } fn load_ext(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec) { @@ -1429,14 +1443,25 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } fn store(&mut self, ty: &str, offset: i32, operands: &[String]) { - uwriteln!( - self.src, - "*(({}*)({} + {})) = {};", - ty, - operands[1], - offset, - operands[0] - ); + 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, + operands[0] + ); + } else { + uwriteln!( + self.src, + "*(({}*)({} + {})) = {};", + ty, + operands[1], + offset, + operands[0] + ); + } } fn has_resources(&self, id: &TypeId) -> bool { @@ -1922,7 +1947,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src.push_str(&func_name_h); self.push_str("("); self.push_str(&operands.join(", ")); - self.push_str(");"); + self.push_str(");\n"); } abi::Instruction::Return { amt, func } => { let import = !self.gen.gen.opts.host; From feda3f88d0c67ff8ddfda424c67ec2060cd95620 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 11 Dec 2023 23:05:03 +0100 Subject: [PATCH 035/672] correct import/export confusion --- crates/cpp/src/lib.rs | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 591ccc598..37e12bf88 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -213,7 +213,7 @@ impl WorldGenerator for Cpp { for (_name, func) in resolve.interfaces[id].functions.iter() { if matches!(func.kind, FunctionKind::Freestanding) { gen.gen.h_src.change_namespace(&namespace); - gen.generate_guest_import(func, id, false); + gen.generate_function(func, id, AbiVariant::GuestImport); } } // gen.finish(); @@ -239,7 +239,7 @@ impl WorldGenerator for Cpp { for (_name, func) in resolve.interfaces[id].functions.iter() { if matches!(func.kind, FunctionKind::Freestanding) { gen.gen.h_src.change_namespace(&namespace); - gen.generate_guest_import(func, id, true); + gen.generate_function(func, id, AbiVariant::GuestExport); } } Ok(()) @@ -835,10 +835,14 @@ impl CppInterfaceGenerator<'_> { } } - fn generate_guest_import(&mut self, func: &Function, interface: InterfaceId, export: bool) { - let params = self.print_signature(func, !(self.gen.opts.host ^ export)); + fn generate_function(&mut self, func: &Function, interface: InterfaceId, variant: AbiVariant) { + let export = match variant { + AbiVariant::GuestImport => self.gen.opts.host, + AbiVariant::GuestExport => !self.gen.opts.host, + }; + let params = self.print_signature(func, !export); self.gen.c_src.src.push_str("{\n"); - let lift_lower = if export ^ self.gen.opts.host { + let lift_lower = if export { LiftLower::LiftArgsLowerResults } else { LiftLower::LowerArgsLiftResults @@ -886,11 +890,7 @@ impl CppInterfaceGenerator<'_> { f.namespace = namespace; abi::call( f.gen.resolve, - if export { - AbiVariant::GuestExport - } else { - AbiVariant::GuestImport - }, + variant, lift_lower, func, &mut f, @@ -1130,14 +1130,15 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> ) { let type_ = &self.resolve.types[id]; if let TypeOwner::Interface(intf) = type_.owner { - let import = self.gen.imported_interfaces.contains(&intf) ^ self.gen.opts.host; + let guest_import = self.gen.imported_interfaces.contains(&intf); + let definition = !(guest_import ^ self.gen.opts.host); 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); let pascal = name.to_upper_camel_case(); let user_filename = namespc.join("-") + "-" + &pascal + ".h"; - if !import { + 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 @@ -1151,17 +1152,17 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } self.gen.h_src.change_namespace(&namespc); - if import { + if !definition { self.gen.dependencies.needs_imported_resources = true; } else { self.gen.dependencies.needs_exported_resources = true; } - if !import { + if definition { uwriteln!(self.gen.c_src.src, "template std::map {world_name}{RESOURCE_EXPORT_BASE_CLASS_NAME}::resources;"); } - let base_type = if !import { + let base_type = if definition { format!("{RESOURCE_EXPORT_BASE_CLASS_NAME}<{pascal}>") } else { RESOURCE_IMPORT_BASE_CLASS_NAME.into() @@ -1169,6 +1170,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> let derive = format!(" : public {world_name}{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 = "[resource-drop]".to_string() + &name; @@ -1179,7 +1181,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> results: Results::Named(vec![]), docs: Docs::default(), }; - self.generate_guest_import(&func, intf, !import); + self.generate_function(&func, intf, variant); } let funcs = self.resolve.interfaces[intf].functions.values(); for func in funcs { @@ -1189,11 +1191,11 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> FunctionKind::Static(mid) => *mid == id, FunctionKind::Constructor(mid) => *mid == id, } { - self.generate_guest_import(func, intf, !import); + self.generate_function(func, intf, variant); } } - if import { + if !definition { // consuming constructor from handle (bindings) uwriteln!( self.gen.h_src.src, @@ -1202,7 +1204,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> uwriteln!(self.gen.h_src.src, "{pascal}({pascal}&&) = default;\n"); } uwriteln!(self.gen.h_src.src, "}};\n"); - if !import { + 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); From 8b0d4ed3b3e30e6b06534117a490d3847ec8b146 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 12 Dec 2023 17:38:06 +0100 Subject: [PATCH 036/672] fix string handling on host --- crates/cpp/src/lib.rs | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 37e12bf88..2c93a66a1 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -363,6 +363,10 @@ impl WorldGenerator for Cpp { ); } } + if self.dependencies.needs_exported_resources { + let world_name = &self.world; + uwriteln!(c_str.src, "template std::map {world_name}::{RESOURCE_EXPORT_BASE_CLASS_NAME}::resources;"); + } } if self.dependencies.needs_exported_resources { @@ -888,13 +892,7 @@ impl CppInterfaceGenerator<'_> { }; let mut f = FunctionBindgen::new(self, params); f.namespace = namespace; - abi::call( - f.gen.resolve, - variant, - lift_lower, - func, - &mut f, - ); + abi::call(f.gen.resolve, variant, lift_lower, func, &mut f); let code = String::from(f.src); self.gen.c_src.src.push_str(&code); } @@ -1158,10 +1156,6 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> self.gen.dependencies.needs_exported_resources = true; } - if definition { - uwriteln!(self.gen.c_src.src, "template std::map {world_name}{RESOURCE_EXPORT_BASE_CLASS_NAME}::resources;"); - } - let base_type = if definition { format!("{RESOURCE_EXPORT_BASE_CLASS_NAME}<{pascal}>") } else { @@ -1170,7 +1164,11 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> let derive = format!(" : public {world_name}{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}; + let variant = if guest_import { + AbiVariant::GuestImport + } else { + AbiVariant::GuestExport + }; // destructor { let name = "[resource-drop]".to_string() + &name; @@ -1592,13 +1590,19 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let ptr = format!("ptr{}", tmp); let len = format!("len{}", tmp); let result = format!("result{}", tmp); - if realloc.is_none() { - self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); + self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); + if self.gen.gen.opts.host { + self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); + self.push_str(&format!("auto {} = {}.size();\n", len, val)); + } else { self.push_str(&format!("auto {} = (int32_t)({}.data());\n", ptr, val)); self.push_str(&format!("auto {} = (int32_t)({}.size());\n", len, val)); + } + if realloc.is_none() { results.push(ptr); } else { self.gen.gen.dependencies.needs_guest_alloc = true; + self.gen.gen.dependencies.needs_cstring = true; uwriteln!(self.src, "int32_t {result} = guest_alloc(exec_env, {len});"); uwriteln!(self.src, "memcpy(wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), {result}), {ptr}, {len});"); results.push(result); @@ -2046,15 +2050,6 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } -// fn wasm_type(ty: WasmType) -> &'static str { -// match ty { -// WasmType::I32 => "int32_t", -// WasmType::I64 => "int64_t", -// WasmType::F32 => "float", -// WasmType::F64 => "double", -// } -// } - fn is_drop_method(func: &Function) -> bool { matches!(func.kind, FunctionKind::Static(_)) && func.name.starts_with("[resource-drop]") } From 8b5c4959f6559527049c5634f0467d3fd49902a2 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 12 Dec 2023 17:56:06 +0100 Subject: [PATCH 037/672] correct list lifting --- crates/cpp/src/lib.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 2c93a66a1..e39e46b70 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -891,7 +891,9 @@ impl CppInterfaceGenerator<'_> { namespace }; let mut f = FunctionBindgen::new(self, params); - f.namespace = namespace; + if !export { + f.namespace = namespace; + } abi::call(f.gen.resolve, variant, lift_lower, func, &mut f); let code = String::from(f.src); self.gen.c_src.src.push_str(&code); @@ -1632,6 +1634,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let tmp = self.tmp(); let size = self.gen.sizes.size(element); let _align = self.gen.sizes.align(element); + let vtype = self.gen.type_name(element, &self.namespace); let len = format!("len{tmp}"); let base = format!("base{tmp}"); let result = format!("result{tmp}"); @@ -1644,7 +1647,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { operand1 = operands[1] )); self.push_str(&format!( - r#"auto mut {result} = std::vector<>(); + r#"auto {result} = std::vector<{vtype}>(); {result}.reserve({len}); "#, )); From 61b17aafc4403b8886e706fa3aed4b06ea38a5c7 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 12 Dec 2023 20:04:36 +0100 Subject: [PATCH 038/672] guest tests (9 pass 50 fail) --- crates/cpp/tests/codegen.rs | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/crates/cpp/tests/codegen.rs b/crates/cpp/tests/codegen.rs index 462870a0b..193952cb3 100644 --- a/crates/cpp/tests/codegen.rs +++ b/crates/cpp/tests/codegen.rs @@ -14,6 +14,7 @@ macro_rules! codegen_test { wit_bindgen_cpp::Opts::default() .build() .generate(resolve, world, files) + .unwrap() }, verify, ); @@ -21,19 +22,32 @@ macro_rules! codegen_test { }; } -test_helpers::codegen_tests!("*.wit"); +test_helpers::codegen_tests!(); fn verify(dir: &Path, name: &str) { - let path = PathBuf::from(env::var_os("WASI_SDK_PATH").unwrap()); - let mut cmd = Command::new(path.join("bin/clang++")); - cmd.arg(dir.join(format!("{}.cpp", name.to_snake_case()))); - cmd.arg("-I").arg(dir); - cmd.arg("-Wall") - .arg("-Wextra") - .arg("-Werror") - .arg("-Wno-unused-parameter"); - cmd.arg("-c"); - cmd.arg("-o").arg(dir.join("obj_host.o")); + 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 shared_args = vec![ + "--sysroot", + sysroot.to_str().unwrap(), + "-I", + dir.to_str().unwrap(), + "-Wall", + "-Wextra", + "-Werror", + "-Wno-unused-parameter", + "-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); } From 49f9411cf13f0045db055da1a127f83d027bb858 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 12 Dec 2023 20:40:01 +0100 Subject: [PATCH 039/672] also test host program --- crates/cpp/test_headers/expected | 6 + crates/cpp/test_headers/expected.hpp | 2444 +++++++++++++++++++++++++ crates/cpp/test_headers/wasm_export.h | 11 + crates/cpp/tests/codegen.rs | 48 +- 4 files changed, 2505 insertions(+), 4 deletions(-) create mode 100644 crates/cpp/test_headers/expected create mode 100644 crates/cpp/test_headers/expected.hpp create mode 100644 crates/cpp/test_headers/wasm_export.h diff --git a/crates/cpp/test_headers/expected b/crates/cpp/test_headers/expected new file mode 100644 index 000000000..66b06b147 --- /dev/null +++ b/crates/cpp/test_headers/expected @@ -0,0 +1,6 @@ +#include + +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 +// . +/// + +#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 +#include +#include +#include + +#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 +#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 +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign + +// 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 + +// 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 +struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible {}; +#ifdef _GLIBCXX_VECTOR +template +struct is_trivially_copy_constructible> : std::false_type {}; +#endif +} // namespace detail +} // namespace tl +#endif + +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#else +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#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 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 unexpected { +public: + static_assert(!std::is_same::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 ::value>::type * = nullptr> + constexpr explicit unexpected(Args &&...args) + : m_val(std::forward(args)...) {} + template < + class U, class... Args, + typename std::enable_if &, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(std::initializer_list l, Args &&...args) + : m_val(l, std::forward(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 unexpected(E) -> unexpected; +#endif + +template +constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() == rhs.value(); +} +template +constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() != rhs.value(); +} +template +constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() < rhs.value(); +} +template +constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() <= rhs.value(); +} +template +constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() > rhs.value(); +} +template +constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() >= rhs.value(); +} + +template +unexpected::type> make_unexpected(E &&e) { + return unexpected::type>(std::forward(e)); +} + +struct unexpect_t { + unexpect_t() = default; +}; +static constexpr unexpect_t unexpect{}; + +namespace detail { +template +[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward(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 using remove_const_t = typename std::remove_const::type; +template +using remove_reference_t = typename std::remove_reference::type; +template using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; + +// std::conjunction from C++17 +template struct conjunction : std::true_type {}; +template struct conjunction : B {}; +template +struct conjunction + : std::conditional, 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 +struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; + +template struct is_const_or_const_ref : std::false_type {}; +template struct is_const_or_const_ref : std::true_type {}; +template struct is_const_or_const_ref : 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::value && + is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, int = 0> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); +} + +template >::value>> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} + +// std::invoke_result from C++17 +template struct invoke_result_impl; + +template +struct invoke_result_impl< + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = + decltype(detail::invoke(std::declval(), std::declval()...)); +}; + +template +using invoke_result = invoke_result_impl; + +template +using invoke_result_t = typename invoke_result::type; + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template struct is_swappable : std::true_type {}; + +template 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 tag swap(T &, T &); +template 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 std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); + +template std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); + +template +struct is_std_swap_noexcept + : std::integral_constant::value && + std::is_nothrow_move_assignable::value> {}; + +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; + +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std( + 0))::value || + is_swappable::value)> {}; + +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; +#endif +#endif + +// Trait for checking if a type is a tl::expected +template struct is_expected_impl : std::false_type {}; +template +struct is_expected_impl> : std::true_type {}; +template using is_expected = is_expected_impl>; + +template +using expected_enable_forward_value = detail::enable_if_t< + std::is_constructible::value && + !std::is_same, in_place_t>::value && + !std::is_same, detail::decay_t>::value && + !std::is_same, detail::decay_t>::value>; + +template +using expected_enable_from_other = detail::enable_if_t< + std::is_constructible::value && + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; + +template +using is_void_or = conditional_t::value, std::true_type, U>; + +template +using is_copy_constructible_or_void = + is_void_or>; + +template +using is_move_constructible_or_void = + is_void_or>; + +template +using is_copy_assignable_or_void = is_void_or>; + +template +using is_move_assignable_or_void = is_void_or>; + +} // 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 ::value, + bool = std::is_trivially_destructible::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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } else { + m_unexpect.~unexpected(); + } + } + union { + T m_val; + unexpected 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 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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// T is trivial, E is not. +template struct expected_storage_base { + 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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// E is trivial, T is not. +template 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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is trivially-destructible +template struct expected_storage_base { + #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 ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + struct dummy {}; + union { + unexpected m_unexpect; + dummy m_val; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is not trivially-destructible +template struct expected_storage_base { + 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 ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + unexpected 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 +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct(Args &&...args) noexcept { + new (std::addressof(this->m_val)) T(std::forward(args)...); + this->m_has_val = true; + } + + template void construct_with(Rhs &&rhs) noexcept { + new (std::addressof(this->m_val)) T(std::forward(rhs).get()); + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(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 ::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + 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 ::value && + std::is_nothrow_move_constructible::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(); + 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 ::value && + !std::is_nothrow_move_constructible::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(); + +#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 ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); +#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(); + 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(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + +#endif + + // The common part of move/copy assigning + template void assign_common(Rhs &&rhs) { + if (this->m_has_val) { + if (rhs.m_has_val) { + get() = std::forward(rhs).get(); + } else { + destroy_val(); + construct_error(std::forward(rhs).geterr()); + } + } else { + if (!rhs.m_has_val) { + geterr() = std::forward(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 &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&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 +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template 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 void construct_with(Rhs &&) noexcept { + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + + template void assign(Rhs &&rhs) noexcept { + if (!this->m_has_val) { + if (rhs.m_has_val) { + geterr().~unexpected(); + construct(); + } else { + geterr() = std::forward(rhs).geterr(); + } + } else { + if (!rhs.m_has_val) { + construct_error(std::forward(rhs).geterr()); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&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 :: + value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; +}; + +// This specialization is for when T or E are not trivially copy constructible +template +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; + + expected_copy_base() = default; + expected_copy_base(const expected_copy_base &rhs) + : expected_operations_base(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 >::value + &&std::is_trivially_move_constructible::value> +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; +}; +#else +template struct expected_move_base; +#endif +template +struct expected_move_base : expected_copy_base { + using expected_copy_base::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::value) + : expected_copy_base(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 >::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 { + using expected_move_base::expected_move_base; +}; + +template +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::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 , + std::is_trivially_move_constructible, + std::is_trivially_move_assignable>>:: + value &&std::is_trivially_destructible::value + &&std::is_trivially_move_constructible::value + &&std::is_trivially_move_assignable::value> +struct expected_move_assign_base : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; +}; +#else +template struct expected_move_assign_base; +#endif + +template +struct expected_move_assign_base + : expected_copy_assign_base { + using expected_copy_assign_base::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::value + &&std::is_nothrow_move_assignable::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 ::value && + std::is_copy_constructible::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::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 +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 = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + 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 +struct expected_delete_ctor_base { + 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 ::value && + std::is_copy_constructible::value && + is_copy_assignable_or_void::value && + std::is_copy_assignable::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value && + is_move_assignable_or_void::value && + std::is_move_assignable::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 +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 = delete; +}; + +template +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 &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +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 &) = 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 ::value || std::is_void::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 struct expected_default_ctor_base { + 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 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` 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 expected : private detail::expected_move_assign_base, + private detail::expected_delete_ctor_base, + private detail::expected_delete_assign_base, + private detail::expected_default_ctor_base { + static_assert(!std::is_reference::value, "T must not be a reference"); + static_assert(!std::is_same::type>::value, + "T must not be in_place_t"); + static_assert(!std::is_same::type>::value, + "T must not be unexpect_t"); + static_assert( + !std::is_same>::type>::value, + "T must not be unexpected"); + static_assert(!std::is_reference::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 *errptr() { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { + return std::addressof(this->m_unexpect); + } + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &val() { + return this->m_val; + } + TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } + + template ::value> * = nullptr> + constexpr const U &val() const { + return this->m_val; + } + constexpr const unexpected &err() const { return this->m_unexpect; } + + using impl_base = detail::expected_move_assign_base; + using ctor_base = detail::expected_default_ctor_base; + +public: + typedef T value_type; + typedef E error_type; + typedef unexpected unexpected_type; + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { + return and_then_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + return and_then_impl(std::move(*this), std::forward(f)); + } + template constexpr auto and_then(F &&f) const & { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template constexpr auto and_then(F &&f) const && { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif + +#else + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) & -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) && -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + return or_else_impl(*this, std::forward(f)); + } + + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + return or_else_impl(std::move(*this), std::forward(f)); + } + + template expected constexpr or_else(F &&f) const & { + return or_else_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template expected constexpr or_else(F &&f) const && { + return or_else_impl(std::move(*this), std::forward(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 ::value> * = + nullptr> + constexpr expected(in_place_t, Args &&...args) + : impl_base(in_place, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected(in_place_t, std::initializer_list il, Args &&...args) + : impl_base(in_place, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit constexpr expected(const unexpected &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected const &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr> + constexpr explicit expected(unexpect_t, Args &&...args) + : impl_base(unexpect, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected(unexpect_t, std::initializer_list il, + Args &&...args) + : impl_base(unexpect, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(const expected &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::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&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::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(expected &&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::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + err().~unexpected(); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } + + return *this; + } + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; +#endif + } + + return *this; + } + + template ::value && + std::is_assignable::value> * = nullptr> + expected &operator=(const unexpected &rhs) { + if (!has_value()) { + err() = rhs; + } else { + this->destroy_val(); + ::new (errptr()) unexpected(rhs); + this->m_has_val = false; + } + + return *this; + } + + template ::value && + std::is_move_assignable::value> * = nullptr> + expected &operator=(unexpected &&rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } else { + this->destroy_val(); + ::new (errptr()) unexpected(std::move(rhs)); + this->m_has_val = false; + } + + return *this; + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + } else { + err().~unexpected(); + this->m_has_val = true; + } + ::new (valptr()) T(std::forward(args)...); + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + ::new (valptr()) T(std::forward(args)...); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; +#endif + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + err().~unexpected(); + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(il, std::forward(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::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::type{}, + typename std::is_nothrow_move_constructible::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 + detail::enable_if_t::value && + detail::is_swappable::value && + (std::is_nothrow_move_constructible::value || + std::is_nothrow_move_constructible::value)> + swap(expected &rhs) noexcept( + std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value + &&std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { + if (has_value() && rhs.has_value()) { + swap_where_both_have_value(rhs, typename std::is_void::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::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 ::value> * = nullptr> + constexpr const U &operator*() const & { + TL_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { + TL_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + constexpr const U &&operator*() const && { + TL_ASSERT(has_value()); + return std::move(val()); + } + template ::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 ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access(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 constexpr T value_or(U &&v) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy-constructible and convertible to from U&&"); + return bool(*this) ? **this : static_cast(std::forward(v)); + } + template TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move-constructible and convertible to from U&&"); + return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); + } +}; + +namespace detail { +template using exp_t = typename detail::decay_t::value_type; +template using err_t = typename detail::decay_t::error_type; +template using ret_t = expected>; + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#else +template struct TC; +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template ())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto or_else_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#else +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto or_else_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#endif +} // namespace detail + +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); +} +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : true); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() == rhs.error() : false); +} + +template +constexpr bool operator==(const expected &x, const U &v) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator==(const U &v, const expected &x) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator!=(const expected &x, const U &v) { + return x.has_value() ? *x != v : true; +} +template +constexpr bool operator!=(const U &v, const expected &x) { + return x.has_value() ? *x != v : true; +} + +template +constexpr bool operator==(const expected &x, const unexpected &e) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator==(const unexpected &e, const expected &x) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator!=(const expected &x, const unexpected &e) { + return x.has_value() ? true : x.error() != e.value(); +} +template +constexpr bool operator!=(const unexpected &e, const expected &x) { + return x.has_value() ? true : x.error() != e.value(); +} + +template ::value || + std::is_move_constructible::value) && + detail::is_swappable::value && + std::is_move_constructible::value && + detail::is_swappable::value> * = nullptr> +void swap(expected &lhs, + expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} +} // namespace tl + +#endif diff --git a/crates/cpp/test_headers/wasm_export.h b/crates/cpp/test_headers/wasm_export.h new file mode 100644 index 000000000..b5597de2a --- /dev/null +++ b/crates/cpp/test_headers/wasm_export.h @@ -0,0 +1,11 @@ +typedef void* wasm_exec_env_t; +typedef void* wasm_module_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); diff --git a/crates/cpp/tests/codegen.rs b/crates/cpp/tests/codegen.rs index 193952cb3..8937b9060 100644 --- a/crates/cpp/tests/codegen.rs +++ b/crates/cpp/tests/codegen.rs @@ -18,6 +18,16 @@ macro_rules! codegen_test { }, verify, ); + 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, + ); } }; } @@ -31,16 +41,20 @@ fn verify(dir: &Path, name: &str) { ); 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(), - "-Wall", - "-Wextra", - "-Werror", - "-Wno-unused-parameter", + "-I", + additional_includes.to_str().unwrap(), + // "-Wall", + // "-Wextra", + // "-Werror", + // "-Wno-unused-parameter", + "-std=c++2b", "-c", "-o", ]; @@ -51,3 +65,29 @@ fn verify(dir: &Path, name: &str) { 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); +} From c28479cd4cf2e608e14603dc9b44b40e2317f471 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 13 Dec 2023 21:02:09 +0100 Subject: [PATCH 040/672] tuple+return pointer support --- crates/cpp/src/lib.rs | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index e39e46b70..161d544d4 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1705,13 +1705,15 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let op = &operands[0]; results.push(op.clone()); } - abi::Instruction::TupleLower { .. } => { - results.push("TupleLower1".into()); - results.push("TupleLower2".into()); + 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<"); + uwrite!(self.src, "auto {name} = std::tuple<"); self.src.push_str( &(tuple .types @@ -1994,30 +1996,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 8 => "uint64_t", _ => todo!(), }; - uwriteln!(self.src, " {tp} ret_area[{elems}];"); - - uwrite!(self.src, "int32_t ptr{tmp} = int32_t(&ret_area);"); - - // Imports get a per-function return area to facilitate using the - // stack whereas exports use a per-module return area to cut down on - // stack usage. Note that for imports this also facilitates "adapter - // modules" for components to not have data segments. - // if self.gen.in_import { - // self.import_return_pointer_area_size = self.import_return_pointer_area_size.max(size); - // self.import_return_pointer_area_align = - // self.import_return_pointer_area_align.max(align); - // uwrite!( - // self.src, - // "int32_t ptr{tmp} = int32_t(&ret_area);" - // ); - // } else { - // self.gen.return_pointer_area_size = self.gen.return_pointer_area_size.max(size); - // self.gen.return_pointer_area_align = self.gen.return_pointer_area_align.max(align); - // uwriteln!( - // self.src, - // "int32_t ptr{tmp} = int32_t(&RET_AREA);" - // ); - // } + let static_var = if self.gen.in_import { ""}else {"static "}; + uwriteln!(self.src, "{static_var}{tp} ret_area[{elems}];"); + uwriteln!(self.src, "int32_t ptr{tmp} = int32_t(&ret_area);"); + format!("ptr{}", tmp) } From d871332b98127506648aa9e5b6a7a97dfbf8c62e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 13 Dec 2023 22:39:20 +0100 Subject: [PATCH 041/672] make 3/4 of the integer test pass --- crates/cpp/src/lib.rs | 5 +++-- crates/cpp/src/wamr.rs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 161d544d4..5431622aa 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -673,10 +673,11 @@ impl CppInterfaceGenerator<'_> { r#"__attribute__((__export_name__("{module_name}#{func_name}")))"# ); } + let return_via_pointer = signature.retptr && self.gen.opts.host; self.gen .c_src .src - .push_str(if signature.results.is_empty() || signature.retptr { + .push_str(if signature.results.is_empty() || return_via_pointer { "void" } else { wasm_type(signature.results[0]) @@ -703,7 +704,7 @@ impl CppInterfaceGenerator<'_> { self.gen.c_src.src.push_str(&name); params.push(name); } - if signature.retptr { + if return_via_pointer { if !first_arg { self.gen.c_src.src.push_str(", "); } diff --git a/crates/cpp/src/wamr.rs b/crates/cpp/src/wamr.rs index 804ccc455..7d5dea483 100644 --- a/crates/cpp/src/wamr.rs +++ b/crates/cpp/src/wamr.rs @@ -92,7 +92,7 @@ fn wamr_add_result(sig: &mut WamrSig, resolve: &Resolve, ty: &Type) { Type::Id(id) => match &resolve.types[*id].kind { TypeDefKind::Record(_r) => sig.wamr_types.push('R'), TypeDefKind::Flags(_) => todo!(), - TypeDefKind::Tuple(_) => todo!(), + TypeDefKind::Tuple(_) => sig.wamr_result.push('i'), TypeDefKind::Variant(_) => todo!(), TypeDefKind::Enum(_e) => { sig.wamr_types.push('*'); From 15bdafa68bc30805b5cb5fb5eee94c026eaf44a1 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 24 Jan 2024 23:44:43 +0100 Subject: [PATCH 042/672] document plan for C++ types --- Cargo.lock | 2 +- crates/cpp/DESIGN.md | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 crates/cpp/DESIGN.md diff --git a/Cargo.lock b/Cargo.lock index bf7d20b22..3ebad05ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2363,7 +2363,7 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder 0.38.0", + "wasm-encoder 0.39.0", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md new file mode 100644 index 000000000..7433390b9 --- /dev/null +++ b/crates/cpp/DESIGN.md @@ -0,0 +1,37 @@ +# 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 | + + +| Code | mode | WIT Type | Rust type | C++ Type | Lower | Reason | +| --- | --- | --- | --- | --- | --- | --- | +| GIA | v | string | &str | string_view (17) | addr, len | | +| | | list | &[T] | span (20) | addr, len | | +| | | tuple | (...) | std::tuple | 0, 1, ...| | +| | | tuple | (&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 | Result<&str, &[]> | std::expected | d,a,l | +| GIR | t | string | String | wasi::string | &(addr, len) | | +| | | list | Vec | wasi::vector | &(a,l) | +| | | result | Result | std::expected | &(d,a,l) | +| GEA | t | string | String | wasi::string&& | addr, len | +| | | result | Result | std::expected&& | d,a,l | +| GER | t | string | String | wasi::string (or std?) | -> &(a,l) cabi_post_N:P/I#F | +| | | result | Result | std::expected | -> &(d,a,l) cabi_post | +| G-S | ? | string | String | wasi::string | addr, len | + +Note: The host never frees memory (is never passed ownership)! From 1f69ecca829a0f1b65cd1d6d7c8e41d436670354 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 24 Jan 2024 23:52:02 +0100 Subject: [PATCH 043/672] host function logic --- crates/cpp/DESIGN.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md index 7433390b9..b5e3474ea 100644 --- a/crates/cpp/DESIGN.md +++ b/crates/cpp/DESIGN.md @@ -14,6 +14,7 @@ | --- | --- | | v | passed by value | | t | owernership transferred | +| p | cabi_post_ cleans up | | Code | mode | WIT Type | Rust type | C++ Type | Lower | Reason | @@ -30,8 +31,12 @@ | | | result | Result | std::expected | &(d,a,l) | | GEA | t | string | String | wasi::string&& | addr, len | | | | result | Result | std::expected&& | d,a,l | -| GER | t | string | String | wasi::string (or std?) | -> &(a,l) cabi_post_N:P/I#F | +| GER | p | string | String | wasi::string (or std?) | -> &(a,l) cabi_post_N:P/I#F | | | | result | Result | std::expected | -> &(d,a,l) cabi_post | -| G-S | ? | string | String | wasi::string | addr, len | +| --S | ? | string | String | wasi::string | addr, len | +| HIA | v | string | | string_view | a,l | +| HIR | t | string | | wasi::string | &(a,l) | +| HEA | t | string | | wasi::string&& | a,l | +| HER | p | string | | string_view + special cleanup | -> &(a,l) | Note: The host never frees memory (is never passed ownership)! From 089735483121837c72a7a49a58b8bce639db6c58 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 28 Jan 2024 00:14:16 +0100 Subject: [PATCH 044/672] host calling into guest (incomplete) --- Cargo.lock | 2 +- crates/cpp/src/lib.rs | 84 ++++++++++++++++++++++++++++++------- crates/cpp/tests/codegen.rs | 12 +++++- tests/other/wasm_export.h | 27 +++++++++++- 4 files changed, 106 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 546b84c1f..224283e56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2409,7 +2409,7 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder 0.39.0", + "wasm-encoder 0.40.0", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 5431622aa..3586235ab 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -299,7 +299,8 @@ impl WorldGenerator for Cpp { uwrite!( h_str.src, "#ifndef __CPP_HOST_BINDINGS_{0}_H - #define __CPP_HOST_BINDINGS_{0}_H\n", + #define __CPP_HOST_BINDINGS_{0}_H + struct WASMExecEnv; // WAMR execution environment\n", world.name.to_shouty_snake_case(), ); } @@ -350,7 +351,8 @@ impl WorldGenerator for Cpp { if !self.opts.short_cut { uwriteln!( c_str.src, - "#include // wasm-micro-runtime header" + "#include // wasm-micro-runtime header\n\ + #include " ); if c_str.src.len() > 0 { @@ -789,6 +791,12 @@ impl CppInterfaceGenerator<'_> { } self.gen.h_src.src.push_str(&cpp_sig.name); self.gen.h_src.src.push_str("("); + if import && self.gen.opts.host { + 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(", "); @@ -819,6 +827,12 @@ impl CppInterfaceGenerator<'_> { 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 { + 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()); } @@ -894,6 +908,7 @@ impl CppInterfaceGenerator<'_> { let mut f = FunctionBindgen::new(self, params); if !export { f.namespace = namespace; + f.wamr_signature = Some(wamr::wamr_signature(&f.gen.resolve, func)); } abi::call(f.gen.resolve, variant, lift_lower, func, &mut f); let code = String::from(f.src); @@ -1384,6 +1399,8 @@ struct FunctionBindgen<'a, 'b> { block_storage: Vec, blocks: Vec<(String, Vec)>, payloads: Vec, + // caching for wasm + wamr_signature: Option, } impl<'a, 'b> FunctionBindgen<'a, 'b> { @@ -1399,6 +1416,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { block_storage: Default::default(), blocks: Default::default(), payloads: Default::default(), + wamr_signature: None, } } @@ -1917,19 +1935,55 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { .as_ref() .map(|e| e.clone()) .unwrap(); - 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()); + 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 => todo!(), + WasmType::F32 => todo!(), + WasmType::F64 => todo!(), + } + } + 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 { + self.src + .push_str("assert(wasm_results[0].kind==WASM_I32);\n"); + self.src.push_str("auto ret = wasm_results[0].of.i32;\n"); + 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"); } - 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); @@ -1997,7 +2051,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 8 => "uint64_t", _ => todo!(), }; - let static_var = if self.gen.in_import { ""}else {"static "}; + let static_var = if self.gen.in_import { "" } else { "static " }; uwriteln!(self.src, "{static_var}{tp} ret_area[{elems}];"); uwriteln!(self.src, "int32_t ptr{tmp} = int32_t(&ret_area);"); diff --git a/crates/cpp/tests/codegen.rs b/crates/cpp/tests/codegen.rs index 8937b9060..99e6c6b7b 100644 --- a/crates/cpp/tests/codegen.rs +++ b/crates/cpp/tests/codegen.rs @@ -41,7 +41,11 @@ fn verify(dir: &Path, name: &str) { ); 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 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", @@ -69,7 +73,11 @@ fn verify(dir: &Path, name: &str) { 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 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", diff --git a/tests/other/wasm_export.h b/tests/other/wasm_export.h index b5597de2a..a3caacfa2 100644 --- a/tests/other/wasm_export.h +++ b/tests/other/wasm_export.h @@ -1,5 +1,26 @@ -typedef void* wasm_exec_env_t; +// minimal WAMR header mock-up for compilation tests +#include +struct WASMExecEnv; +typedef WASMExecEnv* wasm_exec_env_t; typedef void* wasm_module_inst_t; +typedef void* wasm_function_inst_t; +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; 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 { @@ -9,3 +30,7 @@ struct NativeSymbol { 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, wasm_val_t*, uint32_t, wasm_val_t*); +wasm_function_inst_t wasm_runtime_lookup_function(wasm_module_inst_t, const char*, const char*); +#define WASM_INIT_VAL {.kind = WASM_I32, .of = {.i32 = 0}} +#define WASM_I32_VAL(x) {.kind = WASM_I32, .of = {.i32 =(x)}} From 8814a119a18706968b922c7cc033999a39558e7c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 28 Jan 2024 00:20:51 +0100 Subject: [PATCH 045/672] support more result types --- crates/cpp/src/lib.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 3586235ab..1f908c4e7 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1964,9 +1964,15 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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 { - self.src - .push_str("assert(wasm_results[0].kind==WASM_I32);\n"); - self.src.push_str("auto ret = wasm_results[0].of.i32;\n"); + 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")), + 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 { From afc2f3d8d4aa13a7a65a1691e8ade44f1d0e8d7b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 28 Jan 2024 17:15:21 +0100 Subject: [PATCH 046/672] separate namespace for guest exports, fix integer test --- crates/cpp/src/lib.rs | 45 ++++++++++++++------------- crates/cpp/test_headers/wasm_export.h | 28 ++++++++++++++++- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 1f908c4e7..8a9b315f0 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -208,7 +208,7 @@ impl WorldGenerator for Cpp { // if self.gen.interfaces_with_types_printed.insert(id) { gen.types(id); // } - let namespace = namespace(resolve, &TypeOwner::Interface(id)); + let namespace = namespace(resolve, &TypeOwner::Interface(id), false); for (_name, func) in resolve.interfaces[id].functions.iter() { if matches!(func.kind, FunctionKind::Freestanding) { @@ -234,7 +234,7 @@ impl WorldGenerator for Cpp { 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)); + let namespace = namespace(resolve, &TypeOwner::Interface(id), true); for (_name, func) in resolve.interfaces[id].functions.iter() { if matches!(func.kind, FunctionKind::Freestanding) { @@ -372,7 +372,7 @@ impl WorldGenerator for Cpp { } if self.dependencies.needs_exported_resources { - let namespace = namespace(resolve, &TypeOwner::World(world_id)); + let namespace = namespace(resolve, &TypeOwner::World(world_id), false); h_str.change_namespace(&namespace); // this is export, not host uwriteln!( @@ -403,7 +403,7 @@ impl WorldGenerator for Cpp { if self.dependencies.needs_imported_resources { // somehow spaces get removed, newlines remain (problem occurs before const&) // TODO: should into_handle become && ??? - let namespace = namespace(resolve, &TypeOwner::World(world_id)); + let namespace = namespace(resolve, &TypeOwner::World(world_id), false); h_str.change_namespace(&namespace); uwriteln!( h_str.src, @@ -513,9 +513,10 @@ impl WorldGenerator for Cpp { } } -// determine namespace -fn namespace(resolve: &Resolve, owner: &TypeOwner) -> Vec { +// determine namespace (for the lifted C++ function) +fn namespace(resolve: &Resolve, owner: &TypeOwner, export: bool) -> Vec { let mut result = Vec::default(); + if export { result.push(String::from("exports")); } match owner { TypeOwner::World(w) => result.push(resolve.worlds[*w].name.to_snake_case()), TypeOwner::Interface(i) => { @@ -617,7 +618,7 @@ impl CppInterfaceGenerator<'_> { } } - fn func_namespace_name(&self, func: &Function) -> (Vec, String) { + fn func_namespace_name(&self, func: &Function, export: bool) -> (Vec, String) { let (object, owner) = match &func.kind { FunctionKind::Freestanding => None, FunctionKind::Method(i) => Some(i), @@ -634,7 +635,7 @@ impl CppInterfaceGenerator<'_> { .map(|id| TypeOwner::Interface(id)) .unwrap_or(TypeOwner::World(self.gen.world_id.unwrap())), )); - let mut namespace = namespace(self.resolve, &owner); + let mut namespace = namespace(self.resolve, &owner, export); let is_drop = is_drop_method(func); let func_name_h = if !matches!(&func.kind, FunctionKind::Freestanding) { namespace.push(object.clone()); @@ -741,7 +742,7 @@ impl CppInterfaceGenerator<'_> { ) -> HighlevelSignature { let mut res = HighlevelSignature::default(); - let (namespace, func_name_h) = self.func_namespace_name(func); + let (namespace, func_name_h) = self.func_namespace_name(func, !(import ^ self.gen.opts.host)); res.name = func_name_h; res.namespace = namespace; let is_drop = is_drop_method(func); @@ -874,7 +875,7 @@ impl CppInterfaceGenerator<'_> { _ => panic!("drop should be static"), }]; self.gen.c_src.src.push_str(" "); - let mut namespace = namespace(self.resolve, &owner.owner); + let mut namespace = namespace(self.resolve, &owner.owner, matches!(variant, AbiVariant::GuestExport)); namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); self.gen.c_src.qualify(&namespace); uwriteln!(self.gen.c_src.src, "remove_resource({});", params[0]); @@ -892,7 +893,7 @@ impl CppInterfaceGenerator<'_> { } } else { let namespace = if matches!(func.kind, FunctionKind::Freestanding) { - namespace(self.resolve, &TypeOwner::Interface(interface)) + namespace(self.resolve, &TypeOwner::Interface(interface), matches!(variant, AbiVariant::GuestExport)) } else { let owner = &self.resolve.types[match &func.kind { FunctionKind::Static(id) => *id, @@ -901,7 +902,7 @@ impl CppInterfaceGenerator<'_> { FunctionKind::Freestanding => unreachable!(), }] .clone(); - let mut namespace = namespace(self.resolve, &owner.owner); + let mut namespace = namespace(self.resolve, &owner.owner, matches!(variant, AbiVariant::GuestExport)); namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); namespace }; @@ -971,7 +972,7 @@ impl CppInterfaceGenerator<'_> { fn scoped_type_name(&self, id: TypeId, from_namespace: &Vec) -> String { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner); + let namespc = namespace(self.resolve, &ty.owner, false); let mut relative = SourceWithState::default(); relative.namespace = from_namespace.clone(); relative.qualify(&namespc); @@ -1124,7 +1125,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner); + let namespc = namespace(self.resolve, &ty.owner, false); self.gen.h_src.change_namespace(&namespc); Self::docs(&mut self.gen.h_src.src, docs); let pascal = name.to_pascal_case(); @@ -1151,7 +1152,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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); + let namespc = namespace(self.resolve, &type_.owner, !guest_import); let pascal = name.to_upper_camel_case(); let user_filename = namespc.join("-") + "-" + &pascal + ".h"; if definition { @@ -1243,7 +1244,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner); + let namespc = namespace(self.resolve, &ty.owner, false); self.gen.h_src.change_namespace(&namespc); Self::docs(&mut self.gen.h_src.src, docs); let pascal = name.to_pascal_case(); @@ -1281,7 +1282,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner); + let namespc = namespace(self.resolve, &ty.owner, false); self.gen.h_src.change_namespace(&namespc); Self::docs(&mut self.gen.h_src.src, docs); let pascal = name.to_pascal_case(); @@ -1334,7 +1335,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner); + let namespc = namespace(self.resolve, &ty.owner, false); self.gen.h_src.change_namespace(&namespc); let pascal = name.to_pascal_case(); Self::docs(&mut self.gen.h_src.src, docs); @@ -1359,7 +1360,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner); + let namespc = namespace(self.resolve, &ty.owner, false); self.gen.h_src.change_namespace(&namespc); let pascal = name.to_pascal_case(); Self::docs(&mut self.gen.h_src.src, docs); @@ -1451,7 +1452,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { fn load(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec) { 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)); + results.push(format!("*(({}*) wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), ({} + {})))", ty, operands[0], offset)); } else { results.push(format!("*(({}*) ({} + {}))", ty, operands[0], offset)); } @@ -1952,7 +1953,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { for (typ, value) in sig.params.iter().zip(operands.iter()) { match typ { WasmType::I32 => uwrite!(self.src, "WASM_I32_VAL({}),", value), - WasmType::I64 => todo!(), + WasmType::I64 => uwrite!(self.src, "WASM_I64_VAL({}),", value), WasmType::F32 => todo!(), WasmType::F64 => todo!(), } @@ -1994,7 +1995,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::CallInterface { func } => { // dbg!(func); self.let_results(func.results.len(), results); - let (mut namespace, func_name_h) = self.gen.func_namespace_name(func); + let (mut namespace, func_name_h) = self.gen.func_namespace_name(func, !self.gen.gen.opts.host); if matches!(func.kind, FunctionKind::Method(_)) { let this = operands.remove(0); //self.gen.gen.c_src.qualify(&namespace); diff --git a/crates/cpp/test_headers/wasm_export.h b/crates/cpp/test_headers/wasm_export.h index b5597de2a..47bc6c61e 100644 --- a/crates/cpp/test_headers/wasm_export.h +++ b/crates/cpp/test_headers/wasm_export.h @@ -1,5 +1,26 @@ -typedef void* wasm_exec_env_t; +// minimal WAMR header mock-up for compilation tests +#include +struct WASMExecEnv; +typedef WASMExecEnv* wasm_exec_env_t; typedef void* wasm_module_inst_t; +typedef void* wasm_function_inst_t; +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; 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 { @@ -9,3 +30,8 @@ struct NativeSymbol { 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, wasm_val_t*, uint32_t, wasm_val_t*); +wasm_function_inst_t wasm_runtime_lookup_function(wasm_module_inst_t, const char*, const char*); +#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)}} From 5b73b5b898cf69a219cd4a6902b4469f525a7879 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 28 Jan 2024 17:23:30 +0100 Subject: [PATCH 047/672] this example is no longer needed --- tests/other/easy_test.cpp | 7 - tests/other/expected | 6 - tests/other/expected.hpp | 2444 ------------------------------- tests/other/fusion_cpp.h | 242 --- tests/other/result_resource.wit | 12 - tests/other/simple.wit | 13 - tests/other/simple2.wit | 25 - tests/other/wasm_export.h | 36 - 8 files changed, 2785 deletions(-) delete mode 100644 tests/other/easy_test.cpp delete mode 100644 tests/other/expected delete mode 100755 tests/other/expected.hpp delete mode 100644 tests/other/fusion_cpp.h delete mode 100644 tests/other/result_resource.wit delete mode 100644 tests/other/simple.wit delete mode 100644 tests/other/simple2.wit delete mode 100644 tests/other/wasm_export.h diff --git a/tests/other/easy_test.cpp b/tests/other/easy_test.cpp deleted file mode 100644 index 399981fdb..000000000 --- a/tests/other/easy_test.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "my_world_cpp.h" - -int main() { - test::example::my_interface::MyObject o(42); - o.Set(o.Get()+1); - return 0; -} diff --git a/tests/other/expected b/tests/other/expected deleted file mode 100644 index 66b06b147..000000000 --- a/tests/other/expected +++ /dev/null @@ -1,6 +0,0 @@ -#include - -namespace std { - using ::tl::expected; - using ::tl::unexpected; -} diff --git a/tests/other/expected.hpp b/tests/other/expected.hpp deleted file mode 100755 index afee404d4..000000000 --- a/tests/other/expected.hpp +++ /dev/null @@ -1,2444 +0,0 @@ -/// -// 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 -// . -/// - -#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 -#include -#include -#include - -#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 -#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 -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::has_trivial_copy_assign - -// 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 - -// 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 -struct is_trivially_copy_constructible - : std::is_trivially_copy_constructible {}; -#ifdef _GLIBCXX_VECTOR -template -struct is_trivially_copy_constructible> : std::false_type {}; -#endif -} // namespace detail -} // namespace tl -#endif - -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - tl::detail::is_trivially_copy_constructible -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::is_trivially_copy_assignable -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ - std::is_trivially_destructible -#else -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ - std::is_trivially_copy_constructible -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ - std::is_trivially_copy_assignable -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ - std::is_trivially_destructible -#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 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 unexpected { -public: - static_assert(!std::is_same::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 ::value>::type * = nullptr> - constexpr explicit unexpected(Args &&...args) - : m_val(std::forward(args)...) {} - template < - class U, class... Args, - typename std::enable_if &, Args &&...>::value>::type * = nullptr> - constexpr explicit unexpected(std::initializer_list l, Args &&...args) - : m_val(l, std::forward(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 unexpected(E) -> unexpected; -#endif - -template -constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() == rhs.value(); -} -template -constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() != rhs.value(); -} -template -constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() < rhs.value(); -} -template -constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() <= rhs.value(); -} -template -constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() > rhs.value(); -} -template -constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { - return lhs.value() >= rhs.value(); -} - -template -unexpected::type> make_unexpected(E &&e) { - return unexpected::type>(std::forward(e)); -} - -struct unexpect_t { - unexpect_t() = default; -}; -static constexpr unexpect_t unexpect{}; - -namespace detail { -template -[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - throw std::forward(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 using remove_const_t = typename std::remove_const::type; -template -using remove_reference_t = typename std::remove_reference::type; -template using decay_t = typename std::decay::type; -template -using enable_if_t = typename std::enable_if::type; -template -using conditional_t = typename std::conditional::type; - -// std::conjunction from C++17 -template struct conjunction : std::true_type {}; -template struct conjunction : B {}; -template -struct conjunction - : std::conditional, 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 -struct is_pointer_to_non_const_member_func : std::false_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; -template -struct is_pointer_to_non_const_member_func - : std::true_type {}; - -template struct is_const_or_const_ref : std::false_type {}; -template struct is_const_or_const_ref : std::true_type {}; -template struct is_const_or_const_ref : 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::value && - is_const_or_const_ref::value)>, -#endif - typename = enable_if_t>::value>, int = 0> -constexpr auto invoke(Fn &&f, Args &&...args) noexcept( - noexcept(std::mem_fn(f)(std::forward(args)...))) - -> decltype(std::mem_fn(f)(std::forward(args)...)) { - return std::mem_fn(f)(std::forward(args)...); -} - -template >::value>> -constexpr auto invoke(Fn &&f, Args &&...args) noexcept( - noexcept(std::forward(f)(std::forward(args)...))) - -> decltype(std::forward(f)(std::forward(args)...)) { - return std::forward(f)(std::forward(args)...); -} - -// std::invoke_result from C++17 -template struct invoke_result_impl; - -template -struct invoke_result_impl< - F, - decltype(detail::invoke(std::declval(), std::declval()...), void()), - Us...> { - using type = - decltype(detail::invoke(std::declval(), std::declval()...)); -}; - -template -using invoke_result = invoke_result_impl; - -template -using invoke_result_t = typename invoke_result::type; - -#if defined(_MSC_VER) && _MSC_VER <= 1900 -// TODO make a version which works with MSVC 2015 -template struct is_swappable : std::true_type {}; - -template 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 tag swap(T &, T &); -template 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 std::false_type can_swap(...) noexcept(false); -template (), std::declval()))> -std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), - std::declval()))); - -template std::false_type uses_std(...); -template -std::is_same(), std::declval())), tag> -uses_std(int); - -template -struct is_std_swap_noexcept - : std::integral_constant::value && - std::is_nothrow_move_assignable::value> {}; - -template -struct is_std_swap_noexcept : is_std_swap_noexcept {}; - -template -struct is_adl_swap_noexcept - : std::integral_constant(0))> {}; -} // namespace swap_adl_tests - -template -struct is_swappable - : std::integral_constant< - bool, - decltype(detail::swap_adl_tests::can_swap(0))::value && - (!decltype(detail::swap_adl_tests::uses_std(0))::value || - (std::is_move_assignable::value && - std::is_move_constructible::value))> {}; - -template -struct is_swappable - : std::integral_constant< - bool, - decltype(detail::swap_adl_tests::can_swap(0))::value && - (!decltype(detail::swap_adl_tests::uses_std( - 0))::value || - is_swappable::value)> {}; - -template -struct is_nothrow_swappable - : std::integral_constant< - bool, - is_swappable::value && - ((decltype(detail::swap_adl_tests::uses_std(0))::value && - detail::swap_adl_tests::is_std_swap_noexcept::value) || - (!decltype(detail::swap_adl_tests::uses_std(0))::value && - detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; -#endif -#endif - -// Trait for checking if a type is a tl::expected -template struct is_expected_impl : std::false_type {}; -template -struct is_expected_impl> : std::true_type {}; -template using is_expected = is_expected_impl>; - -template -using expected_enable_forward_value = detail::enable_if_t< - std::is_constructible::value && - !std::is_same, in_place_t>::value && - !std::is_same, detail::decay_t>::value && - !std::is_same, detail::decay_t>::value>; - -template -using expected_enable_from_other = detail::enable_if_t< - std::is_constructible::value && - std::is_constructible::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_constructible &>::value && - !std::is_constructible &&>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value && - !std::is_convertible &, T>::value && - !std::is_convertible &&, T>::value>; - -template -using is_void_or = conditional_t::value, std::true_type, U>; - -template -using is_copy_constructible_or_void = - is_void_or>; - -template -using is_move_constructible_or_void = - is_void_or>; - -template -using is_copy_assignable_or_void = is_void_or>; - -template -using is_move_assignable_or_void = is_void_or>; - -} // 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 ::value, - bool = std::is_trivially_destructible::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 ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} - - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - - ~expected_storage_base() { - if (m_has_val) { - m_val.~T(); - } else { - m_unexpect.~unexpected(); - } - } - union { - T m_val; - unexpected 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 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 ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} - - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - - ~expected_storage_base() = default; - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; -}; - -// T is trivial, E is not. -template struct expected_storage_base { - 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 ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} - - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - - ~expected_storage_base() { - if (!m_has_val) { - m_unexpect.~unexpected(); - } - } - - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; -}; - -// E is trivial, T is not. -template 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 ::value> * = - nullptr> - constexpr expected_storage_base(in_place_t, Args &&...args) - : m_val(std::forward(args)...), m_has_val(true) {} - - template &, Args &&...>::value> * = nullptr> - constexpr expected_storage_base(in_place_t, std::initializer_list il, - Args &&...args) - : m_val(il, std::forward(args)...), m_has_val(true) {} - template ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - - ~expected_storage_base() { - if (m_has_val) { - m_val.~T(); - } - } - union { - T m_val; - unexpected m_unexpect; - char m_no_init; - }; - bool m_has_val; -}; - -// `T` is `void`, `E` is trivially-destructible -template struct expected_storage_base { - #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 ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - - ~expected_storage_base() = default; - struct dummy {}; - union { - unexpected m_unexpect; - dummy m_val; - }; - bool m_has_val; -}; - -// `T` is `void`, `E` is not trivially-destructible -template struct expected_storage_base { - 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 ::value> * = - nullptr> - constexpr explicit expected_storage_base(unexpect_t, Args &&...args) - : m_unexpect(std::forward(args)...), m_has_val(false) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected_storage_base(unexpect_t, - std::initializer_list il, - Args &&...args) - : m_unexpect(il, std::forward(args)...), m_has_val(false) {} - - ~expected_storage_base() { - if (!m_has_val) { - m_unexpect.~unexpected(); - } - } - - union { - unexpected 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 -struct expected_operations_base : expected_storage_base { - using expected_storage_base::expected_storage_base; - - template void construct(Args &&...args) noexcept { - new (std::addressof(this->m_val)) T(std::forward(args)...); - this->m_has_val = true; - } - - template void construct_with(Rhs &&rhs) noexcept { - new (std::addressof(this->m_val)) T(std::forward(rhs).get()); - this->m_has_val = true; - } - - template void construct_error(Args &&...args) noexcept { - new (std::addressof(this->m_unexpect)) - unexpected(std::forward(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 ::value> - * = nullptr> - void assign(const expected_operations_base &rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~unexpected(); - 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 ::value && - std::is_nothrow_move_constructible::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(); - 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 ::value && - !std::is_nothrow_move_constructible::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(); - -#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 ::value> - * = nullptr> - void assign(expected_operations_base &&rhs) noexcept { - if (!this->m_has_val && rhs.m_has_val) { - geterr().~unexpected(); - construct(std::move(rhs).get()); - } else { - assign_common(std::move(rhs)); - } - } - - template ::value> - * = nullptr> - void assign(expected_operations_base &&rhs) { - if (!this->m_has_val && rhs.m_has_val) { - auto tmp = std::move(geterr()); - geterr().~unexpected(); -#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(); - 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(); - construct(std::move(rhs).get()); - } else { - assign_common(std::move(rhs)); - } - } - -#endif - - // The common part of move/copy assigning - template void assign_common(Rhs &&rhs) { - if (this->m_has_val) { - if (rhs.m_has_val) { - get() = std::forward(rhs).get(); - } else { - destroy_val(); - construct_error(std::forward(rhs).geterr()); - } - } else { - if (!rhs.m_has_val) { - geterr() = std::forward(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 &geterr() & { - return this->m_unexpect; - } - constexpr const unexpected &geterr() const & { return this->m_unexpect; } - TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { - return std::move(this->m_unexpect); - } -#ifndef TL_EXPECTED_NO_CONSTRR - constexpr const unexpected &&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 -struct expected_operations_base : expected_storage_base { - using expected_storage_base::expected_storage_base; - - template 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 void construct_with(Rhs &&) noexcept { - this->m_has_val = true; - } - - template void construct_error(Args &&...args) noexcept { - new (std::addressof(this->m_unexpect)) - unexpected(std::forward(args)...); - this->m_has_val = false; - } - - template void assign(Rhs &&rhs) noexcept { - if (!this->m_has_val) { - if (rhs.m_has_val) { - geterr().~unexpected(); - construct(); - } else { - geterr() = std::forward(rhs).geterr(); - } - } else { - if (!rhs.m_has_val) { - construct_error(std::forward(rhs).geterr()); - } - } - } - - bool has_value() const { return this->m_has_val; } - - TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { - return this->m_unexpect; - } - constexpr const unexpected &geterr() const & { return this->m_unexpect; } - TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { - return std::move(this->m_unexpect); - } -#ifndef TL_EXPECTED_NO_CONSTRR - constexpr const unexpected &&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 :: - value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> -struct expected_copy_base : expected_operations_base { - using expected_operations_base::expected_operations_base; -}; - -// This specialization is for when T or E are not trivially copy constructible -template -struct expected_copy_base : expected_operations_base { - using expected_operations_base::expected_operations_base; - - expected_copy_base() = default; - expected_copy_base(const expected_copy_base &rhs) - : expected_operations_base(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 >::value - &&std::is_trivially_move_constructible::value> -struct expected_move_base : expected_copy_base { - using expected_copy_base::expected_copy_base; -}; -#else -template struct expected_move_base; -#endif -template -struct expected_move_base : expected_copy_base { - using expected_copy_base::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::value) - : expected_copy_base(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 >::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 { - using expected_move_base::expected_move_base; -}; - -template -struct expected_copy_assign_base : expected_move_base { - using expected_move_base::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 , - std::is_trivially_move_constructible, - std::is_trivially_move_assignable>>:: - value &&std::is_trivially_destructible::value - &&std::is_trivially_move_constructible::value - &&std::is_trivially_move_assignable::value> -struct expected_move_assign_base : expected_copy_assign_base { - using expected_copy_assign_base::expected_copy_assign_base; -}; -#else -template struct expected_move_assign_base; -#endif - -template -struct expected_move_assign_base - : expected_copy_assign_base { - using expected_copy_assign_base::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::value - &&std::is_nothrow_move_assignable::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 ::value && - std::is_copy_constructible::value), - bool EnableMove = (is_move_constructible_or_void::value && - std::is_move_constructible::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 -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 = delete; - expected_delete_ctor_base & - operator=(const expected_delete_ctor_base &) = default; - expected_delete_ctor_base & - operator=(expected_delete_ctor_base &&) noexcept = default; -}; - -template -struct expected_delete_ctor_base { - 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 -struct expected_delete_ctor_base { - 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 ::value && - std::is_copy_constructible::value && - is_copy_assignable_or_void::value && - std::is_copy_assignable::value), - bool EnableMove = (is_move_constructible_or_void::value && - std::is_move_constructible::value && - is_move_assignable_or_void::value && - std::is_move_assignable::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 -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 = delete; -}; - -template -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 &) = delete; - expected_delete_assign_base & - operator=(expected_delete_assign_base &&) noexcept = default; -}; - -template -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 &) = 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 ::value || std::is_void::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 struct expected_default_ctor_base { - 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 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` 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 expected : private detail::expected_move_assign_base, - private detail::expected_delete_ctor_base, - private detail::expected_delete_assign_base, - private detail::expected_default_ctor_base { - static_assert(!std::is_reference::value, "T must not be a reference"); - static_assert(!std::is_same::type>::value, - "T must not be in_place_t"); - static_assert(!std::is_same::type>::value, - "T must not be unexpect_t"); - static_assert( - !std::is_same>::type>::value, - "T must not be unexpected"); - static_assert(!std::is_reference::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 *errptr() { return std::addressof(this->m_unexpect); } - const unexpected *errptr() const { - return std::addressof(this->m_unexpect); - } - - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &val() { - return this->m_val; - } - TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } - - template ::value> * = nullptr> - constexpr const U &val() const { - return this->m_val; - } - constexpr const unexpected &err() const { return this->m_unexpect; } - - using impl_base = detail::expected_move_assign_base; - using ctor_base = detail::expected_default_ctor_base; - -public: - typedef T value_type; - typedef E error_type; - typedef unexpected unexpected_type; - -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { - return and_then_impl(*this, std::forward(f)); - } - template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { - return and_then_impl(std::move(*this), std::forward(f)); - } - template constexpr auto and_then(F &&f) const & { - return and_then_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template constexpr auto and_then(F &&f) const && { - return and_then_impl(std::move(*this), std::forward(f)); - } -#endif - -#else - template - TL_EXPECTED_11_CONSTEXPR auto - and_then(F &&f) & -> decltype(and_then_impl(std::declval(), - std::forward(f))) { - return and_then_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR auto - and_then(F &&f) && -> decltype(and_then_impl(std::declval(), - std::forward(f))) { - return and_then_impl(std::move(*this), std::forward(f)); - } - template - constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( - std::declval(), std::forward(f))) { - return and_then_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( - std::declval(), std::forward(f))) { - return and_then_impl(std::move(*this), std::forward(f)); - } -#endif -#endif - -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template constexpr auto map(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - template constexpr auto map(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( - std::declval(), std::declval())) - map(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), - std::declval())) - map(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - map(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - map(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#endif -#endif - -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template constexpr auto transform(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - template constexpr auto transform(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( - std::declval(), std::declval())) - transform(F &&f) & { - return expected_map_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F &&f) && { - return expected_map_impl(std::move(*this), std::forward(f)); - } - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F &&f) const & { - return expected_map_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(expected_map_impl(std::declval(), - std::declval())) - transform(F &&f) const && { - return expected_map_impl(std::move(*this), std::forward(f)); - } -#endif -#endif - -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { - return map_error_impl(*this, std::forward(f)); - } - template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { - return map_error_impl(std::move(*this), std::forward(f)); - } - template constexpr auto map_error(F &&f) const & { - return map_error_impl(*this, std::forward(f)); - } - template constexpr auto map_error(F &&f) const && { - return map_error_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) & { - return map_error_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) && { - return map_error_impl(std::move(*this), std::forward(f)); - } - template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) const & { - return map_error_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - map_error(F &&f) const && { - return map_error_impl(std::move(*this), std::forward(f)); - } -#endif -#endif -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { - return map_error_impl(*this, std::forward(f)); - } - template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { - return map_error_impl(std::move(*this), std::forward(f)); - } - template constexpr auto transform_error(F &&f) const & { - return map_error_impl(*this, std::forward(f)); - } - template constexpr auto transform_error(F &&f) const && { - return map_error_impl(std::move(*this), std::forward(f)); - } -#else - template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - transform_error(F &&f) & { - return map_error_impl(*this, std::forward(f)); - } - template - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), - std::declval())) - transform_error(F &&f) && { - return map_error_impl(std::move(*this), std::forward(f)); - } - template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - transform_error(F &&f) const & { - return map_error_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template - constexpr decltype(map_error_impl(std::declval(), - std::declval())) - transform_error(F &&f) const && { - return map_error_impl(std::move(*this), std::forward(f)); - } -#endif -#endif - template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { - return or_else_impl(*this, std::forward(f)); - } - - template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { - return or_else_impl(std::move(*this), std::forward(f)); - } - - template expected constexpr or_else(F &&f) const & { - return or_else_impl(*this, std::forward(f)); - } - -#ifndef TL_EXPECTED_NO_CONSTRR - template expected constexpr or_else(F &&f) const && { - return or_else_impl(std::move(*this), std::forward(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 ::value> * = - nullptr> - constexpr expected(in_place_t, Args &&...args) - : impl_base(in_place, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} - - template &, Args &&...>::value> * = nullptr> - constexpr expected(in_place_t, std::initializer_list il, Args &&...args) - : impl_base(in_place, il, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} - - template ::value> * = - nullptr, - detail::enable_if_t::value> * = - nullptr> - explicit constexpr expected(const unexpected &e) - : impl_base(unexpect, e.value()), - ctor_base(detail::default_constructor_tag{}) {} - - template < - class G = E, - detail::enable_if_t::value> * = - nullptr, - detail::enable_if_t::value> * = nullptr> - constexpr expected(unexpected const &e) - : impl_base(unexpect, e.value()), - ctor_base(detail::default_constructor_tag{}) {} - - template < - class G = E, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t::value> * = nullptr> - explicit constexpr expected(unexpected &&e) noexcept( - std::is_nothrow_constructible::value) - : impl_base(unexpect, std::move(e.value())), - ctor_base(detail::default_constructor_tag{}) {} - - template < - class G = E, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t::value> * = nullptr> - constexpr expected(unexpected &&e) noexcept( - std::is_nothrow_constructible::value) - : impl_base(unexpect, std::move(e.value())), - ctor_base(detail::default_constructor_tag{}) {} - - template ::value> * = - nullptr> - constexpr explicit expected(unexpect_t, Args &&...args) - : impl_base(unexpect, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} - - template &, Args &&...>::value> * = nullptr> - constexpr explicit expected(unexpect_t, std::initializer_list il, - Args &&...args) - : impl_base(unexpect, il, std::forward(args)...), - ctor_base(detail::default_constructor_tag{}) {} - - template ::value && - std::is_convertible::value)> * = - nullptr, - detail::expected_enable_from_other - * = nullptr> - explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) - : ctor_base(detail::default_constructor_tag{}) { - if (rhs.has_value()) { - this->construct(*rhs); - } else { - this->construct_error(rhs.error()); - } - } - - template ::value && - std::is_convertible::value)> * = - nullptr, - detail::expected_enable_from_other - * = nullptr> - TL_EXPECTED_11_CONSTEXPR expected(const expected &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::value && - std::is_convertible::value)> * = nullptr, - detail::expected_enable_from_other * = nullptr> - explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&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::value && - std::is_convertible::value)> * = nullptr, - detail::expected_enable_from_other * = nullptr> - TL_EXPECTED_11_CONSTEXPR expected(expected &&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::value> * = nullptr, - detail::expected_enable_forward_value * = nullptr> - explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) - : expected(in_place, std::forward(v)) {} - - template < - class U = T, - detail::enable_if_t::value> * = nullptr, - detail::expected_enable_forward_value * = nullptr> - TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) - : expected(in_place, std::forward(v)) {} - - template < - class U = T, class G = T, - detail::enable_if_t::value> * = - nullptr, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t< - (!std::is_same, detail::decay_t>::value && - !detail::conjunction, - std::is_same>>::value && - std::is_constructible::value && - std::is_assignable::value && - std::is_nothrow_move_constructible::value)> * = nullptr> - expected &operator=(U &&v) { - if (has_value()) { - val() = std::forward(v); - } else { - err().~unexpected(); - ::new (valptr()) T(std::forward(v)); - this->m_has_val = true; - } - - return *this; - } - - template < - class U = T, class G = T, - detail::enable_if_t::value> * = - nullptr, - detail::enable_if_t::value> * = nullptr, - detail::enable_if_t< - (!std::is_same, detail::decay_t>::value && - !detail::conjunction, - std::is_same>>::value && - std::is_constructible::value && - std::is_assignable::value && - std::is_nothrow_move_constructible::value)> * = nullptr> - expected &operator=(U &&v) { - if (has_value()) { - val() = std::forward(v); - } else { - auto tmp = std::move(err()); - err().~unexpected(); - -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (valptr()) T(std::forward(v)); - this->m_has_val = true; - } catch (...) { - err() = std::move(tmp); - throw; - } -#else - ::new (valptr()) T(std::forward(v)); - this->m_has_val = true; -#endif - } - - return *this; - } - - template ::value && - std::is_assignable::value> * = nullptr> - expected &operator=(const unexpected &rhs) { - if (!has_value()) { - err() = rhs; - } else { - this->destroy_val(); - ::new (errptr()) unexpected(rhs); - this->m_has_val = false; - } - - return *this; - } - - template ::value && - std::is_move_assignable::value> * = nullptr> - expected &operator=(unexpected &&rhs) noexcept { - if (!has_value()) { - err() = std::move(rhs); - } else { - this->destroy_val(); - ::new (errptr()) unexpected(std::move(rhs)); - this->m_has_val = false; - } - - return *this; - } - - template ::value> * = nullptr> - void emplace(Args &&...args) { - if (has_value()) { - val().~T(); - } else { - err().~unexpected(); - this->m_has_val = true; - } - ::new (valptr()) T(std::forward(args)...); - } - - template ::value> * = nullptr> - void emplace(Args &&...args) { - if (has_value()) { - val().~T(); - ::new (valptr()) T(std::forward(args)...); - } else { - auto tmp = std::move(err()); - err().~unexpected(); - -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (valptr()) T(std::forward(args)...); - this->m_has_val = true; - } catch (...) { - err() = std::move(tmp); - throw; - } -#else - ::new (valptr()) T(std::forward(args)...); - this->m_has_val = true; -#endif - } - } - - template &, Args &&...>::value> * = nullptr> - void emplace(std::initializer_list il, Args &&...args) { - if (has_value()) { - T t(il, std::forward(args)...); - val() = std::move(t); - } else { - err().~unexpected(); - ::new (valptr()) T(il, std::forward(args)...); - this->m_has_val = true; - } - } - - template &, Args &&...>::value> * = nullptr> - void emplace(std::initializer_list il, Args &&...args) { - if (has_value()) { - T t(il, std::forward(args)...); - val() = std::move(t); - } else { - auto tmp = std::move(err()); - err().~unexpected(); - -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED - try { - ::new (valptr()) T(il, std::forward(args)...); - this->m_has_val = true; - } catch (...) { - err() = std::move(tmp); - throw; - } -#else - ::new (valptr()) T(il, std::forward(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::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::type{}, - typename std::is_nothrow_move_constructible::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 - detail::enable_if_t::value && - detail::is_swappable::value && - (std::is_nothrow_move_constructible::value || - std::is_nothrow_move_constructible::value)> - swap(expected &rhs) noexcept( - std::is_nothrow_move_constructible::value - &&detail::is_nothrow_swappable::value - &&std::is_nothrow_move_constructible::value - &&detail::is_nothrow_swappable::value) { - if (has_value() && rhs.has_value()) { - swap_where_both_have_value(rhs, typename std::is_void::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::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 ::value> * = nullptr> - constexpr const U &operator*() const & { - TL_ASSERT(has_value()); - return val(); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &operator*() & { - TL_ASSERT(has_value()); - return val(); - } - template ::value> * = nullptr> - constexpr const U &&operator*() const && { - TL_ASSERT(has_value()); - return std::move(val()); - } - template ::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 ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR const U &value() const & { - if (!has_value()) - detail::throw_exception(bad_expected_access(err().value())); - return val(); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &value() & { - if (!has_value()) - detail::throw_exception(bad_expected_access(err().value())); - return val(); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR const U &&value() const && { - if (!has_value()) - detail::throw_exception(bad_expected_access(std::move(err()).value())); - return std::move(val()); - } - template ::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &&value() && { - if (!has_value()) - detail::throw_exception(bad_expected_access(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 constexpr T value_or(U &&v) const & { - static_assert(std::is_copy_constructible::value && - std::is_convertible::value, - "T must be copy-constructible and convertible to from U&&"); - return bool(*this) ? **this : static_cast(std::forward(v)); - } - template TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { - static_assert(std::is_move_constructible::value && - std::is_convertible::value, - "T must be move-constructible and convertible to from U&&"); - return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); - } -}; - -namespace detail { -template using exp_t = typename detail::decay_t::value_type; -template using err_t = typename detail::decay_t::error_type; -template using ret_t = expected>; - -#ifdef TL_EXPECTED_CXX14 -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval()))> -constexpr auto and_then_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); - - return exp.has_value() - ? detail::invoke(std::forward(f), *std::forward(exp)) - : Ret(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval()))> -constexpr auto and_then_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); - - return exp.has_value() ? detail::invoke(std::forward(f)) - : Ret(unexpect, std::forward(exp).error()); -} -#else -template struct TC; -template (), - *std::declval())), - detail::enable_if_t>::value> * = nullptr> -auto and_then_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); - - return exp.has_value() - ? detail::invoke(std::forward(f), *std::forward(exp)) - : Ret(unexpect, std::forward(exp).error()); -} - -template ())), - detail::enable_if_t>::value> * = nullptr> -constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); - - return exp.has_value() ? detail::invoke(std::forward(f)) - : Ret(unexpect, std::forward(exp).error()); -} -#endif - -#ifdef TL_EXPECTED_CXX14 -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> -constexpr auto expected_map_impl(Exp &&exp, F &&f) { - using result = ret_t>; - return exp.has_value() ? result(detail::invoke(std::forward(f), - *std::forward(exp))) - : result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> -auto expected_map_impl(Exp &&exp, F &&f) { - using result = expected>; - if (exp.has_value()) { - detail::invoke(std::forward(f), *std::forward(exp)); - return result(); - } - - return result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> -constexpr auto expected_map_impl(Exp &&exp, F &&f) { - using result = ret_t>; - return exp.has_value() ? result(detail::invoke(std::forward(f))) - : result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> -auto expected_map_impl(Exp &&exp, F &&f) { - using result = expected>; - if (exp.has_value()) { - detail::invoke(std::forward(f)); - return result(); - } - - return result(unexpect, std::forward(exp).error()); -} -#else -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> - -constexpr auto expected_map_impl(Exp &&exp, F &&f) - -> ret_t> { - using result = ret_t>; - - return exp.has_value() ? result(detail::invoke(std::forward(f), - *std::forward(exp))) - : result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - *std::declval())), - detail::enable_if_t::value> * = nullptr> - -auto expected_map_impl(Exp &&exp, F &&f) -> expected> { - if (exp.has_value()) { - detail::invoke(std::forward(f), *std::forward(exp)); - return {}; - } - - return unexpected>(std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> - -constexpr auto expected_map_impl(Exp &&exp, F &&f) - -> ret_t> { - using result = ret_t>; - - return exp.has_value() ? result(detail::invoke(std::forward(f))) - : result(unexpect, std::forward(exp).error()); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval())), - detail::enable_if_t::value> * = nullptr> - -auto expected_map_impl(Exp &&exp, F &&f) -> expected> { - if (exp.has_value()) { - detail::invoke(std::forward(f)); - return {}; - } - - return unexpected>(std::forward(exp).error()); -} -#endif - -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, detail::decay_t>; - return exp.has_value() - ? result(*std::forward(exp)) - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, monostate>; - if (exp.has_value()) { - return result(*std::forward(exp)); - } - - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, detail::decay_t>; - return exp.has_value() - ? result() - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) { - using result = expected, monostate>; - if (exp.has_value()) { - return result(); - } - - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -#else -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) - -> expected, detail::decay_t> { - using result = expected, detail::decay_t>; - - return exp.has_value() - ? result(*std::forward(exp)) - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { - using result = expected, monostate>; - if (exp.has_value()) { - return result(*std::forward(exp)); - } - - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto map_error_impl(Exp &&exp, F &&f) - -> expected, detail::decay_t> { - using result = expected, detail::decay_t>; - - return exp.has_value() - ? result() - : result(unexpect, detail::invoke(std::forward(f), - std::forward(exp).error())); -} - -template >::value> * = nullptr, - class Ret = decltype(detail::invoke(std::declval(), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { - using result = expected, monostate>; - if (exp.has_value()) { - return result(); - } - - detail::invoke(std::forward(f), std::forward(exp).error()); - return result(unexpect, monostate{}); -} -#endif - -#ifdef TL_EXPECTED_CXX14 -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -constexpr auto or_else_impl(Exp &&exp, F &&f) { - static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() ? std::forward(exp) - : detail::invoke(std::forward(f), - std::forward(exp).error()); -} - -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() ? std::forward(exp) - : (detail::invoke(std::forward(f), - std::forward(exp).error()), - std::forward(exp)); -} -#else -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -auto or_else_impl(Exp &&exp, F &&f) -> Ret { - static_assert(detail::is_expected::value, "F must return an expected"); - return exp.has_value() ? std::forward(exp) - : detail::invoke(std::forward(f), - std::forward(exp).error()); -} - -template (), - std::declval().error())), - detail::enable_if_t::value> * = nullptr> -detail::decay_t or_else_impl(Exp &&exp, F &&f) { - return exp.has_value() ? std::forward(exp) - : (detail::invoke(std::forward(f), - std::forward(exp).error()), - std::forward(exp)); -} -#endif -} // namespace detail - -template -constexpr bool operator==(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? false - : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); -} -template -constexpr bool operator!=(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? true - : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); -} -template -constexpr bool operator==(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? false - : (!lhs.has_value() ? lhs.error() == rhs.error() : true); -} -template -constexpr bool operator!=(const expected &lhs, - const expected &rhs) { - return (lhs.has_value() != rhs.has_value()) - ? true - : (!lhs.has_value() ? lhs.error() == rhs.error() : false); -} - -template -constexpr bool operator==(const expected &x, const U &v) { - return x.has_value() ? *x == v : false; -} -template -constexpr bool operator==(const U &v, const expected &x) { - return x.has_value() ? *x == v : false; -} -template -constexpr bool operator!=(const expected &x, const U &v) { - return x.has_value() ? *x != v : true; -} -template -constexpr bool operator!=(const U &v, const expected &x) { - return x.has_value() ? *x != v : true; -} - -template -constexpr bool operator==(const expected &x, const unexpected &e) { - return x.has_value() ? false : x.error() == e.value(); -} -template -constexpr bool operator==(const unexpected &e, const expected &x) { - return x.has_value() ? false : x.error() == e.value(); -} -template -constexpr bool operator!=(const expected &x, const unexpected &e) { - return x.has_value() ? true : x.error() != e.value(); -} -template -constexpr bool operator!=(const unexpected &e, const expected &x) { - return x.has_value() ? true : x.error() != e.value(); -} - -template ::value || - std::is_move_constructible::value) && - detail::is_swappable::value && - std::is_move_constructible::value && - detail::is_swappable::value> * = nullptr> -void swap(expected &lhs, - expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { - lhs.swap(rhs); -} -} // namespace tl - -#endif diff --git a/tests/other/fusion_cpp.h b/tests/other/fusion_cpp.h deleted file mode 100644 index a0eb471de..000000000 --- a/tests/other/fusion_cpp.h +++ /dev/null @@ -1,242 +0,0 @@ -// Generated by `wit-bindgen` 0.1.0. DO NOT EDIT! -#ifndef __CPP_GUEST_BINDINGS_FUSION_H -#define __CPP_GUEST_BINDINGS_FUSION_H -#include -#include -#include -#include -#include -#include -#include -namespace fusion { class ResourceBase{ - protected: - int32_t handle; - bool owned; - public: - ResourceBase(int32_t h=0, bool o=false) : handle(h), owned(o) {} - int32_t get_handle() const { return handle; } - }; } -namespace autosar { namespace ara { namespace types { using ErrorCodeType = int32_t; }}} -namespace autosar { namespace ara { namespace types { class ErrorDomain : fusion::ResourceBase { - public: - std::string Name(); - std::string Message(::autosar::ara::types::ErrorCodeType n); -// int32_t wasm_handle() const {return handle;} - ~ErrorDomain(); - ErrorDomain(fusion::ResourceBase&&); -}; }}} -namespace autosar { namespace ara { namespace types { - struct ErrorCode { - ::autosar::ara::types::ErrorCodeType value; - types::ErrorDomain domain; - }; -}}} -namespace autosar { namespace ara { namespace e2exf { - enum class ConfigurationFormat : uint8_t { - kJson = 0, - kXml = 1, - }; -}}} -namespace autosar { namespace ara { namespace core { using ErrorCode = ::autosar::ara::types::ErrorCode; }}} -namespace autosar { namespace ara { namespace com { using ConfigurationFormat = ::autosar::ara::e2exf::ConfigurationFormat; }}} -namespace autosar { namespace ara { namespace core { class InstanceSpecifier : fusion::ResourceBase { - public: - InstanceSpecifier(std::string_view const& spec); - std::string ToString(); - InstanceSpecifier Clone(); - ~InstanceSpecifier(); - InstanceSpecifier(fusion::ResourceBase&&); -}; }}} -namespace autosar { namespace ara { namespace com { using InstanceSpecifier = ::autosar::ara::core::InstanceSpecifier; }}} -namespace autosar { namespace ara { namespace exec { - enum class ExecutionState : uint8_t { - kRunning = 0, - }; -}}} -// 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). -namespace autosar { namespace ara { namespace poll { using Pollable = uint32_t; }}} -namespace autosar { namespace ara { namespace radar { using ErrorCode = ::autosar::ara::types::ErrorCode; }}} -namespace autosar { namespace ara { namespace radar { using InstanceSpecifier = ::autosar::ara::core::InstanceSpecifier; }}} -namespace autosar { namespace ara { namespace radar { using Pollable = ::autosar::ara::poll::Pollable; }}} -namespace autosar { namespace ara { namespace radar { - struct Position { - int32_t x; - int32_t y; - int32_t z; - }; -}}} -namespace autosar { namespace ara { namespace radar { - struct RadarObjects { - bool active; - std::vector object_vector; - }; -}}} -namespace autosar { namespace ara { namespace radar { - struct AdjustOutput { - bool success; - ::autosar::ara::radar::Position effective_position; - }; -}}} -namespace autosar { namespace ara { namespace radar { - enum class FusionVariant : uint8_t { - kChina = 0, - kUsa = 1, - kEurope = 2, - kRussia = 3, - }; -}}} -namespace autosar { namespace ara { namespace radar { - struct CalibrateOutput { - bool call_result; - }; -}}} -namespace autosar { namespace ara { namespace radar { class FutureResultAdjustOutputErrorCode : fusion::ResourceBase { - public: - ::autosar::ara::radar::Pollable Subscribe(); - std::expected Value(); - ~FutureResultAdjustOutputErrorCode(); - FutureResultAdjustOutputErrorCode(fusion::ResourceBase&&); -}; }}} -namespace autosar { namespace ara { namespace radar { class FutureResultCalibrateOutputErrorCode : fusion::ResourceBase { - public: - ::autosar::ara::radar::Pollable Subscribe(); - std::expected Value(); - ~FutureResultCalibrateOutputErrorCode(); - FutureResultCalibrateOutputErrorCode(fusion::ResourceBase&&); -}; }}} -namespace autosar { namespace ara { namespace radar { class FutureU32 : fusion::ResourceBase { - public: - ::autosar::ara::radar::Pollable Subscribe(); - uint32_t Value(); - ~FutureU32(); - FutureU32(fusion::ResourceBase&&); -}; }}} -namespace autosar { namespace ara { namespace radar { class StreamU32 : fusion::ResourceBase { - public: - ::autosar::ara::radar::Pollable Subscribe(); - std::optional Value(); - ~StreamU32(); - StreamU32(fusion::ResourceBase&&); -}; }}} -namespace autosar { namespace ara { namespace radar { class FutureU16 : fusion::ResourceBase { - public: - ::autosar::ara::radar::Pollable Subscribe(); - uint16_t Value(); - ~FutureU16(); - FutureU16(fusion::ResourceBase&&); -}; }}} -namespace autosar { namespace ara { namespace radar { class StreamU16 : fusion::ResourceBase { - public: - ::autosar::ara::radar::Pollable Subscribe(); - std::optional Value(); - ~StreamU16(); - StreamU16(fusion::ResourceBase&&); -}; }}} -namespace autosar { namespace ara { namespace radar { class ProxyHandle : fusion::ResourceBase { - public: -}; }}} -namespace autosar { namespace ara { namespace radar { class StreamProxyHandle : fusion::ResourceBase { - public: - ::autosar::ara::radar::Pollable Subscribe(); - std::optional Value(); - ~StreamProxyHandle(); - StreamProxyHandle(fusion::ResourceBase&&); -}; }}} -namespace autosar { namespace ara { namespace radar { class StreamRadarObjects : fusion::ResourceBase { - public: - ::autosar::ara::radar::Pollable Subscribe(); - std::optional Value(); - ~StreamRadarObjects(); - StreamRadarObjects(fusion::ResourceBase&&); -}; }}} -namespace autosar { namespace ara { namespace radar { class Proxy : fusion::ResourceBase { - public: - Proxy(radar::ProxyHandle handle); - FutureResultAdjustOutputErrorCode Adjust(::autosar::ara::radar::Position const& target_position); - FutureResultCalibrateOutputErrorCode Calibrate(std::string_view const& configuration, ::autosar::ara::radar::FusionVariant radar_variant); - void Echo(std::string_view const& text); - std::expected SubscribeBrakeEvent(uint32_t max_sample_count); - std::expected SubscribeParkingBrakeEvent(uint32_t max_sample_count); - FutureU32 GetUpdateRate(); - FutureU32 SetUpdateRate(uint32_t value); - std::expected SubscribeUpdateRate(uint32_t max_sample_count); - std::expected SubscribeFrontObjectDistance(uint32_t max_sample_count); - FutureU16 GetRearObjectDistance(); - FutureU16 SetObjectDetectionLimit(uint16_t value); - ~Proxy(); - Proxy(fusion::ResourceBase&&); -}; }}} -namespace autosar { namespace ara { namespace log { class Logger : fusion::ResourceBase { - public: - void Report(uint32_t level, std::string_view const& message); - Logger(std::string_view const& context, std::string_view const& description, uint32_t level); - ~Logger(); - Logger(fusion::ResourceBase&&); -}; }}} -namespace autosar { namespace ara { namespace com { class InstanceIdentifier : fusion::ResourceBase { - public: - InstanceIdentifier(std::string_view const& id); - std::string ToString(); - ~InstanceIdentifier(); - InstanceIdentifier(fusion::ResourceBase&&); -}; }}} -namespace autosar { namespace ara { namespace exec { class ExecutionClient : fusion::ResourceBase { - public: - ExecutionClient(void); - void ReportExecutionState(::autosar::ara::exec::ExecutionState state); - ~ExecutionClient(); - ExecutionClient(fusion::ResourceBase&&); -}; }}} -namespace autosar { namespace ara { namespace types { -}}} -namespace autosar { namespace ara { namespace core { - std::expected Initialize(void); - std::expected Deinitialize(void); - std::expected CreateInstanceSpecifier(std::string_view const& spec); -}}} -namespace autosar { namespace ara { namespace poll { - // Dispose of the specified `pollable`, after which it may no longer - // be used. - void DropPollable(::autosar::ara::poll::Pollable ths); - // 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 is the same length as the argument - // list, and indicates the readiness of each corresponding - // element in that / list, with true indicating ready. - std::vector PollOneoff(std::vector const& in); -}}} -namespace autosar { namespace ara { namespace radar { - StreamProxyHandle StartFindService(radar::InstanceSpecifier spec); -}}} -namespace autosar { namespace ara { namespace log { -}}} -namespace autosar { namespace ara { namespace e2exf { -}}} -namespace autosar { namespace ara { namespace com { - bool StatusHandlerConfigure(std::string_view const& binding_configuration, ::autosar::ara::com::ConfigurationFormat binding_format, std::string_view const& e2exf_configuration, ::autosar::ara::com::ConfigurationFormat e2exf_format); - std::vector ResolveInstanceIds(com::InstanceSpecifier spec); -}}} -namespace autosar { namespace ara { namespace exec { -}}} - -#endif diff --git a/tests/other/result_resource.wit b/tests/other/result_resource.wit deleted file mode 100644 index 92121134e..000000000 --- a/tests/other/result_resource.wit +++ /dev/null @@ -1,12 +0,0 @@ -package test:example - -interface my-interface { - resource r - - function1: func() -> option - function2: func() -> result -} - -world test-world { - import my-interface -} diff --git a/tests/other/simple.wit b/tests/other/simple.wit deleted file mode 100644 index 68fcec8ef..000000000 --- a/tests/other/simple.wit +++ /dev/null @@ -1,13 +0,0 @@ -package test:example - -interface my-interface { - resource my-object { - constructor(a: u32) - set: func(v: u32) - get: func() -> u32 - } -} - -world my-world { - import my-interface -} diff --git a/tests/other/simple2.wit b/tests/other/simple2.wit deleted file mode 100644 index 09b4c6837..000000000 --- a/tests/other/simple2.wit +++ /dev/null @@ -1,25 +0,0 @@ -package test:example - -interface my-interface { - resource pollable - - resource my-object { - constructor(a: u32) - set: func(v: u32) - get: func() -> u32 - //static consume: func(obj: own) - } - - resource stream-u16 { - subscribe: func() -> pollable - value: func() -> option - } - - resource container { - create: func() -> result - } -} - -world my-world2 { - import my-interface -} diff --git a/tests/other/wasm_export.h b/tests/other/wasm_export.h deleted file mode 100644 index a3caacfa2..000000000 --- a/tests/other/wasm_export.h +++ /dev/null @@ -1,36 +0,0 @@ -// minimal WAMR header mock-up for compilation tests -#include -struct WASMExecEnv; -typedef WASMExecEnv* wasm_exec_env_t; -typedef void* wasm_module_inst_t; -typedef void* wasm_function_inst_t; -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; -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, wasm_val_t*, uint32_t, wasm_val_t*); -wasm_function_inst_t wasm_runtime_lookup_function(wasm_module_inst_t, const char*, const char*); -#define WASM_INIT_VAL {.kind = WASM_I32, .of = {.i32 = 0}} -#define WASM_I32_VAL(x) {.kind = WASM_I32, .of = {.i32 =(x)}} From 7d3614fee56f1db33bc2951d5156ed01ff731427 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 6 Feb 2024 22:52:09 +0100 Subject: [PATCH 048/672] fix the float test --- Cargo.lock | 2 +- crates/cpp/src/lib.rs | 4 ++-- crates/cpp/test_headers/wasm_export.h | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0a0748638..16e909acc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2399,7 +2399,7 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder 0.40.0", + "wasm-encoder 0.41.0", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 8a9b315f0..c21e88642 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1954,8 +1954,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { match typ { WasmType::I32 => uwrite!(self.src, "WASM_I32_VAL({}),", value), WasmType::I64 => uwrite!(self.src, "WASM_I64_VAL({}),", value), - WasmType::F32 => todo!(), - WasmType::F64 => todo!(), + WasmType::F32 => uwrite!(self.src, "WASM_F32_VAL({}),", value), + WasmType::F64 => uwrite!(self.src, "WASM_F64_VAL({}),", value), } } self.src.push_str("};\n"); diff --git a/crates/cpp/test_headers/wasm_export.h b/crates/cpp/test_headers/wasm_export.h index 47bc6c61e..13eec73c1 100644 --- a/crates/cpp/test_headers/wasm_export.h +++ b/crates/cpp/test_headers/wasm_export.h @@ -35,3 +35,5 @@ wasm_function_inst_t wasm_runtime_lookup_function(wasm_module_inst_t, const char #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)}} From 2addf2b2eb88fcd7dbe28f6796cf559200a52a88 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 6 Feb 2024 23:50:58 +0100 Subject: [PATCH 049/672] work on strings --- crates/cpp/DESIGN.md | 20 +++++++-------- crates/cpp/helper-types/wit-rt.h | 24 ++++++++++++++++++ crates/cpp/src/lib.rs | 43 ++++++++++++++++++++------------ 3 files changed, 61 insertions(+), 26 deletions(-) create mode 100644 crates/cpp/helper-types/wit-rt.h diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md index b5e3474ea..304d2a6d3 100644 --- a/crates/cpp/DESIGN.md +++ b/crates/cpp/DESIGN.md @@ -26,17 +26,17 @@ | | | record{string, list} | &T | T const& | a,l,a,l | | | | large-struct (>16 args) | &T | T const& | &t | | | | result | Result<&str, &[]> | std::expected | d,a,l | -| GIR | t | string | String | wasi::string | &(addr, len) | | -| | | list | Vec | wasi::vector | &(a,l) | -| | | result | Result | std::expected | &(d,a,l) | -| GEA | t | string | String | wasi::string&& | addr, len | -| | | result | Result | std::expected&& | d,a,l | -| GER | p | string | String | wasi::string (or std?) | -> &(a,l) cabi_post_N:P/I#F | -| | | result | Result | std::expected | -> &(d,a,l) cabi_post | -| --S | ? | string | String | wasi::string | addr, len | +| GIR | t | string | String | wit::string | &(addr, len) | | +| | | list | Vec | wit::vector | &(a,l) | +| | | result | Result | std::expected | &(d,a,l) | +| GEA | t | string | String | wit::string&& | addr, len | +| | | result | Result | std::expected&& | d,a,l | +| GER | p | string | String | wit::string (or std?) | -> &(a,l) cabi_post_N:P/I#F | +| | | result | Result | std::expected | -> &(d,a,l) cabi_post | +| --S | ? | string | String | wit::string | addr, len | | HIA | v | string | | string_view | a,l | -| HIR | t | string | | wasi::string | &(a,l) | -| HEA | t | string | | wasi::string&& | a,l | +| HIR | t | string | | wit::string | &(a,l) | +| HEA | t | string | | wit::string&& | a,l | | HER | p | string | | string_view + special cleanup | -> &(a,l) | Note: The host never frees memory (is never passed ownership)! diff --git a/crates/cpp/helper-types/wit-rt.h b/crates/cpp/helper-types/wit-rt.h new file mode 100644 index 000000000..141a27517 --- /dev/null +++ b/crates/cpp/helper-types/wit-rt.h @@ -0,0 +1,24 @@ +#include +#include +#include + +namespace wit { + class string : public std::string_view { + uint8_t * owned; + public: + string(string const&) = delete; + string(string&&b) : std::string_view(b.data(), b.length()), owned(b.owned) { + b.owned=nullptr; + } + string& operator=(string const&) = delete; + string& operator=(string &&b) { + if (owned) {free(owned);} + *static_cast(this) = b; + owned=b.owned; + b.owned=nullptr; + } + ~string() { + free(owned); + } + }; +} diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index c21e88642..0fc2feaae 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -25,6 +25,9 @@ pub const OWNED_CLASS_NAME: &str = "Owned"; type CppType = String; +enum Position { Argument, Result, InStruct } +type Flavor = (AbiVariant, Position); + #[derive(Default)] struct HighlevelSignature { /// this is a constructor or destructor without a written type @@ -53,6 +56,8 @@ struct Includes { needs_exported_resources: bool, needs_variant: bool, needs_tuple: bool, + // needs wit types + needs_wit: bool, } #[derive(Clone)] @@ -741,6 +746,7 @@ impl CppInterfaceGenerator<'_> { from_namespace: &Vec, ) -> HighlevelSignature { let mut res = HighlevelSignature::default(); + let abi_variant = if import ^ self.gen.opts.host { AbiVariant::GuestImport} else {AbiVariant::GuestExport}; let (namespace, func_name_h) = self.func_namespace_name(func, !(import ^ self.gen.opts.host)); res.name = func_name_h; @@ -758,7 +764,7 @@ impl CppInterfaceGenerator<'_> { } } wit_bindgen_core::wit_parser::Results::Anon(ty) => { - res.result = self.type_name(ty, from_namespace); + res.result = self.type_name(ty, from_namespace, (abi_variant, Position::Result)); } } } @@ -771,7 +777,7 @@ impl CppInterfaceGenerator<'_> { continue; } res.arguments - .push((name.to_snake_case(), self.type_name(param, &res.namespace))); + .push((name.to_snake_case(), self.type_name(param, &res.namespace, (abi_variant, Position::Argument)))); } // default to non-const when exporting a method if matches!(func.kind, FunctionKind::Method(_)) && import { @@ -963,9 +969,9 @@ impl CppInterfaceGenerator<'_> { } // in C this is print_optional_ty - fn optional_type_name(&mut self, ty: Option<&Type>, from_namespace: &Vec) -> String { + fn optional_type_name(&mut self, ty: Option<&Type>, from_namespace: &Vec, flavor: Flavor) -> String { match ty { - Some(ty) => self.type_name(ty, from_namespace), + Some(ty) => self.type_name(ty, from_namespace, flavor), None => "void".into(), } } @@ -983,7 +989,7 @@ impl CppInterfaceGenerator<'_> { ) } - fn type_name(&mut self, ty: &Type, from_namespace: &Vec) -> String { + fn type_name(&mut self, ty: &Type, from_namespace: &Vec, flavor: Flavor) -> String { match ty { Type::Bool => "bool".into(), Type::Char => "uint32_t".into(), @@ -998,18 +1004,23 @@ impl CppInterfaceGenerator<'_> { Type::Float32 => "float".into(), Type::Float64 => "double".into(), Type::String => { - self.gen.dependencies.needs_string = true; - "std::string".into() + if flavor == (AbiVariant::GuestImport, Position::Argument) { + self.gen.dependencies.needs_string_view = true; + "std::string_view".into() + } else { + 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), TypeDefKind::Resource => self.scoped_type_name(*id, from_namespace), TypeDefKind::Handle(Handle::Own(id)) => { - self.type_name(&Type::Id(*id), from_namespace) + self.type_name(&Type::Id(*id), from_namespace, flavor) } TypeDefKind::Handle(Handle::Borrow(id)) => { "std::reference_wrapper<".to_string() - + &self.type_name(&Type::Id(*id), from_namespace) + + &self.type_name(&Type::Id(*id), from_namespace, flavor) + ">" } TypeDefKind::Flags(_f) => self.scoped_type_name(*id, from_namespace), @@ -1018,7 +1029,7 @@ impl CppInterfaceGenerator<'_> { if !a.is_empty() { a += ", "; } - a + &self.type_name(b, from_namespace) + a + &self.type_name(b, from_namespace, flavor) }); self.gen.dependencies.needs_tuple = true; String::from("std::tuple<") + &types + ">" @@ -1027,23 +1038,23 @@ impl CppInterfaceGenerator<'_> { TypeDefKind::Enum(_e) => self.scoped_type_name(*id, from_namespace), TypeDefKind::Option(o) => { self.gen.dependencies.needs_optional = true; - "std::optional<".to_string() + &self.type_name(o, from_namespace) + ">" + "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) + + &self.optional_type_name(r.ok.as_ref(), from_namespace, flavor) + ", " - + &self.optional_type_name(r.err.as_ref(), from_namespace) + + &self.optional_type_name(r.err.as_ref(), from_namespace, flavor) + ">" } TypeDefKind::List(ty) => { self.gen.dependencies.needs_vector = true; - "std::vector<".to_string() + &self.type_name(ty, from_namespace) + ">" + "std::vector<".to_string() + &self.type_name(ty, from_namespace, flavor) + ">" } TypeDefKind::Future(_) => todo!(), TypeDefKind::Stream(_) => todo!(), - TypeDefKind::Type(ty) => self.type_name(ty, from_namespace), + TypeDefKind::Type(ty) => self.type_name(ty, from_namespace, flavor), TypeDefKind::Unknown => todo!(), }, } @@ -1132,7 +1143,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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); + let typename = self.type_name(&field.ty, &namespc, (AbiVariant::GuestExport, Position::InStruct)); let fname = field.name.to_lower_camel_case(); uwriteln!(self.gen.h_src.src, "{typename} {fname};"); } From 8049762f4601ccfdc6a130d5a123d1e10a181d46 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 7 Feb 2024 23:41:11 +0100 Subject: [PATCH 050/672] prepare for different string types in different situations --- crates/cpp/src/lib.rs | 99 +++++++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 28 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 0fc2feaae..60516197d 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -25,8 +25,12 @@ pub const OWNED_CLASS_NAME: &str = "Owned"; type CppType = String; -enum Position { Argument, Result, InStruct } -type Flavor = (AbiVariant, Position); +#[derive(Clone, Copy, Debug)] +enum Flavor { + Argument(AbiVariant), + Result(AbiVariant), + InStruct, +} #[derive(Default)] struct HighlevelSignature { @@ -521,7 +525,9 @@ impl WorldGenerator for Cpp { // determine namespace (for the lifted C++ function) fn namespace(resolve: &Resolve, owner: &TypeOwner, export: bool) -> Vec { let mut result = Vec::default(); - if export { result.push(String::from("exports")); } + if export { + result.push(String::from("exports")); + } match owner { TypeOwner::World(w) => result.push(resolve.worlds[*w].name.to_snake_case()), TypeOwner::Interface(i) => { @@ -746,9 +752,14 @@ impl CppInterfaceGenerator<'_> { from_namespace: &Vec, ) -> HighlevelSignature { let mut res = HighlevelSignature::default(); - let abi_variant = if import ^ self.gen.opts.host { AbiVariant::GuestImport} else {AbiVariant::GuestExport}; + let abi_variant = if import ^ self.gen.opts.host { + AbiVariant::GuestImport + } else { + AbiVariant::GuestExport + }; - let (namespace, func_name_h) = self.func_namespace_name(func, !(import ^ self.gen.opts.host)); + let (namespace, func_name_h) = + self.func_namespace_name(func, !(import ^ self.gen.opts.host)); res.name = func_name_h; res.namespace = namespace; let is_drop = is_drop_method(func); @@ -764,7 +775,7 @@ impl CppInterfaceGenerator<'_> { } } wit_bindgen_core::wit_parser::Results::Anon(ty) => { - res.result = self.type_name(ty, from_namespace, (abi_variant, Position::Result)); + res.result = self.type_name(ty, from_namespace, Flavor::Result(abi_variant)); } } } @@ -776,8 +787,10 @@ impl CppInterfaceGenerator<'_> { res.implicit_self = true; continue; } - res.arguments - .push((name.to_snake_case(), self.type_name(param, &res.namespace, (abi_variant, Position::Argument)))); + 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 if matches!(func.kind, FunctionKind::Method(_)) && import { @@ -881,7 +894,11 @@ impl CppInterfaceGenerator<'_> { _ => panic!("drop should be static"), }]; self.gen.c_src.src.push_str(" "); - let mut namespace = namespace(self.resolve, &owner.owner, matches!(variant, AbiVariant::GuestExport)); + let mut namespace = namespace( + self.resolve, + &owner.owner, + matches!(variant, AbiVariant::GuestExport), + ); namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); self.gen.c_src.qualify(&namespace); uwriteln!(self.gen.c_src.src, "remove_resource({});", params[0]); @@ -899,7 +916,11 @@ impl CppInterfaceGenerator<'_> { } } else { let namespace = if matches!(func.kind, FunctionKind::Freestanding) { - namespace(self.resolve, &TypeOwner::Interface(interface), matches!(variant, AbiVariant::GuestExport)) + namespace( + self.resolve, + &TypeOwner::Interface(interface), + matches!(variant, AbiVariant::GuestExport), + ) } else { let owner = &self.resolve.types[match &func.kind { FunctionKind::Static(id) => *id, @@ -908,7 +929,11 @@ impl CppInterfaceGenerator<'_> { FunctionKind::Freestanding => unreachable!(), }] .clone(); - let mut namespace = namespace(self.resolve, &owner.owner, matches!(variant, AbiVariant::GuestExport)); + let mut namespace = namespace( + self.resolve, + &owner.owner, + matches!(variant, AbiVariant::GuestExport), + ); namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); namespace }; @@ -969,7 +994,12 @@ impl CppInterfaceGenerator<'_> { } // in C this is print_optional_ty - fn optional_type_name(&mut self, ty: Option<&Type>, from_namespace: &Vec, flavor: Flavor) -> String { + fn optional_type_name( + &mut self, + ty: Option<&Type>, + from_namespace: &Vec, + flavor: Flavor, + ) -> String { match ty { Some(ty) => self.type_name(ty, from_namespace, flavor), None => "void".into(), @@ -1004,7 +1034,7 @@ impl CppInterfaceGenerator<'_> { Type::Float32 => "float".into(), Type::Float64 => "double".into(), Type::String => { - if flavor == (AbiVariant::GuestImport, Position::Argument) { + if matches!(flavor, Flavor::Argument(AbiVariant::GuestImport)) { self.gen.dependencies.needs_string_view = true; "std::string_view".into() } else { @@ -1143,7 +1173,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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, (AbiVariant::GuestExport, Position::InStruct)); + let typename = self.type_name(&field.ty, &namespc, Flavor::InStruct); let fname = field.name.to_lower_camel_case(); uwriteln!(self.gen.h_src.src, "{typename} {fname};"); } @@ -1308,7 +1338,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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); + let typestr = self.type_name(ty, &namespc, Flavor::InStruct); uwrite!(self.gen.h_src.src, " {typestr} value; ") } uwriteln!(self.gen.h_src.src, "}};"); @@ -1375,7 +1405,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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); + let typename = self.type_name(alias_type, &namespc, Flavor::InStruct); uwriteln!(self.gen.h_src.src, "using {pascal} = {typename};"); } @@ -1665,7 +1695,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let tmp = self.tmp(); let size = self.gen.sizes.size(element); let _align = self.gen.sizes.align(element); - let vtype = self.gen.type_name(element, &self.namespace); + let vtype = self + .gen + .type_name(element, &self.namespace, Flavor::InStruct); let len = format!("len{tmp}"); let base = format!("base{tmp}"); let result = format!("result{tmp}"); @@ -1749,7 +1781,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { &(tuple .types .iter() - .map(|t| self.gen.type_name(t, &self.namespace))) + .map(|t| self.gen.type_name(t, &self.namespace, Flavor::InStruct))) .collect::>() .join(", "), ); @@ -1807,7 +1839,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { { uwriteln!(self.src, "case {}: {{", i); if let Some(ty) = case.ty.as_ref() { - let ty = self.gen.type_name(ty, &self.namespace); + let ty = self.gen.type_name(ty, &self.namespace, Flavor::InStruct); uwrite!( self.src, "const {} *{} = &({}).val", @@ -1888,13 +1920,19 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } 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); + let typename = + self.gen + .type_name(&Type::Id(*ty), &self.namespace, Flavor::InStruct); results.push(format!("({typename}){}", &operands[0])); } abi::Instruction::OptionLower { .. } => self.push_str("OptionLower"), abi::Instruction::OptionLift { payload, .. } => { let mut result: String = "std::optional<".into(); - result.push_str(&self.gen.type_name(*payload, &self.namespace)); + result.push_str( + &self + .gen + .type_name(*payload, &self.namespace, Flavor::InStruct), + ); result.push_str(">("); result.push_str(&operands[0]); result.push(')'); @@ -1927,12 +1965,16 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } else { err = format!("std::move({err})"); } - let ok_type = self - .gen - .optional_type_name(result.ok.as_ref(), &self.namespace); - let err_type = self - .gen - .optional_type_name(result.err.as_ref(), &self.namespace); + 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 type_name = format!("std::expected<{ok_type}, {err_type}>",); let err_type = "std::unexpected"; let operand = &operands[0]; @@ -2006,7 +2048,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::CallInterface { func } => { // dbg!(func); self.let_results(func.results.len(), results); - let (mut namespace, func_name_h) = self.gen.func_namespace_name(func, !self.gen.gen.opts.host); + let (mut namespace, func_name_h) = + self.gen.func_namespace_name(func, !self.gen.gen.opts.host); if matches!(func.kind, FunctionKind::Method(_)) { let this = operands.remove(0); //self.gen.gen.c_src.qualify(&namespace); From 244cfc7fa6c2eafc6f9745bf1bb27c28189c4391 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 13 Feb 2024 22:59:57 +0100 Subject: [PATCH 051/672] post-merge cleanup --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 982603eb3..cf7c80ee6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2399,7 +2399,7 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder 0.41.0", + "wasm-encoder 0.41.2", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", From 73a464ebe77d530224abbee4f468241ff1c7d9ed Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 13 Feb 2024 23:54:38 +0100 Subject: [PATCH 052/672] revised runtime env --- crates/cpp/helper-types/wit-guest.h | 31 +++++++++++++++ crates/cpp/helper-types/wit-host.h | 61 +++++++++++++++++++++++++++++ crates/cpp/helper-types/wit-rt.h | 24 ------------ 3 files changed, 92 insertions(+), 24 deletions(-) create mode 100644 crates/cpp/helper-types/wit-guest.h create mode 100644 crates/cpp/helper-types/wit-host.h delete mode 100644 crates/cpp/helper-types/wit-rt.h diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h new file mode 100644 index 000000000..28ea6a490 --- /dev/null +++ b/crates/cpp/helper-types/wit-guest.h @@ -0,0 +1,31 @@ +#include +#include +#include + +namespace wit { + class string { + uint8_t const* data; + size_t length; + public: + string(string const&) = delete; + string(string&&b) : data(b.data), length(b.length) { + b.data=nullptr; + } + string& operator=(string const&) = delete; + string& operator=(string &&b) { + if (data) {free(const_cast(data));} + data=b.data; + length=b.length; + b.data=nullptr; + return *this; + } + ~string() { + if (data) { + free(const_cast(data)); + } + } + std::string_view get_view() const { + return std::string_view((const char*)data, length); + } + }; +} diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h new file mode 100644 index 000000000..26872e2aa --- /dev/null +++ b/crates/cpp/helper-types/wit-host.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include +#include + +#ifndef WIT_HOST_DIRECT +#define WIT_HOST_WAMR +#endif + +#ifdef WIT_HOST_DIRECT +# define WIT_WASI64 +#endif + +namespace wit { +#ifdef WIT_WASI64 + typedef uint64_t guest_address; + typedef uint64_t guest_size; +#else + typedef uint32_t guest_address; + typedef uint32_t guest_size; +#endif + +#ifdef WIT_HOST_WAMR + typedef void* guest_instance; +#elif defined(WIT_HOST_DIRECT) + typedef int guest_instance; +#endif + typedef void (*guest_cabi_post_t)(guest_instance, guest_address); + typedef void* (*from_guest_address_t)(guest_instance, guest_address); + + // host code never de-allocates directly + class string { + guest_address data; + guest_size length; + public: + // string(string const&) = default; + // string(string&&b) = default; + // string& operator=(string const&) = default; + // string& operator=(string &&b) = default; + // ~string() {} + std::string_view get_view(from_guest_address_t conv, guest_instance inst) const { + return std::string_view((char const*)(*conv)(inst, data), length); + } + }; + + template + class guest_owned { + guest_address data; + guest_cabi_post_t free_func; + from_guest_address_t conv_func; + guest_instance instance; + public: + T const* operator->() const { + return (T const*)(*conv_func)(instance, data); + } + T* operator->() { + return (T*)(*conv_func)(instance, data); + } + }; +} diff --git a/crates/cpp/helper-types/wit-rt.h b/crates/cpp/helper-types/wit-rt.h deleted file mode 100644 index 141a27517..000000000 --- a/crates/cpp/helper-types/wit-rt.h +++ /dev/null @@ -1,24 +0,0 @@ -#include -#include -#include - -namespace wit { - class string : public std::string_view { - uint8_t * owned; - public: - string(string const&) = delete; - string(string&&b) : std::string_view(b.data(), b.length()), owned(b.owned) { - b.owned=nullptr; - } - string& operator=(string const&) = delete; - string& operator=(string &&b) { - if (owned) {free(owned);} - *static_cast(this) = b; - owned=b.owned; - b.owned=nullptr; - } - ~string() { - free(owned); - } - }; -} From 7b599ce93475d8722c851e846ebe891a65b4132e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 14 Feb 2024 20:42:52 +0100 Subject: [PATCH 053/672] version increase --- Cargo.lock | 2 +- crates/cpp/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b9c3b5e0f..3483d2e8a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2392,7 +2392,7 @@ dependencies = [ [[package]] name = "wit-bindgen-cpp" -version = "0.2.0" +version = "0.3.0" dependencies = [ "anyhow", "clap", diff --git a/crates/cpp/Cargo.toml b/crates/cpp/Cargo.toml index 16482c718..38102ee58 100644 --- a/crates/cpp/Cargo.toml +++ b/crates/cpp/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "wit-bindgen-cpp" authors = ["Christof Petig "] -version = "0.2.0" +version = "0.3.0" edition.workspace = true repository = 'https://github.com/cpetig/wit-bindgen' license = "Apache-2.0 WITH LLVM-exception" From cadaae6fa4f4a73914baa6fe5cd9f436650d24a2 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 14 Feb 2024 21:17:02 +0100 Subject: [PATCH 054/672] strings test guest passes 4/6 --- crates/cpp/helper-types/wit-guest.h | 21 ++++++++++++--------- crates/cpp/helper-types/wit-host.h | 10 +++++----- crates/cpp/src/lib.rs | 9 ++++++++- crates/cpp/test_headers/wit-guest.h | 1 + crates/cpp/test_headers/wit-host.h | 1 + 5 files changed, 27 insertions(+), 15 deletions(-) create mode 120000 crates/cpp/test_headers/wit-guest.h create mode 120000 crates/cpp/test_headers/wit-host.h diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 28ea6a490..047eedc2b 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -4,28 +4,31 @@ namespace wit { class string { - uint8_t const* data; + uint8_t const* data_; size_t length; public: string(string const&) = delete; - string(string&&b) : data(b.data), length(b.length) { - b.data=nullptr; + string(string&&b) : data_(b.data_), length(b.length) { + b.data_=nullptr; } string& operator=(string const&) = delete; string& operator=(string &&b) { - if (data) {free(const_cast(data));} - data=b.data; + if (data_) {free(const_cast(data_));} + data_=b.data_; length=b.length; - b.data=nullptr; + 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; } ~string() { - if (data) { - free(const_cast(data)); + if (data_) { + free(const_cast(data_)); } } std::string_view get_view() const { - return std::string_view((const char*)data, length); + return std::string_view((const char*)data_, length); } }; } diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 26872e2aa..79379d710 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -31,7 +31,7 @@ namespace wit { // host code never de-allocates directly class string { - guest_address data; + guest_address data_; guest_size length; public: // string(string const&) = default; @@ -40,22 +40,22 @@ namespace wit { // string& operator=(string &&b) = default; // ~string() {} std::string_view get_view(from_guest_address_t conv, guest_instance inst) const { - return std::string_view((char const*)(*conv)(inst, data), length); + return std::string_view((char const*)(*conv)(inst, data_), length); } }; template class guest_owned { - guest_address data; + guest_address data_; guest_cabi_post_t free_func; from_guest_address_t conv_func; guest_instance instance; public: T const* operator->() const { - return (T const*)(*conv_func)(instance, data); + return (T const*)(*conv_func)(instance, data_); } T* operator->() { - return (T*)(*conv_func)(instance, data); + return (T*)(*conv_func)(instance, data_); } }; } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 60516197d..a2152e769 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -344,6 +344,13 @@ impl WorldGenerator for Cpp { if self.dependencies.needs_tuple { self.include(""); } + if self.dependencies.needs_wit { + if self.opts.host || self.opts.short_cut { + self.include(""); + } else { + self.include(""); + } + } for include in self.includes.iter() { uwriteln!(h_str.src, "#include {include}"); @@ -1687,7 +1694,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let tmp = self.tmp(); let len = format!("len{}", tmp); uwriteln!(self.src, "auto {} = {};\n", len, operands[1]); - let result = format!("std::string((char const*)({}), {len})", operands[0]); + let result = format!("wit::string((char const*)({}), {len})", operands[0]); results.push(result); } abi::Instruction::ListLift { element, .. } => { 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 From 16bf0b0b8fac8722c31aad8a6d535971b86d92e5 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 14 Feb 2024 22:22:32 +0100 Subject: [PATCH 055/672] post return for strings --- crates/cpp/helper-types/wit-guest.h | 4 ++ crates/cpp/src/lib.rs | 59 ++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 047eedc2b..4dece60f8 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -27,6 +27,10 @@ namespace wit { free(const_cast(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); } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index a2152e769..742e043e2 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -954,6 +954,44 @@ impl CppInterfaceGenerator<'_> { self.gen.c_src.src.push_str(&code); } self.gen.c_src.src.push_str("}\n"); + // cabi_post + if !self.gen.opts.host + && matches!(variant, AbiVariant::GuestExport) + && abi::guest_export_needs_post_return(self.resolve, func) + { + let sig = self.resolve.wasm_signature(variant, func); + let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + let export_name = func.core_export_name(Some(&module_name)); + let import_name = CppInterfaceGenerator::export_name2(&module_name, &func.name); + uwriteln!( + self.gen.c_src.src, + "__attribute__((__weak__, __export_name__(\"cabi_post_{export_name}\")))" + ); + uwrite!(self.gen.c_src.src, "void {import_name}_post_return("); + + let mut params = Vec::new(); + // let mut c_sig = CSig { + // name: String::from("INVALID"), + // sig: String::from("INVALID"), + // params: Vec::new(), + // ret: Return::default(), + // retptrs: Vec::new(), + // }; + for (i, result) in sig.results.iter().enumerate() { + let name = format!("arg{i}"); + uwrite!(self.gen.c_src.src, "{} {name}", wasm_type(*result)); + // c_sig.params.push((false, name.clone())); + params.push(name); + } + 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); + 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 { @@ -1671,11 +1709,16 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if realloc.is_none() { results.push(ptr); } else { - self.gen.gen.dependencies.needs_guest_alloc = true; - self.gen.gen.dependencies.needs_cstring = true; - uwriteln!(self.src, "int32_t {result} = guest_alloc(exec_env, {len});"); - uwriteln!(self.src, "memcpy(wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), {result}), {ptr}, {len});"); - results.push(result); + if self.gen.gen.opts.host { + self.gen.gen.dependencies.needs_guest_alloc = true; + self.gen.gen.dependencies.needs_cstring = true; + uwriteln!(self.src, "int32_t {result} = guest_alloc(exec_env, {len});"); + uwriteln!(self.src, "memcpy(wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), {result}), {ptr}, {len});"); + results.push(result); + } else { + uwriteln!(self.src, "{}.leak();\n", operands[0]); + results.push(ptr); + } } results.push(len); } @@ -2103,7 +2146,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } abi::Instruction::Malloc { .. } => todo!(), abi::Instruction::GuestDeallocate { .. } => todo!(), - abi::Instruction::GuestDeallocateString => todo!(), + 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 { .. } => todo!(), abi::Instruction::GuestDeallocateVariant { .. } => todo!(), } From d5422f9f81734dc7768c63943665b90c1cf184e1 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 14 Feb 2024 22:54:55 +0100 Subject: [PATCH 056/672] proper move of string args (guest) --- crates/cpp/helper-types/wit-host.h | 1 + crates/cpp/src/lib.rs | 27 ++++++++++++++------------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 79379d710..6f62d48b0 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -42,6 +42,7 @@ namespace wit { std::string_view get_view(from_guest_address_t conv, guest_instance inst) const { return std::string_view((char const*)(*conv)(inst, data_), length); } + string(guest_address a, guest_size s) : data_(a), length(s) {} }; template diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 742e043e2..0b6bc12b8 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -970,17 +970,9 @@ impl CppInterfaceGenerator<'_> { uwrite!(self.gen.c_src.src, "void {import_name}_post_return("); let mut params = Vec::new(); - // let mut c_sig = CSig { - // name: String::from("INVALID"), - // sig: String::from("INVALID"), - // params: Vec::new(), - // ret: Return::default(), - // retptrs: Vec::new(), - // }; for (i, result) in sig.results.iter().enumerate() { let name = format!("arg{i}"); uwrite!(self.gen.c_src.src, "{} {name}", wasm_type(*result)); - // c_sig.params.push((false, name.clone())); params.push(name); } self.gen.c_src.src.push_str(") {\n"); @@ -1078,15 +1070,20 @@ impl CppInterfaceGenerator<'_> { Type::S64 => "int64_t".into(), Type::Float32 => "float".into(), Type::Float64 => "double".into(), - Type::String => { - if matches!(flavor, Flavor::Argument(AbiVariant::GuestImport)) { + Type::String => match flavor { + Flavor::Argument(AbiVariant::GuestImport) => { self.gen.dependencies.needs_string_view = true; "std::string_view".into() - } else { + } + Flavor::Argument(AbiVariant::GuestExport) => { + self.gen.dependencies.needs_wit = true; + "wit::string &&".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), TypeDefKind::Resource => self.scoped_type_name(*id, from_namespace), @@ -2148,7 +2145,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::GuestDeallocate { .. } => todo!(), abi::Instruction::GuestDeallocateString => { uwriteln!(self.src, "if (({}) > 0) {{", operands[1]); - uwriteln!(self.src, "wit::string::drop_raw((void*) ({}));", operands[0]); + uwriteln!( + self.src, + "wit::string::drop_raw((void*) ({}));", + operands[0] + ); uwriteln!(self.src, "}}"); } abi::Instruction::GuestDeallocateList { .. } => todo!(), From 68cefbfd8f4825a47e341b37a0ccc27f9e169765 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 15 Feb 2024 00:24:53 +0100 Subject: [PATCH 057/672] more ideas for native --- crates/cpp/DESIGN.md | 2 +- crates/cpp/helper-types/wit-host.h | 72 +++++++++++++++++++-------- crates/cpp/src/lib.rs | 4 ++ crates/cpp/test_headers/wasm_export.h | 4 +- 4 files changed, 60 insertions(+), 22 deletions(-) diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md index 304d2a6d3..89403bfa0 100644 --- a/crates/cpp/DESIGN.md +++ b/crates/cpp/DESIGN.md @@ -37,6 +37,6 @@ | HIA | v | string | | string_view | a,l | | HIR | t | string | | wit::string | &(a,l) | | HEA | t | string | | wit::string&& | a,l | -| HER | p | string | | string_view + special cleanup | -> &(a,l) | +| HER | p | string | | string_view ? + special cleanup | -> &(a,l) | Note: The host never frees memory (is never passed ownership)! diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 6f62d48b0..30316cd58 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -20,43 +20,75 @@ namespace wit { typedef uint32_t guest_address; typedef uint32_t guest_size; #endif +} #ifdef WIT_HOST_WAMR - typedef void* guest_instance; -#elif defined(WIT_HOST_DIRECT) - typedef int guest_instance; +# include #endif - typedef void (*guest_cabi_post_t)(guest_instance, guest_address); - typedef void* (*from_guest_address_t)(guest_instance, guest_address); + +namespace wit { + typedef void (*guest_cabi_post_t)(WASMExecEnv*, guest_address); + typedef guest_address (*guest_alloc_t)(WASMExecEnv*, guest_size size, guest_size align); // host code never de-allocates directly class string { guest_address data_; guest_size length; public: - // string(string const&) = default; - // string(string&&b) = default; - // string& operator=(string const&) = default; - // string& operator=(string &&b) = default; - // ~string() {} - std::string_view get_view(from_guest_address_t conv, guest_instance inst) const { - return std::string_view((char const*)(*conv)(inst, data_), length); +#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) {} + // add a convenient way to create a string }; template - class guest_owned { + class guest_owned : public T { guest_address data_; - guest_cabi_post_t free_func; - from_guest_address_t conv_func; - guest_instance instance; +#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: - T const* operator->() const { - return (T const*)(*conv_func)(instance, data_); + 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(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 + } } - T* operator->() { - return (T*)(*conv_func)(instance, data_); + 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_ = nullptr; } + 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 + {} }; } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 0b6bc12b8..51e3cdc6c 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1079,6 +1079,10 @@ impl CppInterfaceGenerator<'_> { self.gen.dependencies.needs_wit = true; "wit::string &&".into() } + Flavor::Result(AbiVariant::GuestExport) if self.gen.opts.host => { + self.gen.dependencies.needs_string_view = true; + "std::string_view".into() + } _ => { self.gen.dependencies.needs_wit = true; "wit::string".into() diff --git a/crates/cpp/test_headers/wasm_export.h b/crates/cpp/test_headers/wasm_export.h index 13eec73c1..e37c4ae5a 100644 --- a/crates/cpp/test_headers/wasm_export.h +++ b/crates/cpp/test_headers/wasm_export.h @@ -1,8 +1,10 @@ +#pragma once // minimal WAMR header mock-up for compilation tests #include struct WASMExecEnv; typedef WASMExecEnv* wasm_exec_env_t; -typedef void* wasm_module_inst_t; +struct WASMModuleInstanceCommon; +typedef WASMModuleInstanceCommon* wasm_module_inst_t; typedef void* wasm_function_inst_t; typedef uint8_t wasm_valkind_t; enum wasm_valkind_enum { From 1d59660060e19542e74d1fad74afc472af0577ae Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 17 Feb 2024 09:55:00 +0100 Subject: [PATCH 058/672] post-merge fix --- crates/cpp/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 51e3cdc6c..b9d55aae5 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -284,7 +284,7 @@ impl WorldGenerator for Cpp { todo!() } - fn finish(&mut self, resolve: &Resolve, world_id: WorldId, files: &mut Files) { + 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(); @@ -526,6 +526,7 @@ impl WorldGenerator for Cpp { for (name, content) in self.user_class_files.iter() { files.push(&name, content.as_bytes()); } + Ok(()) } } From 97b59907e1e366b56b48191756a28f2c409dc9f5 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 17 Feb 2024 11:05:05 +0100 Subject: [PATCH 059/672] more host design decisions, accept direct as an argument --- crates/cpp/DESIGN.md | 21 ++++++++++++++------- crates/cpp/helper-types/wit-host.h | 8 ++++++++ crates/cpp/src/lib.rs | 16 +++++++++++++--- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md index 89403bfa0..8f49e77b8 100644 --- a/crates/cpp/DESIGN.md +++ b/crates/cpp/DESIGN.md @@ -16,17 +16,16 @@ | t | owernership transferred | | p | cabi_post_ cleans up | - | Code | mode | WIT Type | Rust type | C++ Type | Lower | Reason | | --- | --- | --- | --- | --- | --- | --- | -| GIA | v | string | &str | string_view (17) | addr, len | | +| GIA | v | string | &str[^1] | string_view (17) | addr, len | | | | | list | &[T] | span (20) | addr, len | | | | | tuple | (...) | std::tuple | 0, 1, ...| | | | | tuple | (&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 | Result<&str, &[]> | std::expected | d,a,l | -| GIR | t | string | String | wit::string | &(addr, len) | | +| GIR | t | string | String | wit::string[^2] | &(addr, len) | | | | | list | Vec | wit::vector | &(a,l) | | | | result | Result | std::expected | &(d,a,l) | | GEA | t | string | String | wit::string&& | addr, len | @@ -35,8 +34,16 @@ | | | result | Result | std::expected | -> &(d,a,l) cabi_post | | --S | ? | string | String | wit::string | addr, len | | HIA | v | string | | string_view | a,l | -| HIR | t | string | | wit::string | &(a,l) | -| HEA | t | string | | wit::string&& | a,l | -| HER | p | string | | string_view ? + special cleanup | -> &(a,l) | +| HIR | t | string | | wit::string[^3] | &(a,l) | +| HEA | t | string | | wit::string[^4] | a,l | +| HER | p | string | | wit::guest_owned | -> &(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). -Note: The host never frees memory (is never passed ownership)! +[^4]: A host side wit::string doesn't own the data (not free in dtor), thus no move semantics. diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 30316cd58..299c7afa4 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -90,5 +90,13 @@ namespace wit { , exec_env(e) #endif {} + +#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 }; } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index b9d55aae5..6d06f7bef 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -98,7 +98,7 @@ pub struct Opts { #[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()))] + #[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()))] @@ -284,7 +284,12 @@ impl WorldGenerator for Cpp { todo!() } - fn finish(&mut self, resolve: &Resolve, world_id: WorldId, files: &mut Files) -> std::result::Result<(), anyhow::Error> { + 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(); @@ -1739,7 +1744,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let tmp = self.tmp(); let len = format!("len{}", tmp); uwriteln!(self.src, "auto {} = {};\n", len, operands[1]); - let result = format!("wit::string((char const*)({}), {len})", operands[0]); + let result = 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 { + format!("wit::string((char const*)({}), {len})", operands[0]) + }; results.push(result); } abi::Instruction::ListLift { element, .. } => { From 65526dbb2f0c7dd1c09a5cc02760f813c306fe50 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 17 Feb 2024 11:30:58 +0100 Subject: [PATCH 060/672] the string test passes --- crates/cpp/helper-types/wit-host.h | 2 ++ crates/cpp/src/lib.rs | 26 ++++++++++++++++++-------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 299c7afa4..91cb0e5a2 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -45,6 +45,8 @@ namespace wit { } #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 }; diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 6d06f7bef..7f3151ed5 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -824,7 +824,9 @@ impl CppInterfaceGenerator<'_> { } self.gen.h_src.src.push_str(&cpp_sig.name); self.gen.h_src.src.push_str("("); - if import && self.gen.opts.host { + if + /*import &&*/ + self.gen.opts.host { self.gen.h_src.src.push_str("WASMExecEnv* exec_env"); if !cpp_sig.arguments.is_empty() { self.gen.h_src.src.push_str(", "); @@ -1706,7 +1708,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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 { + if self.gen.gen.opts.short_cut { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); self.push_str(&format!("auto {} = {}.size();\n", len, val)); } else { @@ -1717,15 +1719,15 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { results.push(ptr); } else { if self.gen.gen.opts.host { - self.gen.gen.dependencies.needs_guest_alloc = true; - self.gen.gen.dependencies.needs_cstring = true; - uwriteln!(self.src, "int32_t {result} = guest_alloc(exec_env, {len});"); - uwriteln!(self.src, "memcpy(wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), {result}), {ptr}, {len});"); - results.push(result); + // self.gen.gen.dependencies.needs_guest_alloc = true; + // self.gen.gen.dependencies.needs_cstring = true; + // uwriteln!(self.src, "int32_t {result} = guest_alloc(exec_env, {len});"); + // uwriteln!(self.src, "memcpy(wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), {result}), {ptr}, {len});"); + // results.push(result); } else { uwriteln!(self.src, "{}.leak();\n", operands[0]); - results.push(ptr); } + results.push(ptr); } results.push(len); } @@ -2135,6 +2137,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } self.src.push_str(&func_name_h); 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(", ")); self.push_str(");\n"); } From 93072fb3969395f8f049556b14f174f17e5e272a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 17 Feb 2024 11:33:49 +0100 Subject: [PATCH 061/672] omit move semantics on HEA string --- crates/cpp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7f3151ed5..1df58f8dc 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1083,7 +1083,7 @@ impl CppInterfaceGenerator<'_> { self.gen.dependencies.needs_string_view = true; "std::string_view".into() } - Flavor::Argument(AbiVariant::GuestExport) => { + Flavor::Argument(AbiVariant::GuestExport) if !self.gen.opts.host => { self.gen.dependencies.needs_wit = true; "wit::string &&".into() } From c72088ac0d0ac764bd5ebab9612df6602f7c4ed1 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 17 Feb 2024 12:08:06 +0100 Subject: [PATCH 062/672] more runtime implementation types --- crates/cpp/DESIGN.md | 4 +- crates/cpp/helper-types/wit-common.h | 76 ++++++++++++++ crates/cpp/helper-types/wit-guest.h | 71 ++++++------- crates/cpp/helper-types/wit-host.h | 138 +++++++++++++------------ crates/cpp/src/lib.rs | 146 +++++++++++++-------------- 5 files changed, 262 insertions(+), 173 deletions(-) create mode 100644 crates/cpp/helper-types/wit-common.h diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md index 8f49e77b8..c28119eb8 100644 --- a/crates/cpp/DESIGN.md +++ b/crates/cpp/DESIGN.md @@ -19,7 +19,7 @@ | Code | mode | WIT Type | Rust type | C++ Type | Lower | Reason | | --- | --- | --- | --- | --- | --- | --- | | GIA | v | string | &str[^1] | string_view (17) | addr, len | | -| | | list | &[T] | span (20) | addr, len | | +| | | list | &[T] | wit::span [^5] | addr, len | | | | | tuple | (...) | std::tuple | 0, 1, ...| | | | | tuple | (&str, &[T]) | std::tuple<...> | a,l,a,l | | | | record{string, list} | &T | T const& | a,l,a,l | @@ -47,3 +47,5 @@ Complex (non-POD) struct elements on the host will need exec_env to decode or co [^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. diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h new file mode 100644 index 000000000..a17222463 --- /dev/null +++ b/crates/cpp/helper-types/wit-common.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include +#if __cplusplus > 202001L +#include +#endif + +namespace wit { +#if __cplusplus > 202001L +#else +// minimal implementation to get things going +template 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; } + T const &operator[](size_t index) { return address[index]; } +}; +#endif + +class ResourceImportBase { + static const int32_t invalid = -1; + +protected: + int32_t handle; + +public: + ResourceImportBase(int32_t h = invalid) : handle(h) {} + ResourceImportBase(ResourceImportBase &&r) : handle(r.handle) { + r.handle = invalid; + } + ResourceImportBase(ResourceImportBase const &) = delete; + void set_handle(int32_t h) { handle = h; } + int32_t get_handle() const { return handle; } + int32_t into_handle() { + int32_t h = handle; + handle = invalid; + return h; + } + ResourceImportBase &operator=(ResourceImportBase &&r) { + assert(handle < 0); + handle = r.handle; + r.handle = invalid; + return *this; + } + ResourceImportBase &operator=(ResourceImportBase const &r) = delete; +}; + +template class ResourceExportBase { + static std::map 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(id, std::move(value))); + return id; + } + static void remove_resource(int32_t id) { resources.erase(id); } +}; +template struct Owned { + T *ptr; +}; +} // namespace wit diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 4dece60f8..e5b7fc543 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -1,38 +1,39 @@ -#include -#include #include +#include +#include namespace wit { - class string { - uint8_t const* data_; - size_t length; - public: - string(string const&) = delete; - string(string&&b) : data_(b.data_), length(b.length) { - b.data_=nullptr; - } - string& operator=(string const&) = delete; - string& operator=(string &&b) { - if (data_) {free(const_cast(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; } - ~string() { - if (data_) { - free(const_cast(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); - } - }; -} +class string { + uint8_t const *data_; + size_t length; + +public: + string(string const &) = delete; + string(string &&b) : data_(b.data_), length(b.length) { b.data_ = nullptr; } + string &operator=(string const &) = delete; + string &operator=(string &&b) { + if (data_) { + free(const_cast(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; } + ~string() { + if (data_) { + free(const_cast(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); + } +}; +} // namespace wit diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 91cb0e5a2..c829aee46 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -1,104 +1,114 @@ #pragma once -#include -#include #include +#include +#include #ifndef WIT_HOST_DIRECT #define WIT_HOST_WAMR #endif #ifdef WIT_HOST_DIRECT -# define WIT_WASI64 +#define WIT_WASI64 #endif namespace wit { #ifdef WIT_WASI64 - typedef uint64_t guest_address; - typedef uint64_t guest_size; +typedef uint64_t guest_address; +typedef uint64_t guest_size; #else - typedef uint32_t guest_address; - typedef uint32_t guest_size; +typedef uint32_t guest_address; +typedef uint32_t guest_size; #endif -} +} // namespace wit #ifdef WIT_HOST_WAMR -# include +#include #endif namespace wit { - typedef void (*guest_cabi_post_t)(WASMExecEnv*, guest_address); - typedef guest_address (*guest_alloc_t)(WASMExecEnv*, guest_size size, guest_size align); - - // host code never de-allocates directly - class string { - guest_address data_; - guest_size length; - public: +typedef void (*guest_cabi_post_t)(WASMExecEnv *, guest_address); +typedef guest_address (*guest_alloc_t)(WASMExecEnv *, guest_size size, + guest_size align); + +// 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); - } + 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); - } + 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 - }; + 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 +}; - template - class guest_owned : public T { - guest_address data_; +template class guest_owned : public T { + guest_address data_; #ifdef WIT_HOST_WAMR - wasm_function_inst_t free_func; - WASMExecEnv* exec_env; + wasm_function_inst_t free_func; + WASMExecEnv *exec_env; #elif defined(WIT_HOST_DIRECT) - void (*free_func)(guest_address); + void (*free_func)(guest_address); #endif - public: - guest_owned(guest_owned const&) = delete; - guest_owned& operator=(guest_owned const&) = delete; - ~guest_owned() { - if (data_) { +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(data_),}; - wasm_runtime_call_wasm_a(exec_env, free_func, 0, wasm_results, 1, wasm_args); + wasm_val_t *wasm_results = nullptr; + wasm_val_t wasm_args[1] = { + WASM_I32_VAL(data_), + }; + wasm_runtime_call_wasm_a(exec_env, free_func, 0, wasm_results, 1, + wasm_args); #elif defined(WIT_HOST_DIRECT) - (*free_func)(data_); + (*free_func)(data_); #endif - } - } - guest_owned(guest_owned&&b) : T(b), data_(b.data_), free_func(b.free_func) + } + } + guest_owned(guest_owned &&b) + : T(b), data_(b.data_), free_func(b.free_func) #ifdef WIT_HOST_WAMR - , exec_env(b.exec_env) + , + exec_env(b.exec_env) #endif - { - b.data_ = nullptr; - } - guest_owned(T&& t, guest_address a, + { + b.data_ = nullptr; + } + guest_owned(T &&t, guest_address a, #ifdef WIT_HOST_WAMR - wasm_function_inst_t f, - WASMExecEnv* e + wasm_function_inst_t f, WASMExecEnv *e #elif defined(WIT_HOST_DIRECT) - , void (*f)(guest_address) + , void (*f)(guest_address) #endif - ) : T(std::move(t)), data_(a), free_func(f) + ) + : T(std::move(t)), data_(a), free_func(f) #ifdef WIT_HOST_WAMR - , exec_env(e) + , + exec_env(e) #endif - {} + { + } #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; - // } + // 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 - }; -} +}; +} // namespace wit diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 1df58f8dc..455946240 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -388,76 +388,76 @@ impl WorldGenerator for Cpp { } if self.dependencies.needs_exported_resources { let world_name = &self.world; - uwriteln!(c_str.src, "template std::map {world_name}::{RESOURCE_EXPORT_BASE_CLASS_NAME}::resources;"); - } - } - - if self.dependencies.needs_exported_resources { - let namespace = namespace(resolve, &TypeOwner::World(world_id), false); - h_str.change_namespace(&namespace); - // this is export, not host - uwriteln!( - h_str.src, - "template - class {RESOURCE_EXPORT_BASE_CLASS_NAME} {{ - static std::map 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(id, std::move(value))); - return id; - }} - static void remove_resource(int32_t id) {{ - resources.erase(id); - }} - }}; - template struct {OWNED_CLASS_NAME} {{ - T *ptr; - }};" - ); - } - if self.dependencies.needs_imported_resources { - // somehow spaces get removed, newlines remain (problem occurs before const&) - // TODO: should into_handle become && ??? - let namespace = namespace(resolve, &TypeOwner::World(world_id), false); - h_str.change_namespace(&namespace); - uwriteln!( - h_str.src, - "class {RESOURCE_IMPORT_BASE_CLASS_NAME} {{ - static const int32_t invalid = -1; - protected: - int32_t handle; - public: - {RESOURCE_IMPORT_BASE_CLASS_NAME}(int32_t h=invalid) : handle(h) {{}} - {RESOURCE_IMPORT_BASE_CLASS_NAME}({RESOURCE_IMPORT_BASE_CLASS_NAME}&&r) - : handle(r.handle) {{ - r.handle=invalid; - }} - {RESOURCE_IMPORT_BASE_CLASS_NAME}({RESOURCE_IMPORT_BASE_CLASS_NAME} - const&) = delete; - void set_handle(int32_t h) {{ handle=h; }} - int32_t get_handle() const {{ return handle; }} - int32_t into_handle() {{ - int32_t h= handle; - handle= invalid; - return h; - }} - {RESOURCE_IMPORT_BASE_CLASS_NAME}& operator=({RESOURCE_IMPORT_BASE_CLASS_NAME}&&r) {{ - assert(handle<0); - handle= r.handle; - r.handle= invalid; - return *this; - }} - {RESOURCE_IMPORT_BASE_CLASS_NAME}& operator=({RESOURCE_IMPORT_BASE_CLASS_NAME} - const&r) = delete; - }};" - ); - } + uwriteln!(c_str.src, "template std::map wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}::resources;"); + } + } + + // if self.dependencies.needs_exported_resources { + // let namespace = namespace(resolve, &TypeOwner::World(world_id), false); + // h_str.change_namespace(&namespace); + // // this is export, not host + // uwriteln!( + // h_str.src, + // "template + // class {RESOURCE_EXPORT_BASE_CLASS_NAME} {{ + // static std::map 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(id, std::move(value))); + // return id; + // }} + // static void remove_resource(int32_t id) {{ + // resources.erase(id); + // }} + // }}; + // template struct {OWNED_CLASS_NAME} {{ + // T *ptr; + // }};" + // ); + // } + // if self.dependencies.needs_imported_resources { + // // somehow spaces get removed, newlines remain (problem occurs before const&) + // // TODO: should into_handle become && ??? + // let namespace = namespace(resolve, &TypeOwner::World(world_id), false); + // h_str.change_namespace(&namespace); + // uwriteln!( + // h_str.src, + // "class {RESOURCE_IMPORT_BASE_CLASS_NAME} {{ + // static const int32_t invalid = -1; + // protected: + // int32_t handle; + // public: + // {RESOURCE_IMPORT_BASE_CLASS_NAME}(int32_t h=invalid) : handle(h) {{}} + // {RESOURCE_IMPORT_BASE_CLASS_NAME}({RESOURCE_IMPORT_BASE_CLASS_NAME}&&r) + // : handle(r.handle) {{ + // r.handle=invalid; + // }} + // {RESOURCE_IMPORT_BASE_CLASS_NAME}({RESOURCE_IMPORT_BASE_CLASS_NAME} + // const&) = delete; + // void set_handle(int32_t h) {{ handle=h; }} + // int32_t get_handle() const {{ return handle; }} + // int32_t into_handle() {{ + // int32_t h= handle; + // handle= invalid; + // return h; + // }} + // {RESOURCE_IMPORT_BASE_CLASS_NAME}& operator=({RESOURCE_IMPORT_BASE_CLASS_NAME}&&r) {{ + // assert(handle<0); + // handle= r.handle; + // r.handle= invalid; + // return *this; + // }} + // {RESOURCE_IMPORT_BASE_CLASS_NAME}& operator=({RESOURCE_IMPORT_BASE_CLASS_NAME} + // const&r) = delete; + // }};" + // ); + // } h_str.change_namespace(&Vec::default()); self.c_src.change_namespace(&Vec::default()); @@ -1271,9 +1271,9 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } let base_type = if definition { - format!("{RESOURCE_EXPORT_BASE_CLASS_NAME}<{pascal}>") + format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}<{pascal}>") } else { - RESOURCE_IMPORT_BASE_CLASS_NAME.into() + std::String("wit::") + RESOURCE_IMPORT_BASE_CLASS_NAME }; let derive = format!(" : public {world_name}{base_type}"); uwriteln!(self.gen.h_src.src, "class {pascal}{derive} {{\n"); @@ -1311,7 +1311,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> // consuming constructor from handle (bindings) uwriteln!( self.gen.h_src.src, - "{pascal}({world_name}{RESOURCE_IMPORT_BASE_CLASS_NAME}&&);\n" + "{pascal}(wit::{RESOURCE_IMPORT_BASE_CLASS_NAME}&&);\n" ); uwriteln!(self.gen.h_src.src, "{pascal}({pascal}&&) = default;\n"); } From f821e07a26b739faa31e7c0f41a501bd45c7938f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 17 Feb 2024 16:44:57 +0100 Subject: [PATCH 063/672] list: guest header --- crates/cpp/helper-types/wit-common.h | 2 ++ crates/cpp/helper-types/wit-guest.h | 37 +++++++++++++++++++++ crates/cpp/src/lib.rs | 49 +++++++++++++++++++++++++--- crates/cpp/test_headers/wit-common.h | 1 + 4 files changed, 85 insertions(+), 4 deletions(-) create mode 120000 crates/cpp/test_headers/wit-common.h diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h index a17222463..c7ae6144c 100644 --- a/crates/cpp/helper-types/wit-common.h +++ b/crates/cpp/helper-types/wit-common.h @@ -2,12 +2,14 @@ #include #include +#include #if __cplusplus > 202001L #include #endif namespace wit { #if __cplusplus > 202001L +using std::span; #else // minimal implementation to get things going template class span { diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index e5b7fc543..b3f31daa6 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -1,6 +1,7 @@ #include #include #include +#include "wit-common.h" namespace wit { class string { @@ -36,4 +37,40 @@ class string { return std::string_view((const char *)data_, length); } }; + +template +class vector { + T *data_; + size_t length; + +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_) { + free(const_cast(data_)); + } + data_ = b.data_; + length = b.length; + b.data_ = nullptr; + return *this; + } + vector(T const *d, size_t l) : data_(d), length(l) {} + T const *data() const { return data_; } + T *data() { return data_; } + size_t size() const { return length; } + ~vector() { + if (data_) { + free(data_); + } + } + // leak the memory + void leak() { data_ = nullptr; } + // typically called by post + static void drop_raw(void *ptr) { free(ptr); } + wit::span get_view() const { + return wit::span(data_, length); + } +}; } // namespace wit diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 455946240..92e49a10b 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -4,6 +4,7 @@ use std::{ fmt::Write as FmtWrite, io::{Read, Write}, process::{Command, Stdio}, + str::FromStr, }; use wit_bindgen_c::{to_c_ident, wasm_type}; use wit_bindgen_core::{ @@ -1133,8 +1134,26 @@ impl CppInterfaceGenerator<'_> { + ">" } TypeDefKind::List(ty) => { - self.gen.dependencies.needs_vector = true; - "std::vector<".to_string() + &self.type_name(ty, from_namespace, flavor) + ">" + let inner = self.type_name(ty, from_namespace, flavor); + match flavor { + //self.gen.dependencies.needs_vector = true; + Flavor::Argument(AbiVariant::GuestImport) => { + self.gen.dependencies.needs_wit = true; + format!("wit::span<{inner}>") + } + 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}>") + } + _ => { + self.gen.dependencies.needs_wit = true; + format!("wit::vector<{inner}>") + } + } } TypeDefKind::Future(_) => todo!(), TypeDefKind::Stream(_) => todo!(), @@ -1273,7 +1292,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> let base_type = if definition { format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}<{pascal}>") } else { - std::String("wit::") + RESOURCE_IMPORT_BASE_CLASS_NAME + String::from_str("wit::").unwrap() + RESOURCE_IMPORT_BASE_CLASS_NAME }; let derive = format!(" : public {world_name}{base_type}"); uwriteln!(self.gen.h_src.src, "class {pascal}{derive} {{\n"); @@ -1522,6 +1541,10 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { ret } + fn tempname(&mut self, base: &str, idx: usize) -> String { + format!("{base}{idx}") + } + fn push_str(&mut self, s: &str) { self.src.push_str(s); } @@ -2177,7 +2200,25 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { ); uwriteln!(self.src, "}}"); } - abi::Instruction::GuestDeallocateList { .. } => todo!(), + 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, "int32_t {ptr} = {};", operands[0]); + uwriteln!(self.src, "int32_t {len} = {};", operands[1]); + let i = self.tempname("i", tmp); + uwriteln!(self.src, "for (int32_t {i} = 0; {i} < {len}; {i}++) {{"); + let size = self.gen.sizes.size(element); + uwriteln!(self.src, "int32_t base = {ptr} + {i} * {size};"); + 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 { .. } => todo!(), } } 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 From 075af208c39b2ceef4bfdf98d492d5e295c3c243 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 17 Feb 2024 17:05:33 +0100 Subject: [PATCH 064/672] list guest import working --- crates/cpp/src/lib.rs | 67 ++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 92e49a10b..4b88e104f 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1720,9 +1720,29 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::Float32FromF32 => top_as("float"), abi::Instruction::Float64FromF64 => top_as("double"), abi::Instruction::BoolFromI32 => top_as("bool"), - abi::Instruction::ListCanonLower { .. } => { - results.push("ListCanonLower.addr".into()); - results.push("ListCanonLower.len".into()); + 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.short_cut { + self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); + self.push_str(&format!("auto {} = {}.size();\n", len, val)); + } else { + self.push_str(&format!("auto {} = (int32_t)({}.data());\n", ptr, val)); + self.push_str(&format!("auto {} = (int32_t)({}.size());\n", len, val)); + } + if realloc.is_none() { + results.push(ptr); + } else { + if !self.gen.gen.opts.host { + uwriteln!(self.src, "{}.leak();\n", operands[0]); + } + results.push(ptr); + } + results.push(len); } abi::Instruction::StringLower { realloc } => { let tmp = self.tmp(); @@ -1741,28 +1761,43 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if realloc.is_none() { results.push(ptr); } else { - if self.gen.gen.opts.host { - // self.gen.gen.dependencies.needs_guest_alloc = true; - // self.gen.gen.dependencies.needs_cstring = true; - // uwriteln!(self.src, "int32_t {result} = guest_alloc(exec_env, {len});"); - // uwriteln!(self.src, "memcpy(wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), {result}), {ptr}, {len});"); - // results.push(result); - } else { + if !self.gen.gen.opts.host { uwriteln!(self.src, "{}.leak();\n", operands[0]); } results.push(ptr); } results.push(len); } - abi::Instruction::ListLower { .. } => { - results.push("ListLower1".into()); - results.push("ListLower2".into()); + abi::Instruction::ListLower { element, 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.short_cut { + self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); + self.push_str(&format!("auto {} = {}.size();\n", len, val)); + } else { + self.push_str(&format!("auto {} = (int32_t)({}.data());\n", ptr, val)); + self.push_str(&format!("auto {} = (int32_t)({}.size());\n", len, val)); + } + if realloc.is_none() { + results.push(ptr); + } else { + if !self.gen.gen.opts.host { + uwriteln!(self.src, "{}.leak();\n", operands[0]); + } + results.push(ptr); + } + results.push(len); } - abi::Instruction::ListCanonLift { .. } => { + abi::Instruction::ListCanonLift { element, .. } => { let tmp = self.tmp(); let len = format!("len{}", tmp); - self.push_str(&format!("let {} = {};\n", len, operands[1])); - let result = format!("std::vector<...>({0}, {0}+{1})", operands[0], len); + let inner = self.gen.type_name(element, &self.namespace, Flavor::InStruct); + self.push_str(&format!("auto {} = {};\n", len, operands[1])); + let result = format!("wit::vector<{2}>(({2} const *){0}, {1})", operands[0], len, inner); results.push(result); } abi::Instruction::StringLift => { From 6dff849e9241041b3e34bc2f97b2b0d36e6d37c4 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 17 Feb 2024 17:25:58 +0100 Subject: [PATCH 065/672] list Bindgen for FunctionBindgen<'a, 'b> { operand1 = operands[1] )); self.push_str(&format!( - r#"auto {result} = std::vector<{vtype}>(); - {result}.reserve({len}); + r#"auto {result} = wit::vector<{vtype}>(); + {result}.allocate({len}); "#, )); uwriteln!(self.src, "for (unsigned i=0; i<{len}; ++i) {{"); uwriteln!(self.src, "auto base = {base} + i * {size};"); uwriteln!(self.src, "auto e{tmp} = todo();"); + // inplace construct uwriteln!(self.src, "{result}.push_back(e{tmp});"); uwriteln!(self.src, "}}"); results.push(result); From 9ef750f6f3646649d5f29eebf35cdb5198d6281a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 17 Feb 2024 17:45:28 +0100 Subject: [PATCH 066/672] optional, work in progress --- crates/cpp/src/lib.rs | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 5575bec81..97e63d167 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1428,7 +1428,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> _payload: &wit_bindgen_core::wit_parser::Type, _docs: &wit_bindgen_core::wit_parser::Docs, ) { - todo!() + // I assume I don't need to do anything ... } fn type_result( @@ -1438,7 +1438,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> _result: &wit_bindgen_core::wit_parser::Result_, _docs: &wit_bindgen_core::wit_parser::Docs, ) { - todo!() + // I assume I don't need to do anything ... } fn type_enum( @@ -2048,7 +2048,40 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { .type_name(&Type::Id(*ty), &self.namespace, Flavor::InStruct); results.push(format!("({typename}){}", &operands[0])); } - abi::Instruction::OptionLower { .. } => self.push_str("OptionLower"), + 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(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 ty = self.gen.type_name(payload, &self.namespace, Flavor::InStruct); + let bind_some = format!("const {ty} *{some_payload} = &({op0}).val;"); + + uwrite!( + self.src, + "\ + if (({op0}).is_some) {{ + {bind_some} + {some}}} else {{ + {none}}} + " + ); + } abi::Instruction::OptionLift { payload, .. } => { let mut result: String = "std::optional<".into(); result.push_str( From e0b6d0c7b5708d491a5b17519bd5bbc3a4414026 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 17 Feb 2024 18:43:28 +0100 Subject: [PATCH 067/672] avoid defining structs twice --- crates/cpp/src/lib.rs | 61 +++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 97e63d167..9a4b47728 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -90,6 +90,7 @@ struct Cpp { world_id: Option, imported_interfaces: HashSet, user_class_files: HashMap, + defined_types: HashSet<(Vec, String)>, } #[derive(Default, Debug, Clone)] @@ -119,6 +120,16 @@ impl Cpp { Cpp::default() } + pub fn is_first_definition(&mut self, ns: &Vec, 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()); } @@ -615,6 +626,11 @@ struct CppInterfaceGenerator<'a> { pub wasm_import_module: Option, } +// I wish this was possible +// impl Equivalent<(Vec, String)> for (&Vec, &str) { + +// } + impl CppInterfaceGenerator<'_> { fn types(&mut self, iface: InterfaceId) { let iface = &self.resolve().interfaces[iface]; @@ -1240,17 +1256,19 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> ) { let ty = &self.resolve.types[id]; let namespc = namespace(self.resolve, &ty.owner, false); - 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_lower_camel_case(); - uwriteln!(self.gen.h_src.src, "{typename} {fname};"); + 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_lower_camel_case(); + uwriteln!(self.gen.h_src.src, "{typename} {fname};"); + } + uwriteln!(self.gen.h_src.src, "}};"); } - uwriteln!(self.gen.h_src.src, "}};"); } fn type_resource( @@ -1795,9 +1813,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::ListCanonLift { element, .. } => { let tmp = self.tmp(); let len = format!("len{}", tmp); - let inner = self.gen.type_name(element, &self.namespace, Flavor::InStruct); + let inner = self + .gen + .type_name(element, &self.namespace, Flavor::InStruct); self.push_str(&format!("auto {} = {};\n", len, operands[1])); - let result = format!("wit::vector<{2}>(({2} const *){0}, {1})", operands[0], len, inner); + let result = format!( + "wit::vector<{2}>(({2} const *){0}, {1})", + operands[0], len, inner + ); results.push(result); } abi::Instruction::StringLift => { @@ -2048,7 +2071,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { .type_name(&Type::Id(*ty), &self.namespace, Flavor::InStruct); results.push(format!("({typename}){}", &operands[0])); } - abi::Instruction::OptionLower { payload, results: result_types, .. } => { + 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(); @@ -2069,13 +2096,15 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } let op0 = &operands[0]; - let ty = self.gen.type_name(payload, &self.namespace, Flavor::InStruct); - let bind_some = format!("const {ty} *{some_payload} = &({op0}).val;"); + let ty = self + .gen + .type_name(payload, &self.namespace, Flavor::InStruct); + let bind_some = format!("{ty} {some_payload} = (std::move({op0})).value();"); uwrite!( self.src, "\ - if (({op0}).is_some) {{ + if (({op0}).has_value()) {{ {bind_some} {some}}} else {{ {none}}} From 2993df5c8396a4362c9d50ca121e657a69395a07 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 Feb 2024 18:11:39 +0100 Subject: [PATCH 068/672] more correct namespace qualification --- TODO.md | 6 +----- crates/cpp/src/lib.rs | 10 +++++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/TODO.md b/TODO.md index e27278c7a..7eb8b326d 100644 --- a/TODO.md +++ b/TODO.md @@ -61,8 +61,4 @@ # Cpp -* Use Resolve::wasm_signature ? - -* Reuse code between host and guest (RustFunctionGenerator like?) - -* Evaluate emit/call for host side code generation +* Start namespace at root within exports referring to normal structs diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 9a4b47728..7114ffbcf 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -596,6 +596,7 @@ impl SourceWithState { fn qualify(&mut self, target: &Vec) { let mut same = 0; + let mut subpart = false; // itertools::fold_while? for (a, b) in self.namespace.iter().zip(target.iter()) { if a == b { @@ -604,9 +605,12 @@ impl SourceWithState { break; } } - // if same == 0 { - // self.src.push_str("::"); - // } + 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}::"); } From 4b4d1cdc13b72d2cc16b1e958d85d304c7ac0538 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 Feb 2024 18:34:10 +0100 Subject: [PATCH 069/672] fix the first simple-option function --- crates/cpp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7114ffbcf..2c9fd7006 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1950,7 +1950,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::FlagsLift { .. } => results.push("FlagsLift".to_string()), abi::Instruction::VariantPayloadName => { let name = format!("payload{}", self.tmp()); - results.push(format!("*{}", name)); + results.push(name.clone()); //format!("*{}", name)); self.payloads.push(name); } abi::Instruction::VariantLower { From 7b48cb20e3beb148fc3bc8a482992badc6fbd143 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 Feb 2024 18:58:28 +0100 Subject: [PATCH 070/672] encode external names in a non-destructive way, exports still missing --- crates/cpp/src/lib.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 2c9fd7006..7bd5e7dcc 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1183,19 +1183,41 @@ impl CppInterfaceGenerator<'_> { } } + 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() + } + } + fn make_export_name(input: &str) -> String { input .chars() .map(|c| match c { - 'A'..='Z' | 'a'..='z' | '0'..='9' => 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_str("X").unwrap(); + s.push(Self::hexdigit((c as u32 & 0xf0) >> 4)); + s.push(Self::hexdigit(c as u32 & 0xf)); + s + } }) .collect() } fn export_name2(module_name: &str, name: &str) -> String { let mut res = Self::make_export_name(module_name); - res.push('_'); + res.push_str("X23"); // # character res.push_str(&Self::make_export_name(name)); res } From 247123d881df99087013b3b1ca287feec9e51fb0 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 Feb 2024 19:09:17 +0100 Subject: [PATCH 071/672] distinguish export and import names --- crates/cpp/src/lib.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7bd5e7dcc..9c4d9c37d 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -697,7 +697,7 @@ impl CppInterfaceGenerator<'_> { (namespace, func_name_h) } - // print the signature of the lowered (wasm) function calling into highlevel + // print the signature of the guest export (lowered (wasm) function calling into highlevel) fn print_export_signature(&mut self, func: &Function) -> Vec { let is_drop = is_drop_method(func); let signature = if is_drop { @@ -731,7 +731,8 @@ impl CppInterfaceGenerator<'_> { wasm_type(signature.results[0]) }); self.gen.c_src.src.push_str(" "); - let export_name = CppInterfaceGenerator::export_name2(&module_name, &func.name); + let export_name = + CppInterfaceGenerator::export_name2(&module_name, &func.name, AbiVariant::GuestExport); self.gen.c_src.src.push_str(&export_name); self.gen.c_src.src.push_str("("); let mut first_arg = true; @@ -991,12 +992,16 @@ impl CppInterfaceGenerator<'_> { let sig = self.resolve.wasm_signature(variant, func); let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); let export_name = func.core_export_name(Some(&module_name)); - let import_name = CppInterfaceGenerator::export_name2(&module_name, &func.name); + let import_name = CppInterfaceGenerator::export_name2( + &module_name, + &func.name, + AbiVariant::GuestExport, + ); uwriteln!( self.gen.c_src.src, "__attribute__((__weak__, __export_name__(\"cabi_post_{export_name}\")))" ); - uwrite!(self.gen.c_src.src, "void {import_name}_post_return("); + uwrite!(self.gen.c_src.src, "void cabi_post_{import_name}("); let mut params = Vec::new(); for (i, result) in sig.results.iter().enumerate() { @@ -1215,9 +1220,13 @@ impl CppInterfaceGenerator<'_> { .collect() } - fn export_name2(module_name: &str, name: &str) -> String { + fn export_name2(module_name: &str, name: &str, variant: AbiVariant) -> String { let mut res = Self::make_export_name(module_name); - res.push_str("X23"); // # character + res.push_str(if matches!(variant, AbiVariant::GuestExport) { + "X23" + } else { + "X00" + }); // NUL character res.push_str(&Self::make_export_name(name)); res } @@ -1228,7 +1237,7 @@ impl CppInterfaceGenerator<'_> { args: &str, result: &str, ) -> (String, String) { - let extern_name = Self::export_name2(module_name, name); + let extern_name = Self::export_name2(module_name, name, AbiVariant::GuestImport); let import = format!("extern __attribute__((import_module(\"{module_name}\")))\n __attribute__((import_name(\"{name}\")))\n {result} {extern_name}({args});\n"); (extern_name, import) } From 7d9b790d5f2bdde808196f12a7fbe44507a60054 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 Feb 2024 22:27:44 +0100 Subject: [PATCH 072/672] correct case for struct elements (to Google style) --- crates/cpp/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 9c4d9c37d..4e79f64a5 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1223,10 +1223,10 @@ impl CppInterfaceGenerator<'_> { fn export_name2(module_name: &str, name: &str, variant: AbiVariant) -> String { let mut res = Self::make_export_name(module_name); res.push_str(if matches!(variant, AbiVariant::GuestExport) { - "X23" + "X23" // Hash character } else { - "X00" - }); // NUL character + "X00" // NUL character (some tools use '.' for display) + }); res.push_str(&Self::make_export_name(name)); res } @@ -1299,7 +1299,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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_lower_camel_case(); + let fname = field.name.to_snake_case(); uwriteln!(self.gen.h_src.src, "{typename} {fname};"); } uwriteln!(self.gen.h_src.src, "}};"); From d8afbe8d47a6ab3cd130ae8df3e1a8b2efd0c371 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 Feb 2024 23:23:17 +0100 Subject: [PATCH 073/672] simple option works --- crates/cpp/src/lib.rs | 50 ++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 4e79f64a5..9360e4397 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1594,7 +1594,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { ret } - fn tempname(&mut self, base: &str, idx: usize) -> String { + fn tempname(&self, base: &str, idx: usize) -> String { format!("{base}{idx}") } @@ -1916,11 +1916,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } abi::Instruction::RecordLift { record, ty, .. } => { - let mut result = self.typename_lift(*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("), "); } result.push_str("}"); results.push(result); @@ -2147,16 +2150,39 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { ); } abi::Instruction::OptionLift { payload, .. } => { - let mut result: String = "std::optional<".into(); - result.push_str( - &self - .gen - .type_name(*payload, &self.namespace, Flavor::InStruct), + let (mut some, some_results) = self.blocks.pop().unwrap(); + let (mut none, none_results) = self.blocks.pop().unwrap(); + assert!(none_results.len() == 0); + assert!(some_results.len() == 1); + let some_result = &some_results[0]; + let type_name = self + .gen + .type_name(*payload, &self.namespace, Flavor::InStruct); + 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(std::move({})); + }}", + some_results[0] ); - result.push_str(">("); - result.push_str(&operands[0]); - result.push(')'); - results.push(result); + + // let mut result: String = "std::optional<".into(); + // result.push_str( + // &self + // .gen + // .type_name(*payload, &self.namespace, Flavor::InStruct), + // ); + // result.push_str(">("); + // result.push_str(&operands[0]); + // result.push(')'); + results.push(resultname); } abi::Instruction::ResultLower { results: result_types, From 076ea9e03fd4188de8d097a56efec7fe311a4ae8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 19 Feb 2024 20:01:54 +0100 Subject: [PATCH 074/672] fix simple_enum --- TODO.md | 4 ++- crates/cpp/src/lib.rs | 70 +++++++++++++++++++++++-------------------- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/TODO.md b/TODO.md index 7eb8b326d..fd055bf01 100644 --- a/TODO.md +++ b/TODO.md @@ -61,4 +61,6 @@ # Cpp -* Start namespace at root within exports referring to normal structs +* Nested lists +* Host: Strings inside records +* result diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 9360e4397..f82cf15d6 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1412,23 +1412,25 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> ) { let ty = &self.resolve.types[id]; let namespc = namespace(self.resolve, &ty.owner, false); - 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} = (1<<{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)); }} + 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} = (1<<{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( @@ -1438,7 +1440,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> _flags: &wit_bindgen_core::wit_parser::Tuple, _docs: &wit_bindgen_core::wit_parser::Docs, ) { - todo!() + // I assume I don't need to do anything ... } fn type_variant( @@ -1503,20 +1505,22 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> ) { let ty = &self.resolve.types[id]; let namespc = namespace(self.resolve, &ty.owner, false); - 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(), - ); + 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"); } - uwriteln!(self.gen.h_src.src, "}};\n"); } fn type_alias( @@ -1916,8 +1920,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } 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); + // 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) { From 27c4da785b1ce74eb4b7638770712e9a4c92bdc3 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 19 Feb 2024 20:25:06 +0100 Subject: [PATCH 075/672] smoke test passes --- crates/cpp/src/lib.rs | 48 ++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index f82cf15d6..984da385b 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -137,7 +137,7 @@ impl Cpp { fn interface<'a>( &'a mut self, resolve: &'a Resolve, - name: &'a Option<&'a WorldKey>, + name: Option<&'a WorldKey>, in_import: bool, wasm_import_module: Option, ) -> CppInterfaceGenerator<'a> { @@ -224,7 +224,7 @@ impl WorldGenerator for Cpp { 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)); + let mut gen = self.interface(resolve, binding, true, Some(wasm_import_module)); gen.interface = Some(id); // if self.gen.interfaces_with_types_printed.insert(id) { gen.types(id); @@ -234,7 +234,7 @@ impl WorldGenerator for Cpp { 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, id, AbiVariant::GuestImport); + gen.generate_function(func, &TypeOwner::Interface(id), AbiVariant::GuestImport); } } // gen.finish(); @@ -252,7 +252,7 @@ impl WorldGenerator for Cpp { .push_str(&format!("// export_interface {name:?}\n")); 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)); + 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); @@ -260,7 +260,7 @@ impl WorldGenerator for Cpp { 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, id, AbiVariant::GuestExport); + gen.generate_function(func, &TypeOwner::Interface(id), AbiVariant::GuestExport); } } Ok(()) @@ -278,12 +278,26 @@ impl WorldGenerator for Cpp { fn export_funcs( &mut self, - _resolve: &Resolve, - _world: WorldId, - _funcs: &[(&str, &Function)], + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], _files: &mut Files, ) -> anyhow::Result<()> { - todo!() + 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, Some(wasm_import_module)); + //gen.interface = Some(id); + //gen.types(id); + let namespace = namespace(resolve, &TypeOwner::World(world), true); + + 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( @@ -622,7 +636,7 @@ struct CppInterfaceGenerator<'a> { gen: &'a mut Cpp, resolve: &'a Resolve, interface: Option, - _name: &'a Option<&'a WorldKey>, + _name: Option<&'a WorldKey>, sizes: SizeAlign, in_import: bool, // return_pointer_area_size: usize, @@ -911,7 +925,13 @@ impl CppInterfaceGenerator<'_> { } } - fn generate_function(&mut self, func: &Function, interface: InterfaceId, variant: AbiVariant) { + fn generate_function( + &mut self, + func: &Function, + owner: &TypeOwner, + //interface: InterfaceId, + variant: AbiVariant, + ) { let export = match variant { AbiVariant::GuestImport => self.gen.opts.host, AbiVariant::GuestExport => !self.gen.opts.host, @@ -955,7 +975,7 @@ impl CppInterfaceGenerator<'_> { let namespace = if matches!(func.kind, FunctionKind::Freestanding) { namespace( self.resolve, - &TypeOwner::Interface(interface), + owner, matches!(variant, AbiVariant::GuestExport), ) } else { @@ -1365,7 +1385,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> results: Results::Named(vec![]), docs: Docs::default(), }; - self.generate_function(&func, intf, variant); + self.generate_function(&func, &TypeOwner::Interface(intf), variant); } let funcs = self.resolve.interfaces[intf].functions.values(); for func in funcs { @@ -1375,7 +1395,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> FunctionKind::Static(mid) => *mid == id, FunctionKind::Constructor(mid) => *mid == id, } { - self.generate_function(func, intf, variant); + self.generate_function(func, &TypeOwner::Interface(intf), variant); } } From 5698acf7fbedea47908e16e1caf9cab30c90333e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 19 Feb 2024 22:32:25 +0100 Subject: [PATCH 076/672] support flags and import_func test --- crates/cpp/helper-types/wit-host.h | 23 ++++ crates/cpp/src/lib.rs | 169 ++++++++++++++++++++--------- crates/cpp/src/wamr.rs | 5 +- 3 files changed, 147 insertions(+), 50 deletions(-) diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index c829aee46..49dcccc17 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -3,6 +3,7 @@ #include #include #include +#include "wit-common.h" #ifndef WIT_HOST_DIRECT #define WIT_HOST_WAMR @@ -54,6 +55,28 @@ class string { // add a convenient way to create a string }; +template +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; } +}; + template class guest_owned : public T { guest_address data_; #ifdef WIT_HOST_WAMR diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 984da385b..05b389bf9 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -268,12 +268,23 @@ impl WorldGenerator for Cpp { fn import_funcs( &mut self, - _resolve: &Resolve, - _world: WorldId, - _funcs: &[(&str, &Function)], + resolve: &Resolve, + world: WorldId, + funcs: &[(&str, &Function)], _files: &mut Files, ) { - todo!() + 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(), true, Some(wasm_import_module)); + let namespace = namespace(resolve, &TypeOwner::World(world), false); + + 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( @@ -287,8 +298,6 @@ impl WorldGenerator for Cpp { let wasm_import_module = resolve.name_world_key(&name); let binding = Some(name); let mut gen = self.interface(resolve, binding.as_ref(), false, Some(wasm_import_module)); - //gen.interface = Some(id); - //gen.types(id); let namespace = namespace(resolve, &TypeOwner::World(world), true); for (_name, func) in funcs.iter() { @@ -413,7 +422,7 @@ impl WorldGenerator for Cpp { } } if self.dependencies.needs_exported_resources { - let world_name = &self.world; + // let world_name = &self.world; uwriteln!(c_str.src, "template std::map wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}::resources;"); } } @@ -610,7 +619,7 @@ impl SourceWithState { fn qualify(&mut self, target: &Vec) { let mut same = 0; - let mut subpart = false; + // let mut subpart = false; // itertools::fold_while? for (a, b) in self.namespace.iter().zip(target.iter()) { if a == b { @@ -1184,7 +1193,7 @@ impl CppInterfaceGenerator<'_> { //self.gen.dependencies.needs_vector = true; Flavor::Argument(AbiVariant::GuestImport) => { self.gen.dependencies.needs_wit = true; - format!("wit::span<{inner}>") + format!("wit::span<{inner} const>") } Flavor::Argument(AbiVariant::GuestExport) if !self.gen.opts.host => { self.gen.dependencies.needs_wit = true; @@ -1192,7 +1201,7 @@ impl CppInterfaceGenerator<'_> { } Flavor::Result(AbiVariant::GuestExport) if self.gen.opts.host => { self.gen.dependencies.needs_wit = true; - format!("wit::span<{inner}>") + format!("wit::span<{inner} const>") } _ => { self.gen.dependencies.needs_wit = true; @@ -1442,7 +1451,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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} = (1<<{n}),"); + uwriteln!(self.gen.h_src.src, "k{fname} = (1ULL<<{n}),"); } uwriteln!(self.gen.h_src.src, "}};"); uwriteln!( @@ -1589,6 +1598,7 @@ struct FunctionBindgen<'a, 'b> { namespace: Vec, src: Source, block_storage: Vec, + /// intermediate calculations for contained objects blocks: Vec<(String, Vec)>, payloads: Vec, // caching for wasm @@ -1802,7 +1812,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let val = format!("vec{}", tmp); let ptr = format!("ptr{}", tmp); let len = format!("len{}", tmp); - let result = format!("result{}", tmp); + // let result = format!("result{}", tmp); self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); if self.gen.gen.opts.short_cut { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); @@ -1826,7 +1836,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let val = format!("vec{}", tmp); let ptr = format!("ptr{}", tmp); let len = format!("len{}", tmp); - let result = format!("result{}", tmp); + // let result = format!("result{}", tmp); self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); if self.gen.gen.opts.short_cut { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); @@ -1845,12 +1855,15 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } results.push(len); } - abi::Instruction::ListLower { element, realloc } => { + abi::Instruction::ListLower { + element: _, + 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); + // let result = format!("result{}", tmp); self.push_str(&format!("auto const&{} = {};\n", val, operands[0])); if self.gen.gen.opts.short_cut { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); @@ -1876,10 +1889,21 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { .gen .type_name(element, &self.namespace, Flavor::InStruct); self.push_str(&format!("auto {} = {};\n", len, operands[1])); - let result = format!( - "wit::vector<{2}>(({2} const *){0}, {1})", - operands[0], len, inner - ); + // let typecast = if self.gen.gen.opts.host { + // String::new() + // } else { + // format!("({inner} const *)") + // }; + // let result = format!("wit::vector<{inner}>({typecast}{}, {len})", operands[0]); + 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 { + format!( + "wit::vector<{inner}>(({inner} const*)({}), {len})", + operands[0] + ) + }; results.push(result); } abi::Instruction::StringLift => { @@ -2176,11 +2200,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { ); } abi::Instruction::OptionLift { payload, .. } => { - let (mut some, some_results) = self.blocks.pop().unwrap(); - let (mut none, none_results) = self.blocks.pop().unwrap(); + 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 some_result = &some_results[0]; let type_name = self .gen .type_name(*payload, &self.namespace, Flavor::InStruct); @@ -2198,44 +2222,79 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { }}", some_results[0] ); - - // let mut result: String = "std::optional<".into(); - // result.push_str( - // &self - // .gen - // .type_name(*payload, &self.namespace, Flavor::InStruct), - // ); - // result.push_str(">("); - // result.push_str(&operands[0]); - // result.push(')'); results.push(resultname); } abi::Instruction::ResultLower { results: result_types, + result, .. } => { - let err = self.blocks.pop().unwrap().0; - let ok = self.blocks.pop().unwrap().0; - self.let_results(result_types.len(), results); - let operand = &operands[0]; - self.push_str(&format!( - "{operand}.has_value() - ? ({ok}) - : ({err});" - )); + 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(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 = self.blocks.pop().unwrap().0; - let mut ok = self.blocks.pop().unwrap().0; + 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 = format!("std::move({ok})"); + ok_result = format!("std::move({})", ok_results[0]); } if result.err.is_none() { err.clear(); } else { - err = format!("std::move({err})"); + err_result = format!("std::move({})", err_results[0]); } let ok_type = self.gen.optional_type_name( result.ok.as_ref(), @@ -2247,12 +2306,24 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { &self.namespace, Flavor::InStruct, ); - let type_name = format!("std::expected<{ok_type}, {err_type}>",); + let full_type = format!("std::expected<{ok_type}, {err_type}>",); let err_type = "std::unexpected"; let operand = &operands[0]; - results.push(format!( - "{operand}==0 \n? {type_name}({ok}) \n: {type_name}({err_type}({err}))" - )); + + 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 } => { let module_name = self diff --git a/crates/cpp/src/wamr.rs b/crates/cpp/src/wamr.rs index 7d5dea483..42ef2a6a3 100644 --- a/crates/cpp/src/wamr.rs +++ b/crates/cpp/src/wamr.rs @@ -91,7 +91,10 @@ fn wamr_add_result(sig: &mut WamrSig, resolve: &Resolve, ty: &Type) { } Type::Id(id) => match &resolve.types[*id].kind { TypeDefKind::Record(_r) => sig.wamr_types.push('R'), - TypeDefKind::Flags(_) => todo!(), + TypeDefKind::Flags(fl) => { + sig.wamr_types + .push(if fl.flags.len() > 32 { 'I' } else { 'i' }) + } TypeDefKind::Tuple(_) => sig.wamr_result.push('i'), TypeDefKind::Variant(_) => todo!(), TypeDefKind::Enum(_e) => { From cbfd4ac04ad90df89556e0f72ca9ce06dd24fdf0 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 19 Feb 2024 22:59:07 +0100 Subject: [PATCH 077/672] flags test now passes --- crates/cpp/src/lib.rs | 45 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 05b389bf9..bf790c4a7 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -8,11 +8,10 @@ use std::{ }; use wit_bindgen_c::{to_c_ident, wasm_type}; use wit_bindgen_core::{ - abi::{self, AbiVariant, LiftLower, WasmSignature}, - abi::{Bindgen, WasmType}, + abi::{self, AbiVariant, Bindgen, LiftLower, WasmSignature, WasmType}, uwrite, uwriteln, wit_parser::{ - Docs, Function, FunctionKind, Handle, InterfaceId, Resolve, Results, SizeAlign, Type, + Docs, Function, FunctionKind, Handle, Int, InterfaceId, Resolve, Results, SizeAlign, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, }, Files, InterfaceGenerator, Source, WorldGenerator, @@ -2024,14 +2023,42 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src.push_str(");\n"); results.push(name); } - abi::Instruction::FlagsLower { flags, .. } => { - let tmp = self.tmp(); - self.push_str(&format!("auto flags{} = {};\n", tmp, operands[0])); - for i in 0..flags.repr().count() { - results.push(format!("((flags{} >> {})&1)!=0", tmp, i * 32)); + 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::FlagsLift { .. } => results.push("FlagsLift".to_string()), abi::Instruction::VariantPayloadName => { let name = format!("payload{}", self.tmp()); results.push(name.clone()); //format!("*{}", name)); From a7f03b166c9d076b592a8f8e4ae7bc7c382557c5 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 19 Feb 2024 23:45:50 +0100 Subject: [PATCH 078/672] guest side of just_export passes --- TODO.md | 1 - crates/cpp/helper-types/wit-guest.h | 2 +- crates/cpp/src/lib.rs | 22 +++++++++++++++++++--- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/TODO.md b/TODO.md index fd055bf01..48667fc4d 100644 --- a/TODO.md +++ b/TODO.md @@ -63,4 +63,3 @@ * Nested lists * Host: Strings inside records -* result diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index b3f31daa6..4e8b7539d 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -56,7 +56,7 @@ class vector { b.data_ = nullptr; return *this; } - vector(T const *d, size_t l) : data_(d), length(l) {} + vector(T *d, size_t l) : data_(d), length(l) {} T const *data() const { return data_; } T *data() { return data_; } size_t size() const { return length; } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index bf790c4a7..ae509e15b 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1899,7 +1899,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { format!("wit::span<{inner} const>(ptr{}, (size_t){len})", tmp) } else { format!( - "wit::vector<{inner}>(({inner} const*)({}), {len})", + "wit::vector<{inner}>(({inner}*)({}), {len})", operands[0] ) }; @@ -2473,7 +2473,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } abi::Instruction::Malloc { .. } => todo!(), - abi::Instruction::GuestDeallocate { .. } => todo!(), + abi::Instruction::GuestDeallocate { .. } => { + uwriteln!(self.src, "free((void*) ({}));", operands[0]); + }, abi::Instruction::GuestDeallocateString => { uwriteln!(self.src, "if (({}) > 0) {{", operands[1]); uwriteln!( @@ -2502,7 +2504,21 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { uwriteln!(self.src, "free((void*) ({ptr}));"); uwriteln!(self.src, "}}"); } - abi::Instruction::GuestDeallocateVariant { .. } => todo!(), + abi::Instruction::GuestDeallocateVariant { blocks } => { + let blocks = self + .blocks + .drain(self.blocks.len() - blocks..) + .collect::>(); + + 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"); + } } } From b457d76efd8194910098855ce16b12a057985648 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 19 Feb 2024 23:54:03 +0100 Subject: [PATCH 079/672] on the path to support variants --- crates/cpp/src/lib.rs | 44 +++++++++++++++++++++++++++++++++++------- crates/cpp/src/wamr.rs | 4 +++- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index ae509e15b..ee59e8f56 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -8,7 +8,7 @@ use std::{ }; use wit_bindgen_c::{to_c_ident, wasm_type}; use wit_bindgen_core::{ - abi::{self, AbiVariant, Bindgen, LiftLower, WasmSignature, WasmType}, + abi::{self, AbiVariant, Bindgen, Bitcast, LiftLower, WasmSignature, WasmType}, uwrite, uwriteln, wit_parser::{ Docs, Function, FunctionKind, Handle, Int, InterfaceId, Resolve, Results, SizeAlign, Type, @@ -1740,7 +1740,40 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } abi::Instruction::I32Const { val } => results.push(format!("(int32_t({}))", val)), - abi::Instruction::Bitcasts { .. } => todo!(), + abi::Instruction::Bitcasts { casts } => { + for (cast, op) in casts.iter().zip(operands) { + let op = op; + match cast { + Bitcast::I32ToF32 | Bitcast::I64ToF32 => { + results + .push(format!("((union {{ int32_t a; float b; }}){{ {} }}).b", op)); + } + Bitcast::F32ToI32 | Bitcast::F32ToI64 => { + results + .push(format!("((union {{ float a; int32_t b; }}){{ {} }}).b", op)); + } + Bitcast::I64ToF64 => { + results.push(format!( + "((union {{ int64_t a; double b; }}){{ {} }}).b", + op + )); + } + Bitcast::F64ToI64 => { + results.push(format!( + "((union {{ double a; int64_t b; }}){{ {} }}).b", + op + )); + } + Bitcast::I32ToI64 => { + results.push(format!("(int64_t) {}", op)); + } + Bitcast::I64ToI32 => { + results.push(format!("(int32_t) {}", op)); + } + Bitcast::None => results.push(op.to_string()), + } + } + } abi::Instruction::ConstZero { tys } => { for ty in tys.iter() { match ty { @@ -1898,10 +1931,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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 { - format!( - "wit::vector<{inner}>(({inner}*)({}), {len})", - operands[0] - ) + format!("wit::vector<{inner}>(({inner}*)({}), {len})", operands[0]) }; results.push(result); } @@ -2475,7 +2505,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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!( diff --git a/crates/cpp/src/wamr.rs b/crates/cpp/src/wamr.rs index 42ef2a6a3..d8d9d3a17 100644 --- a/crates/cpp/src/wamr.rs +++ b/crates/cpp/src/wamr.rs @@ -96,7 +96,9 @@ fn wamr_add_result(sig: &mut WamrSig, resolve: &Resolve, ty: &Type) { .push(if fl.flags.len() > 32 { 'I' } else { 'i' }) } TypeDefKind::Tuple(_) => sig.wamr_result.push('i'), - TypeDefKind::Variant(_) => todo!(), + TypeDefKind::Variant(_) => { + sig.wamr_types.push('*'); + } TypeDefKind::Enum(_e) => { sig.wamr_types.push('*'); } From 27fd2b8ed2b83ef7f743259c5e2aa68cdf563143 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 19 Feb 2024 23:58:09 +0100 Subject: [PATCH 080/672] fix conventions test (20/61 pass) --- crates/cpp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index ee59e8f56..75f3a8725 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -193,7 +193,6 @@ impl WorldGenerator for Cpp { uwriteln!( self.c_src.src, r#"#include "{}_cpp.h" - #include #include // realloc extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size); @@ -353,6 +352,7 @@ impl WorldGenerator for Cpp { ); } self.include(""); + self.include(""); // for std::move if self.dependencies.needs_string { self.include(""); } From a271b4c2bb1a72922c12996fc723d3d1407bda6e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 20 Feb 2024 22:22:22 +0100 Subject: [PATCH 081/672] multiple results map to tuples --- Cargo.lock | 2 +- crates/cpp/src/lib.rs | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d6491ad6b..e3d57ba05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2409,7 +2409,7 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder 0.41.2", + "wasm-encoder 0.200.0", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 75f3a8725..42f63a16f 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -828,7 +828,12 @@ impl CppInterfaceGenerator<'_> { if n.is_empty() { res.result = "void".into(); } else { - todo!(); + res.result = "std::tuple<".into(); + for (i, (name, ty)) in n.iter().enumerate() { + if i>0 {res.result.push_str(", ");} + res.result.push_str(&self.type_name(ty, &res.namespace, Flavor::Result(abi_variant))); + } + res.result.push('>'); } } wit_bindgen_core::wit_parser::Results::Anon(ty) => { From 4b28ebf867be6e117a2e3ab8a2ebd763acb8029b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 20 Feb 2024 22:55:43 +0100 Subject: [PATCH 082/672] multiple returns don't crash --- crates/cpp/src/lib.rs | 57 ++++++++++++++++++++++++++++++------------ crates/cpp/src/wamr.rs | 4 +-- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 42f63a16f..5dd3e160c 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -829,9 +829,15 @@ impl CppInterfaceGenerator<'_> { res.result = "void".into(); } else { res.result = "std::tuple<".into(); - for (i, (name, ty)) in n.iter().enumerate() { - if i>0 {res.result.push_str(", ");} - res.result.push_str(&self.type_name(ty, &res.namespace, Flavor::Result(abi_variant))); + for (i, (_name, ty)) in n.iter().enumerate() { + if i > 0 { + res.result.push_str(", "); + } + res.result.push_str(&self.type_name( + ty, + &res.namespace, + Flavor::Result(abi_variant), + )); } res.result.push('>'); } @@ -1645,17 +1651,19 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } fn let_results(&mut self, amt: usize, results: &mut Vec) { - match amt { - 0 => {} - 1 => { - let tmp = self.tmp(); - let res = format!("result{}", tmp); - self.push_str("auto "); - self.push_str(&res); + 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); - self.push_str(" = "); + } else { + for i in 0..amt { + results.push(format!("std::get<{i}>({res})")); + } } - _n => todo!(), } } @@ -2493,7 +2501,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let import = !self.gen.gen.opts.host; match amt { 0 => {} - 1 => { + _ => { + assert!(*amt == operands.len()); match &func.kind { FunctionKind::Constructor(_) if import => { // strange but works @@ -2501,10 +2510,26 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } _ => self.src.push_str("return "), } - self.src.push_str(&operands[0]); - self.src.push_str(";\n"); + if *amt == 1 { + self.src.push_str(&operands[0]); + self.src.push_str(";\n"); + } else { + 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(");\n"); + } } - _ => todo!(), } } abi::Instruction::Malloc { .. } => todo!(), diff --git a/crates/cpp/src/wamr.rs b/crates/cpp/src/wamr.rs index d8d9d3a17..db36b1e60 100644 --- a/crates/cpp/src/wamr.rs +++ b/crates/cpp/src/wamr.rs @@ -131,8 +131,8 @@ pub fn wamr_signature(resolve: &Resolve, func: &Function) -> WamrSig { match &func.results { Results::Named(p) => { if !p.is_empty() { - dbg!(p); - todo!() + // assume a pointer + result.wamr_types.push('*'); } } Results::Anon(e) => wamr_add_result(&mut result, resolve, e), From 78a214b4f265d8182cb64ff0a818696706e50c7d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 25 Feb 2024 18:26:33 +0100 Subject: [PATCH 083/672] initial adaptation to wasm64 --- Cargo.lock | 62 +++++++++++++++++++++++++----------- Cargo.toml | 10 +++--- crates/c/src/lib.rs | 3 ++ crates/core/src/abi.rs | 6 ++++ crates/cpp/src/lib.rs | 40 +++++++++++++++++++++++ crates/csharp/src/lib.rs | 6 ++++ crates/rust/src/bindgen.rs | 3 ++ crates/rust/src/lib.rs | 3 ++ crates/teavm-java/src/lib.rs | 6 ++++ 9 files changed, 115 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76805f1bc..b7e014c20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1375,7 +1375,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.200.0", + "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", "wit-bindgen-core", "wit-component", "wit-parser 0.200.0", @@ -1684,11 +1684,18 @@ dependencies = [ "leb128", ] +[[package]] +name = "wasm-encoder" +version = "0.200.0" +source = "git+https://github.com/bytecodealliance/wasm-tools.git#a9223be96f65d09ddbcf7adc8cc560a832f26f7f" +dependencies = [ + "leb128", +] + [[package]] name = "wasm-metadata" version = "0.200.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b8cc0c21f46d55b0aaa419cacce1eadcf28eaebd0e1488d6a6313ee71a586" +source = "git+https://github.com/bytecodealliance/wasm-tools.git#a9223be96f65d09ddbcf7adc8cc560a832f26f7f" dependencies = [ "anyhow", "indexmap", @@ -1696,7 +1703,7 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.200.0", + "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", "wasmparser 0.200.0", ] @@ -1724,8 +1731,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.200.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a03f65ac876612140c57ff6c3b8fe4990067cce97c2cfdb07368a3cc3354b062" +source = "git+https://github.com/bytecodealliance/wasm-tools.git#a9223be96f65d09ddbcf7adc8cc560a832f26f7f" dependencies = [ "bitflags 2.4.2", "indexmap", @@ -1777,7 +1783,7 @@ dependencies = [ "wasmtime-jit", "wasmtime-runtime", "wasmtime-winch", - "wat", + "wat 1.200.0 (registry+https://github.com/rust-lang/crates.io-index)", "windows-sys 0.52.0", ] @@ -2103,7 +2109,19 @@ dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.200.0", + "wasm-encoder 0.200.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wast" +version = "200.0.0" +source = "git+https://github.com/bytecodealliance/wasm-tools.git#a9223be96f65d09ddbcf7adc8cc560a832f26f7f" +dependencies = [ + "bumpalo", + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", ] [[package]] @@ -2112,7 +2130,15 @@ version = "1.200.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "776cbd10e217f83869beaa3f40e312bb9e91d5eee29bbf6f560db1261b6a4c3d" dependencies = [ - "wast 200.0.0", + "wast 200.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wat" +version = "1.200.0" +source = "git+https://github.com/bytecodealliance/wasm-tools.git#a9223be96f65d09ddbcf7adc8cc560a832f26f7f" +dependencies = [ + "wast 200.0.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", ] [[package]] @@ -2362,7 +2388,7 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder 0.200.0", + "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -2377,7 +2403,7 @@ dependencies = [ "clap", "heck", "test-artifacts", - "wasm-encoder 0.200.0", + "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", "wasmparser 0.200.0", "wasmtime", "wasmtime-wasi", @@ -2409,7 +2435,7 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder 0.200.0", + "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", @@ -2424,7 +2450,7 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder 0.200.0", + "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", "wasm-metadata", "wasmparser 0.200.0", "wit-bindgen-core", @@ -2499,8 +2525,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.200.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39979723340baea490b87b11b2abae05f149d86f2b55c18d41d78a2a2b284c16" +source = "git+https://github.com/bytecodealliance/wasm-tools.git#a9223be96f65d09ddbcf7adc8cc560a832f26f7f" dependencies = [ "anyhow", "bitflags 2.4.2", @@ -2509,10 +2534,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.200.0", + "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", "wasm-metadata", "wasmparser 0.200.0", - "wat", + "wat 1.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", "wit-parser 0.200.0", ] @@ -2536,8 +2561,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.200.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f717576b37f01c15696bda7f6f13868367b9c5913485f9f0ec8e59fd28c8e13" +source = "git+https://github.com/bytecodealliance/wasm-tools.git#a9223be96f65d09ddbcf7adc8cc560a832f26f7f" dependencies = [ "anyhow", "id-arena", diff --git a/Cargo.toml b/Cargo.toml index 402ce3b99..c586d88d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,11 +29,11 @@ clap = { version = "4.4.7", features = ["derive"] } env_logger = "0.10.0" indexmap = "2.0.2" -wasmparser = "0.200.0" -wasm-encoder = "0.200.0" -wasm-metadata = "0.200.0" -wit-parser = "0.200.0" -wit-component = "0.200.0" +wasmparser = { git = "https://github.com/bytecodealliance/wasm-tools.git" } +wasm-encoder = { git = "https://github.com/bytecodealliance/wasm-tools.git" } +wasm-metadata = { git = "https://github.com/bytecodealliance/wasm-tools.git" } +wit-parser = { git = "https://github.com/bytecodealliance/wasm-tools.git" } +wit-component = { git = "https://github.com/bytecodealliance/wasm-tools.git" } wit-bindgen-core = { path = 'crates/core', version = '0.19.1' } wit-bindgen-c = { path = 'crates/c', version = '0.19.1' } diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 2b6848e72..ff28a8277 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -3015,6 +3015,9 @@ pub fn wasm_type(ty: WasmType) -> &'static str { WasmType::I64 => "int64_t", WasmType::F32 => "float", WasmType::F64 => "double", + WasmType::Length => "size_t", + WasmType::Pointer => "intptr_t", + WasmType::PointerOrI64 => "int64_t", } } diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 83fe4d39d..35bc243f8 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -1876,6 +1876,12 @@ fn cast(from: WasmType, to: WasmType) -> Bitcast { (I64, F32) => Bitcast::I64ToF32, (F32, F64) | (F64, F32) | (F64, I32) | (I32, F64) => unreachable!(), + (WasmType::Pointer, _) | (WasmType::PointerOrI64, _) | (WasmType::Length, _) => { + unreachable!() + } + (_, WasmType::Pointer) | (_, WasmType::PointerOrI64) | (_, WasmType::Length) => { + unreachable!() + } } } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 5dd3e160c..32316988a 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -104,6 +104,9 @@ pub struct Opts { /// 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, } impl Opts { @@ -1794,6 +1797,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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()), } } } @@ -2422,6 +2428,23 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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({}),", value) + } + } + WasmType::Pointer => { + if self.gen.gen.opts.wasm64 { + uwrite!(self.src, "WASM_I64_VAL({}),", value) + } else { + uwrite!(self.src, "WASM_I32_VAL({}),", value) + } + } + WasmType::PointerOrI64 => { + uwrite!(self.src, "WASM_I64_VAL({}),", value) + } } } self.src.push_str("};\n"); @@ -2436,6 +2459,23 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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});"); diff --git a/crates/csharp/src/lib.rs b/crates/csharp/src/lib.rs index efc51f522..c994add40 100644 --- a/crates/csharp/src/lib.rs +++ b/crates/csharp/src/lib.rs @@ -1696,6 +1696,9 @@ impl Bindgen for FunctionBindgen<'_, '_> { WasmType::I64 => "0L", WasmType::F32 => "0.0F", WasmType::F64 => "0.0D", + WasmType::Length => todo!(), + WasmType::Pointer => todo!(), + WasmType::PointerOrI64 => todo!(), } .to_owned() })), @@ -2223,6 +2226,9 @@ fn wasm_type(ty: WasmType) -> &'static str { WasmType::I64 => "long", WasmType::F32 => "float", WasmType::F64 => "double", + WasmType::Length => todo!(), + WasmType::Pointer => todo!(), + WasmType::PointerOrI64 => todo!(), } } diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 393e28bed..9a4dc3c9c 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -313,6 +313,9 @@ impl Bindgen for FunctionBindgen<'_, '_> { WasmType::I64 => results.push("0i64".to_string()), WasmType::F32 => results.push("0.0f32".to_string()), WasmType::F64 => results.push("0.0f64".to_string()), + WasmType::Length => results.push("0usize".to_string()), + WasmType::Pointer => results.push("0usize".to_string()), + WasmType::PointerOrI64 => results.push("0i64".to_string()), } } } diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index bb80f5b0c..ba7525e73 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -835,6 +835,9 @@ fn wasm_type(ty: WasmType) -> &'static str { WasmType::I64 => "i64", WasmType::F32 => "f32", WasmType::F64 => "f64", + WasmType::Length => "usize", + WasmType::Pointer => "usize", + WasmType::PointerOrI64 => "i64", } } diff --git a/crates/teavm-java/src/lib.rs b/crates/teavm-java/src/lib.rs index 3b51547bc..2fbfcd766 100644 --- a/crates/teavm-java/src/lib.rs +++ b/crates/teavm-java/src/lib.rs @@ -1294,6 +1294,9 @@ impl Bindgen for FunctionBindgen<'_, '_> { WasmType::I64 => "0L", WasmType::F32 => "0.0F", WasmType::F64 => "0.0D", + WasmType::Length => todo!(), + WasmType::Pointer => todo!(), + WasmType::PointerOrI64 => todo!(), } .to_owned() })), @@ -2103,6 +2106,9 @@ fn wasm_type(ty: WasmType) -> &'static str { WasmType::I64 => "long", WasmType::F32 => "float", WasmType::F64 => "double", + WasmType::Length => todo!(), + WasmType::Pointer => todo!(), + WasmType::PointerOrI64 => todo!(), } } From c53614610ab5ced9220cdc5a92a1588bea47f980 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 25 Feb 2024 19:24:04 +0100 Subject: [PATCH 084/672] prefer ptr and size types --- crates/cpp/src/lib.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 32316988a..82d84c7f3 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1865,12 +1865,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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.short_cut { + if self.gen.gen.opts.short_cut || self.gen.gen.opts.host { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); self.push_str(&format!("auto {} = {}.size();\n", len, val)); } else { - self.push_str(&format!("auto {} = (int32_t)({}.data());\n", ptr, val)); - self.push_str(&format!("auto {} = (int32_t)({}.size());\n", len, val)); + self.push_str(&format!("auto {} = (intptr_t)({}.data());\n", ptr, val)); + self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val)); } if realloc.is_none() { results.push(ptr); @@ -1889,12 +1889,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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.short_cut { + if self.gen.gen.opts.short_cut || self.gen.gen.opts.host { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); self.push_str(&format!("auto {} = {}.size();\n", len, val)); } else { - self.push_str(&format!("auto {} = (int32_t)({}.data());\n", ptr, val)); - self.push_str(&format!("auto {} = (int32_t)({}.size());\n", len, val)); + self.push_str(&format!("auto {} = (intptr_t)({}.data());\n", ptr, val)); + self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val)); } if realloc.is_none() { results.push(ptr); @@ -1916,12 +1916,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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.short_cut { + if self.gen.gen.opts.short_cut || self.gen.gen.opts.host { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); self.push_str(&format!("auto {} = {}.size();\n", len, val)); } else { - self.push_str(&format!("auto {} = (int32_t)({}.data());\n", ptr, val)); - self.push_str(&format!("auto {} = (int32_t)({}.size());\n", len, val)); + self.push_str(&format!("auto {} = (intptr_t)({}.data());\n", ptr, val)); + self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val)); } if realloc.is_none() { results.push(ptr); @@ -2432,14 +2432,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if self.gen.gen.opts.wasm64 { uwrite!(self.src, "WASM_I64_VAL({}),", value) } else { - uwrite!(self.src, "WASM_I32_VAL({}),", value) + 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({}),", value) + uwrite!(self.src, "WASM_I32_VAL((int32_t){}),", value) } } WasmType::PointerOrI64 => { From 4b54c5445753d36e0756523006bf61732eac5098 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 25 Feb 2024 21:01:17 +0100 Subject: [PATCH 085/672] initial native prototype: guest --- crates/cpp/src/lib.rs | 9 +- crates/cpp/tests/native_strings/Makefile | 12 ++ crates/cpp/tests/native_strings/guest.cpp | 13 ++ crates/cpp/tests/native_strings/guest.lds | 7 + crates/cpp/tests/native_strings/the_world.cpp | 122 ++++++++++++++++++ .../cpp/tests/native_strings/the_world_cpp.h | 30 +++++ .../cpp/tests/native_strings/wit/strings.wit | 1 + 7 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 crates/cpp/tests/native_strings/Makefile create mode 100644 crates/cpp/tests/native_strings/guest.cpp create mode 100644 crates/cpp/tests/native_strings/guest.lds create mode 100644 crates/cpp/tests/native_strings/the_world.cpp create mode 100644 crates/cpp/tests/native_strings/the_world_cpp.h create mode 120000 crates/cpp/tests/native_strings/wit/strings.wit diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 82d84c7f3..27b2e7512 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -402,7 +402,9 @@ impl WorldGenerator for Cpp { c_str.src, "// Generated by `wit-bindgen` {version}. DO NOT EDIT!" ); - if !self.opts.host { + if self.opts.short_cut { + uwriteln!(c_str.src, "#include \"{snake}_cpp_native.h\""); + } else if !self.opts.host { // uwriteln!(c_str.src, "#include \"{snake}_cpp.h\""); } else { uwriteln!(c_str.src, "#include \"{snake}_cpp_host.h\""); @@ -558,7 +560,10 @@ impl WorldGenerator for Cpp { Self::clang_format(&mut h_str.src); } - if !self.opts.host { + 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 { diff --git a/crates/cpp/tests/native_strings/Makefile b/crates/cpp/tests/native_strings/Makefile new file mode 100644 index 000000000..24e8ae139 --- /dev/null +++ b/crates/cpp/tests/native_strings/Makefile @@ -0,0 +1,12 @@ +CXXFLAGS=-g -O0 -I../../helper-types + +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 $@ $^ -lstrings 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/the_world.cpp b/crates/cpp/tests/native_strings/the_world.cpp new file mode 100644 index 000000000..b323da7c3 --- /dev/null +++ b/crates/cpp/tests/native_strings/the_world.cpp @@ -0,0 +1,122 @@ +// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT! +#include "the_world_cpp.h" +#include // 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(intptr_t, + size_t); +void foo::foo::strings::A(std::string_view x) { + auto const &vec0 = x; + auto ptr0 = (intptr_t)(vec0.data()); + auto len0 = (size_t)(vec0.size()); + fooX3AfooX2FstringsX00a(ptr0, len0); +} +extern "C" __attribute__((import_module("foo:foo/strings"))) + __attribute__((import_name("b"))) void fooX3AfooX2FstringsX00b(intptr_t); +wit::string foo::foo::strings::B() { + uintptr_t ret_area[2]; + intptr_t ptr0 = intptr_t(&ret_area); + fooX3AfooX2FstringsX00b(ptr0); + intptr_t l1 = *((intptr_t const *)(ptr0 + 0)); + size_t l2 = *((size_t const *)(ptr0 + 8)); + auto len3 = l2; + + return wit::string((char const *)(l1), len3); +} +extern "C" __attribute__((import_module("foo:foo/strings"))) + __attribute__((import_name("c"))) void fooX3AfooX2FstringsX00c( + intptr_t, size_t, intptr_t, size_t, intptr_t); +wit::string foo::foo::strings::C(std::string_view a, std::string_view b) { + auto const &vec0 = a; + auto ptr0 = (intptr_t)(vec0.data()); + auto len0 = (size_t)(vec0.size()); + auto const &vec1 = b; + auto ptr1 = (intptr_t)(vec1.data()); + auto len1 = (size_t)(vec1.size()); + uintptr_t ret_area[2]; + intptr_t ptr2 = intptr_t(&ret_area); + fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2); + intptr_t l3 = *((intptr_t const *)(ptr2 + 0)); + size_t l4 = *((size_t const *)(ptr2 + 4)); + auto len5 = l4; + + return wit::string((char const *)(l3), len5); +} +extern "C" +__attribute__((__export_name__("foo:foo/strings#a"))) void +fooX3AfooX2FstringsX23a(intptr_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"))) intptr_t +fooX3AfooX2FstringsX23b() { + auto result0 = exports::foo::foo::strings::B(); + static uintptr_t ret_area[2]; + intptr_t ptr1 = intptr_t(&ret_area); + auto const &vec2 = result0; + auto ptr2 = (intptr_t)(vec2.data()); + auto len2 = (size_t)(vec2.size()); + result0.leak(); + + *((size_t *)(ptr1 + 8)) = len2; + *((intptr_t *)(ptr1 + 0)) = ptr2; + return ptr1; +} +extern "C" +__attribute__((__weak__, __export_name__("cabi_post_foo:foo/strings#b"))) void +cabi_post_fooX3AfooX2FstringsX23b(intptr_t arg0) { + intptr_t l0 = *((intptr_t const *)(arg0 + 0)); + size_t l1 = *((size_t const *)(arg0 + 8)); + if ((l1) > 0) { + wit::string::drop_raw((void *)(l0)); + } +} +extern "C" +__attribute__((__export_name__("foo:foo/strings#c"))) intptr_t +fooX3AfooX2FstringsX23c(intptr_t arg0, size_t arg1, intptr_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 uintptr_t ret_area[2]; + intptr_t ptr3 = intptr_t(&ret_area); + auto const &vec4 = result2; + auto ptr4 = (intptr_t)(vec4.data()); + auto len4 = (size_t)(vec4.size()); + result2.leak(); + + *((size_t *)(ptr3 + 8)) = len4; + *((intptr_t *)(ptr3 + 0)) = ptr4; + return ptr3; +} +extern "C" +__attribute__((__weak__, __export_name__("cabi_post_foo:foo/strings#c"))) void +cabi_post_fooX3AfooX2FstringsX23c(intptr_t arg0) { + intptr_t l0 = *((intptr_t const *)(arg0 + 0)); + size_t l1 = *((size_t const *)(arg0 + 8)); + if ((l1) > 0) { + wit::string::drop_raw((void *)(l0)); + } +} + +// 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 +#include +#include +#include +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/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 From 5c9b36292b859cc0418e55ef0706939bae206ca4 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 25 Feb 2024 21:34:41 +0100 Subject: [PATCH 086/672] initial native header support --- crates/cpp/src/lib.rs | 82 +++++++++++-------- crates/cpp/tests/native_strings/Makefile | 5 ++ .../native_strings/the_world_cpp_native.h | 30 +++++++ 3 files changed, 82 insertions(+), 35 deletions(-) create mode 100644 crates/cpp/tests/native_strings/the_world_cpp_native.h diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 27b2e7512..fba4652fe 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -115,6 +115,10 @@ impl Opts { r.opts = self; Box::new(r) } + + fn host_side(&self) -> bool { + self.short_cut || self.host + } } impl Cpp { @@ -192,7 +196,7 @@ impl WorldGenerator for Cpp { self.world = name.to_string(); self.world_id = Some(world); // self.sizes.fill(resolve); - if !self.opts.host { + if !self.opts.host_side() { uwriteln!( self.c_src.src, r#"#include "{}_cpp.h" @@ -338,7 +342,14 @@ impl WorldGenerator for Cpp { "// Generated by `wit-bindgen` {version}. DO NOT EDIT!" ); - if !self.opts.host { + 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 @@ -387,7 +398,7 @@ impl WorldGenerator for Cpp { self.include(""); } if self.dependencies.needs_wit { - if self.opts.host || self.opts.short_cut { + if self.opts.host_side() { self.include(""); } else { self.include(""); @@ -408,28 +419,26 @@ impl WorldGenerator for Cpp { // uwriteln!(c_str.src, "#include \"{snake}_cpp.h\""); } else { uwriteln!(c_str.src, "#include \"{snake}_cpp_host.h\""); - if !self.opts.short_cut { - uwriteln!( - c_str.src, - "#include // wasm-micro-runtime header\n\ + uwriteln!( + c_str.src, + "#include // wasm-micro-runtime header\n\ #include " - ); + ); - 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 c_str.src.len() > 0 { + c_str.src.push_str("\n"); } - if self.dependencies.needs_exported_resources { - // let world_name = &self.world; - uwriteln!(c_str.src, "template std::map wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}::resources;"); + 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 { + // let world_name = &self.world; + uwriteln!(c_str.src, "template std::map wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}::resources;"); + } // if self.dependencies.needs_exported_resources { // let namespace = namespace(resolve, &TypeOwner::World(world_id), false); @@ -742,6 +751,7 @@ impl CppInterfaceGenerator<'_> { self.resolve.wasm_signature(AbiVariant::GuestExport, func) }; let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + if self.gen.opts.short_cut { } else if self.gen.opts.host { self.gen.c_src.src.push_str("static "); } else { @@ -751,7 +761,7 @@ impl CppInterfaceGenerator<'_> { r#"__attribute__((__export_name__("{module_name}#{func_name}")))"# ); } - let return_via_pointer = signature.retptr && self.gen.opts.host; + let return_via_pointer = signature.retptr && self.gen.opts.host_side(); self.gen .c_src .src @@ -790,11 +800,13 @@ impl CppInterfaceGenerator<'_> { // else { // first_arg = false; // } - self.gen.c_src.src.push_str("int32_t resultptr"); + let ptrtype = if !self.gen.opts.host { "intptr_t"} else if self.gen.opts.wasm64 {"int64_t"} else {"int32_t"}; + self.gen.c_src.src.push_str(ptrtype); + 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 { + if self.gen.opts.host_side() { let signature = wamr::wamr_signature(self.resolve, func); let remember = HostFunction { wasm_name: func.name.clone(), @@ -817,14 +829,14 @@ impl CppInterfaceGenerator<'_> { from_namespace: &Vec, ) -> HighlevelSignature { let mut res = HighlevelSignature::default(); - let abi_variant = if import ^ self.gen.opts.host { + 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, !(import ^ self.gen.opts.host)); + self.func_namespace_name(func, !(import ^ self.gen.opts.host_side())); res.name = func_name_h; res.namespace = namespace; let is_drop = is_drop_method(func); @@ -960,8 +972,8 @@ impl CppInterfaceGenerator<'_> { variant: AbiVariant, ) { let export = match variant { - AbiVariant::GuestImport => self.gen.opts.host, - AbiVariant::GuestExport => !self.gen.opts.host, + AbiVariant::GuestImport => self.gen.opts.host_side(), + AbiVariant::GuestExport => !self.gen.opts.host_side(), }; let params = self.print_signature(func, !export); self.gen.c_src.src.push_str("{\n"); @@ -1032,7 +1044,7 @@ impl CppInterfaceGenerator<'_> { } self.gen.c_src.src.push_str("}\n"); // cabi_post - if !self.gen.opts.host + if !self.gen.opts.host_side() && matches!(variant, AbiVariant::GuestExport) && abi::guest_export_needs_post_return(self.resolve, func) { @@ -1156,11 +1168,11 @@ impl CppInterfaceGenerator<'_> { self.gen.dependencies.needs_string_view = true; "std::string_view".into() } - Flavor::Argument(AbiVariant::GuestExport) if !self.gen.opts.host => { + 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 => { + Flavor::Result(AbiVariant::GuestExport) if self.gen.opts.host_side() => { self.gen.dependencies.needs_string_view = true; "std::string_view".into() } @@ -1870,7 +1882,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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.short_cut || self.gen.gen.opts.host { + 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 { @@ -1894,7 +1906,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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.short_cut || self.gen.gen.opts.host { + 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 { @@ -1904,7 +1916,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if realloc.is_none() { results.push(ptr); } else { - if !self.gen.gen.opts.host { + if !self.gen.gen.opts.host_side() { uwriteln!(self.src, "{}.leak();\n", operands[0]); } results.push(ptr); @@ -1921,7 +1933,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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.short_cut || self.gen.gen.opts.host { + 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 { @@ -1931,7 +1943,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if realloc.is_none() { results.push(ptr); } else { - if !self.gen.gen.opts.host { + if !self.gen.gen.opts.host_side() { uwriteln!(self.src, "{}.leak();\n", operands[0]); } results.push(ptr); diff --git a/crates/cpp/tests/native_strings/Makefile b/crates/cpp/tests/native_strings/Makefile index 24e8ae139..4584d1f1a 100644 --- a/crates/cpp/tests/native_strings/Makefile +++ b/crates/cpp/tests/native_strings/Makefile @@ -1,4 +1,5 @@ CXXFLAGS=-g -O0 -I../../helper-types +WIT_BINDGEN=../../../../target/debug/wit-bindgen all: libstrings.so app-strings @@ -10,3 +11,7 @@ libstrings.so: the_world.pie.o guest.pie.o app-strings: the_world_native.o main.o $(CXX) $(CXXFLAGS) -o $@ $^ -lstrings + +bindgen: wit/strings.wit + $(WIT_BINDGEN) cpp wit --wasm64 --format + $(WIT_BINDGEN) cpp wit --wasm64 --format --direct 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..0fa5229ce --- /dev/null +++ b/crates/cpp/tests/native_strings/the_world_cpp_native.h @@ -0,0 +1,30 @@ +// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT! +#ifndef __CPP_NATIVE_BINDINGS_THE_WORLD_H +#define __CPP_NATIVE_BINDINGS_THE_WORLD_H +#include +#include +#include +#include +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 B(); +wit::guest_owned C(wit::string a, wit::string b); +} // namespace strings +} // namespace foo +} // namespace foo +} // namespace exports + +#endif From 9bfa6689e335f32f25c759e0ee6aa809aaeb3532 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 25 Feb 2024 22:09:59 +0100 Subject: [PATCH 087/672] more work on native --- crates/cpp/helper-types/wit-host.h | 15 ++-- crates/cpp/src/lib.rs | 12 ++- crates/cpp/tests/native_strings/main.cpp | 0 .../native_strings/the_world_cpp_native.h | 1 + .../tests/native_strings/the_world_native.cpp | 80 +++++++++++++++++++ 5 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 crates/cpp/tests/native_strings/main.cpp create mode 100644 crates/cpp/tests/native_strings/the_world_native.cpp diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 49dcccc17..7056262fc 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -9,12 +9,15 @@ #define WIT_HOST_WAMR #endif -#ifdef WIT_HOST_DIRECT -#define WIT_WASI64 -#endif +// #ifdef WIT_HOST_DIRECT +// #define WIT_WASI64 +// #endif namespace wit { -#ifdef WIT_WASI64 +#ifdef WIT_HOST_DIRECT +typedef intptr_t guest_address; +typedef size_t guest_size; +#elif defined(WIT_WASI64) typedef uint64_t guest_address; typedef uint64_t guest_size; #else @@ -28,9 +31,11 @@ typedef uint32_t guest_size; #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 // host code never de-allocates directly class string { @@ -115,7 +120,7 @@ template class guest_owned : public T { #ifdef WIT_HOST_WAMR wasm_function_inst_t f, WASMExecEnv *e #elif defined(WIT_HOST_DIRECT) - , void (*f)(guest_address) + void (*f)(guest_address) #endif ) : T(std::move(t)), data_(a), free_func(f) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index fba4652fe..a1e10204d 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -751,8 +751,8 @@ impl CppInterfaceGenerator<'_> { self.resolve.wasm_signature(AbiVariant::GuestExport, func) }; let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); - if self.gen.opts.short_cut { } else - if self.gen.opts.host { + if self.gen.opts.short_cut { + } else if self.gen.opts.host { self.gen.c_src.src.push_str("static "); } else { let func_name = &func.name; @@ -800,7 +800,13 @@ impl CppInterfaceGenerator<'_> { // else { // first_arg = false; // } - let ptrtype = if !self.gen.opts.host { "intptr_t"} else if self.gen.opts.wasm64 {"int64_t"} else {"int32_t"}; + let ptrtype = if !self.gen.opts.host { + "intptr_t" + } else if self.gen.opts.wasm64 { + "int64_t" + } else { + "int32_t" + }; self.gen.c_src.src.push_str(ptrtype); self.gen.c_src.src.push_str(" resultptr"); params.push("resultptr".into()); diff --git a/crates/cpp/tests/native_strings/main.cpp b/crates/cpp/tests/native_strings/main.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/crates/cpp/tests/native_strings/the_world_cpp_native.h b/crates/cpp/tests/native_strings/the_world_cpp_native.h index 0fa5229ce..ea0e50340 100644 --- a/crates/cpp/tests/native_strings/the_world_cpp_native.h +++ b/crates/cpp/tests/native_strings/the_world_cpp_native.h @@ -4,6 +4,7 @@ #include #include #include +#define WIT_HOST_DIRECT #include namespace foo { namespace foo { 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..96288835a --- /dev/null +++ b/crates/cpp/tests/native_strings/the_world_native.cpp @@ -0,0 +1,80 @@ +// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT! +#include "the_world_cpp_native.h" +extern "C" +void fooX3AfooX2FstringsX00a(intptr_t arg0, size_t arg1) { + auto len0 = arg1; + + foo::foo::strings::A(std::string_view((char const*)(arg0), len0)); +} +extern "C" +void fooX3AfooX2FstringsX00b(intptr_t resultptr) { + auto result0 = foo::foo::strings::B(); + auto const &vec1 = result0; + auto ptr1 = vec1.data(); + auto len1 = vec1.size(); + *((size_t *)(resultptr + 8)) = len1; + *((intptr_t *)(resultptr + 0)) = ptr1; +} +extern "C" +void fooX3AfooX2FstringsX00c(intptr_t arg0, size_t arg1, intptr_t arg2, + size_t arg3, intptr_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 *)(resultptr + 8)) = len3; + *((intptr_t *)(resultptr + 0)) = ptr3; +} +extern "C" __attribute__((import_module("foo:foo/strings"))) + __attribute__((import_name("a"))) void fooX3AfooX2FstringsX23a(intptr_t, + size_t); +void exports::foo::foo::strings::A(wit::string x) { + auto const &vec0 = x; + auto ptr0 = vec0.data(); + auto len0 = vec0.size(); + fooX3AfooX2FstringsX23a(ptr0, len0); +} +extern "C" __attribute__((import_module("foo:foo/strings"))) + __attribute__((import_name("b"))) intptr_t + fooX3AfooX2FstringsX23b(); +extern "C" +__attribute__((__weak__, __export_name__("cabi_post_foo:foo/strings#b"))) void +cabi_post_fooX3AfooX2FstringsX23b(intptr_t arg0); +wit::guest_owned exports::foo::foo::strings::B() { + auto ret = fooX3AfooX2FstringsX23b(); + intptr_t l0 = *((intptr_t const *)(ret + 0)); + size_t l1 = *((size_t const *)(ret + 8)); + auto len2 = l1; + + return wit::guest_owned (std::string_view((char const*)l0, len2), ret, cabi_post_fooX3AfooX2FstringsX23b); +// wit::string((l0), len2); +} +extern "C" +__attribute__((__weak__, __export_name__("cabi_post_foo:foo/strings#c"))) void +cabi_post_fooX3AfooX2FstringsX23c(intptr_t arg0); +extern "C" __attribute__((import_module("foo:foo/strings"))) + __attribute__((import_name("c"))) + intptr_t fooX3AfooX2FstringsX23c(intptr_t, size_t, intptr_t, size_t); +wit::guest_owned 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); + intptr_t l2 = *((intptr_t const *)(ret + 0)); + size_t l3 = *((size_t const *)(ret + 8)); + auto len4 = l3; + + return wit::guest_owned (std::string_view((char const*)l2, len4), ret, cabi_post_fooX3AfooX2FstringsX23c); +// return wit::string((char const *)(l2), len4); +} + +// Component Adapters From 29b68edb156d58a9a4c14748d42c576349067b55 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 25 Feb 2024 22:35:10 +0100 Subject: [PATCH 088/672] mostly working native example --- crates/cpp/helper-types/wit-host.h | 12 ++++++++ crates/cpp/tests/native_strings/Makefile | 2 +- crates/cpp/tests/native_strings/main.cpp | 30 +++++++++++++++++++ .../native_strings/the_world_cpp_native.h | 4 +++ 4 files changed, 47 insertions(+), 1 deletion(-) diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 7056262fc..29879cc55 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -2,6 +2,7 @@ #include #include +#include #include #include "wit-common.h" @@ -17,6 +18,8 @@ namespace wit { #ifdef WIT_HOST_DIRECT typedef intptr_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); #elif defined(WIT_WASI64) typedef uint64_t guest_address; typedef uint64_t guest_size; @@ -58,6 +61,14 @@ class string { guest_address data() const { return data_; } guest_size size() const { return length; } // add a convenient way to create a string + + static string from_view(std::string_view v) { +#if defined(WIT_HOST_DIRECT) + void* addr = cabi_realloc(nullptr, 0, 1, v.length()); +#endif + memcpy(addr, v.data(), v.length()); + return string((guest_address)addr, v.length()); + } }; template @@ -130,6 +141,7 @@ template class guest_owned : public T { #endif { } + T const& inner() const { return *static_cast(this); } #ifdef WIT_HOST_WAMR // not necessary? as the only way to get a guest_owned object diff --git a/crates/cpp/tests/native_strings/Makefile b/crates/cpp/tests/native_strings/Makefile index 4584d1f1a..5a3311bad 100644 --- a/crates/cpp/tests/native_strings/Makefile +++ b/crates/cpp/tests/native_strings/Makefile @@ -10,7 +10,7 @@ libstrings.so: the_world.pie.o guest.pie.o $(CXX) $(CXXFLAGS) -fPIE -o $@ -c $^ app-strings: the_world_native.o main.o - $(CXX) $(CXXFLAGS) -o $@ $^ -lstrings + $(CXX) $(CXXFLAGS) -o $@ $^ -L. -lstrings bindgen: wit/strings.wit $(WIT_BINDGEN) cpp wit --wasm64 --format diff --git a/crates/cpp/tests/native_strings/main.cpp b/crates/cpp/tests/native_strings/main.cpp index e69de29bb..7c989a17f 100644 --- a/crates/cpp/tests/native_strings/main.cpp +++ b/crates/cpp/tests/native_strings/main.cpp @@ -0,0 +1,30 @@ + +#include "the_world_cpp_native.h" +#include + +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; + + 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; //valgrind complains + return 0; +} diff --git a/crates/cpp/tests/native_strings/the_world_cpp_native.h b/crates/cpp/tests/native_strings/the_world_cpp_native.h index ea0e50340..984741e97 100644 --- a/crates/cpp/tests/native_strings/the_world_cpp_native.h +++ b/crates/cpp/tests/native_strings/the_world_cpp_native.h @@ -6,6 +6,10 @@ #include #define WIT_HOST_DIRECT #include + +extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align, + size_t new_size); + namespace foo { namespace foo { namespace strings { From 3e7c68a4fcd34c6a3ed5519f9d4d8069c185700f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 25 Feb 2024 23:09:07 +0100 Subject: [PATCH 089/672] build wasm binary as well --- crates/cpp/tests/native_strings/.gitignore | 3 +++ crates/cpp/tests/native_strings/Makefile | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 crates/cpp/tests/native_strings/.gitignore 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 index 5a3311bad..f3f024d1e 100644 --- a/crates/cpp/tests/native_strings/Makefile +++ b/crates/cpp/tests/native_strings/Makefile @@ -15,3 +15,6 @@ app-strings: the_world_native.o main.o bindgen: wit/strings.wit $(WIT_BINDGEN) cpp wit --wasm64 --format $(WIT_BINDGEN) cpp wit --wasm64 --format --direct + +guest.wasm: the_world.cpp guest.cpp + /opt/wasi-sdk/bin/clang++ -o $@ $^ $(CXXFLAGS) \ No newline at end of file From d34910fb679ec716678828a8851ce5699872cac9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 1 Mar 2024 00:41:24 +0100 Subject: [PATCH 090/672] adapt to provenance --- Cargo.lock | 78 +++++++++++++++---------------------------- crates/cpp/src/lib.rs | 17 ++++++++++ 2 files changed, 44 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9e00d2f6c..bba13dd78 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1382,7 +1382,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", + "wasm-encoder 0.201.0", "wit-bindgen-core", "wit-component", "wit-parser 0.201.0", @@ -1692,18 +1692,11 @@ dependencies = [ "leb128", ] -[[package]] -name = "wasm-encoder" -version = "0.200.0" -source = "git+https://github.com/bytecodealliance/wasm-tools.git#a9223be96f65d09ddbcf7adc8cc560a832f26f7f" -dependencies = [ - "leb128", -] - [[package]] name = "wasm-metadata" -version = "0.200.0" -source = "git+https://github.com/bytecodealliance/wasm-tools.git#a9223be96f65d09ddbcf7adc8cc560a832f26f7f" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2" dependencies = [ "anyhow", "indexmap", @@ -1711,8 +1704,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", - "wasmparser 0.200.0", + "wasm-encoder 0.201.0", + "wasmparser 0.201.0", ] [[package]] @@ -1738,8 +1731,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.200.0" -source = "git+https://github.com/bytecodealliance/wasm-tools.git#a9223be96f65d09ddbcf7adc8cc560a832f26f7f" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708" dependencies = [ "bitflags 2.4.2", "indexmap", @@ -1791,7 +1785,7 @@ dependencies = [ "wasmtime-jit", "wasmtime-runtime", "wasmtime-winch", - "wat 1.200.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wat", "windows-sys 0.52.0", ] @@ -2117,19 +2111,7 @@ dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.200.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wast" -version = "200.0.0" -source = "git+https://github.com/bytecodealliance/wasm-tools.git#a9223be96f65d09ddbcf7adc8cc560a832f26f7f" -dependencies = [ - "bumpalo", - "leb128", - "memchr", - "unicode-width", - "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", + "wasm-encoder 0.201.0", ] [[package]] @@ -2138,15 +2120,7 @@ version = "1.201.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453d5b37a45b98dee4f4cb68015fc73634d7883bbef1c65e6e9c78d454cf3f32" dependencies = [ - "wast 200.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wat" -version = "1.200.0" -source = "git+https://github.com/bytecodealliance/wasm-tools.git#a9223be96f65d09ddbcf7adc8cc560a832f26f7f" -dependencies = [ - "wast 200.0.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", + "wast 201.0.0", ] [[package]] @@ -2396,7 +2370,7 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", + "wasm-encoder 0.201.0", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -2411,8 +2385,8 @@ dependencies = [ "clap", "heck", "test-artifacts", - "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", - "wasmparser 0.200.0", + "wasm-encoder 0.201.0", + "wasmparser 0.201.0", "wasmtime", "wasmtime-wasi", "wit-bindgen-c", @@ -2443,7 +2417,7 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", + "wasm-encoder 0.201.0", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", @@ -2458,7 +2432,7 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", + "wasm-encoder 0.201.0", "wasm-metadata", "wasmparser 0.201.0", "wit-bindgen-core", @@ -2533,8 +2507,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.200.0" -source = "git+https://github.com/bytecodealliance/wasm-tools.git#a9223be96f65d09ddbcf7adc8cc560a832f26f7f" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825" dependencies = [ "anyhow", "bitflags 2.4.2", @@ -2543,11 +2518,11 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", + "wasm-encoder 0.201.0", "wasm-metadata", - "wasmparser 0.200.0", - "wat 1.200.0 (git+https://github.com/bytecodealliance/wasm-tools.git)", - "wit-parser 0.200.0", + "wasmparser 0.201.0", + "wat", + "wit-parser 0.201.0", ] [[package]] @@ -2569,8 +2544,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.200.0" -source = "git+https://github.com/bytecodealliance/wasm-tools.git#a9223be96f65d09ddbcf7adc8cc560a832f26f7f" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "196d3ecfc4b759a8573bf86a9b3f8996b304b3732e4c7de81655f875f6efdca6" dependencies = [ "anyhow", "id-arena", diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index a1e10204d..20b1ffa80 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1810,6 +1810,19 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { results.push(format!("(int32_t) {}", op)); } Bitcast::None => results.push(op.to_string()), + Bitcast::P64ToI64 => todo!(), + Bitcast::I64ToP64 => todo!(), + Bitcast::P64ToP => todo!(), + Bitcast::PToP64 => todo!(), + Bitcast::I32ToP => todo!(), + Bitcast::PToI32 => todo!(), + Bitcast::PToL => todo!(), + Bitcast::LToP => todo!(), + Bitcast::I32ToL => todo!(), + Bitcast::LToI32 => todo!(), + Bitcast::I64ToL => todo!(), + Bitcast::LToI64 => todo!(), + Bitcast::Sequence(_) => todo!(), } } } @@ -2642,6 +2655,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } self.src.push_str("}\n"); } + abi::Instruction::PointerLoad { offset } => todo!(), + abi::Instruction::LengthLoad { offset } => todo!(), + abi::Instruction::PointerStore { offset } => todo!(), + abi::Instruction::LengthStore { offset } => todo!(), } } From 9e7ae9302a4e6f36a77913dcc44e093020622069 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 1 Mar 2024 23:54:27 +0100 Subject: [PATCH 091/672] implement ptr load + store --- crates/cpp/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 20b1ffa80..dadf5ebff 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2655,10 +2655,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } self.src.push_str("}\n"); } - abi::Instruction::PointerLoad { offset } => todo!(), - abi::Instruction::LengthLoad { offset } => todo!(), - abi::Instruction::PointerStore { offset } => todo!(), - abi::Instruction::LengthStore { offset } => todo!(), + abi::Instruction::PointerLoad { offset } => self.load("uint8_t*", *offset, operands, results), + abi::Instruction::LengthLoad { offset } => self.load("size_t", *offset, operands, results), + abi::Instruction::PointerStore { offset } => self.store("uint8_t*", *offset, operands), + abi::Instruction::LengthStore { offset } => self.store("size_t", *offset, operands), } } From 0ed52a7c0cf48ac7e6086a961b8910974e85d692 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Mar 2024 12:16:34 +0100 Subject: [PATCH 092/672] strings compile again --- crates/cpp/helper-types/wit-host.h | 32 ++++++++++++++++++++++++++++-- crates/cpp/src/lib.rs | 12 +++++------ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 29879cc55..bdfaeaaba 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -62,13 +62,41 @@ class string { guest_size size() const { return length; } // add a convenient way to create a string - static string from_view(std::string_view v) { #if defined(WIT_HOST_DIRECT) + static string from_view(std::string_view v) { void* addr = cabi_realloc(nullptr, 0, 1, v.length()); -#endif 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 }; template diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index dadf5ebff..dd9fa8627 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1905,7 +1905,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); self.push_str(&format!("auto {} = {}.size();\n", len, val)); } else { - self.push_str(&format!("auto {} = (intptr_t)({}.data());\n", ptr, val)); + self.push_str(&format!("auto {} = (uintptr_t)({}.data());\n", ptr, val)); self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val)); } if realloc.is_none() { @@ -1929,7 +1929,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); self.push_str(&format!("auto {} = {}.size();\n", len, val)); } else { - self.push_str(&format!("auto {} = (intptr_t)({}.data());\n", ptr, val)); + self.push_str(&format!("auto {} = (uintptr_t)({}.data());\n", ptr, val)); self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val)); } if realloc.is_none() { @@ -1956,7 +1956,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); self.push_str(&format!("auto {} = {}.size();\n", len, val)); } else { - self.push_str(&format!("auto {} = (intptr_t)({}.data());\n", ptr, val)); + self.push_str(&format!("auto {} = (uintptr_t)({}.data());\n", ptr, val)); self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val)); } if realloc.is_none() { @@ -2655,9 +2655,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } self.src.push_str("}\n"); } - abi::Instruction::PointerLoad { offset } => self.load("uint8_t*", *offset, operands, results), + abi::Instruction::PointerLoad { offset } => self.load("uintptr_t", *offset, operands, results), abi::Instruction::LengthLoad { offset } => self.load("size_t", *offset, operands, results), - abi::Instruction::PointerStore { offset } => self.store("uint8_t*", *offset, operands), + abi::Instruction::PointerStore { offset } => self.store("uintptr_t", *offset, operands), abi::Instruction::LengthStore { offset } => self.store("size_t", *offset, operands), } } @@ -2674,7 +2674,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { }; let static_var = if self.gen.in_import { "" } else { "static " }; uwriteln!(self.src, "{static_var}{tp} ret_area[{elems}];"); - uwriteln!(self.src, "int32_t ptr{tmp} = int32_t(&ret_area);"); + uwriteln!(self.src, "intptr_t ptr{tmp} = intptr_t(&ret_area);"); format!("ptr{}", tmp) } From da4dbb7aff091410b66323d954189daa08883b52 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Mar 2024 13:06:35 +0100 Subject: [PATCH 093/672] first part of a rework towards proper guest based resources --- crates/cpp/helper-types/wit-common.h | 16 ---------------- crates/cpp/helper-types/wit-guest.h | 8 ++++++++ crates/cpp/helper-types/wit-host.h | 27 +++++++++++++++++++++++++++ crates/cpp/test_headers/wasm_export.h | 24 +----------------------- 4 files changed, 36 insertions(+), 39 deletions(-) diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h index c7ae6144c..89fa31df0 100644 --- a/crates/cpp/helper-types/wit-common.h +++ b/crates/cpp/helper-types/wit-common.h @@ -56,22 +56,6 @@ class ResourceImportBase { ResourceImportBase &operator=(ResourceImportBase const &r) = delete; }; -template class ResourceExportBase { - static std::map 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(id, std::move(value))); - return id; - } - static void remove_resource(int32_t id) { resources.erase(id); } -}; template struct Owned { T *ptr; }; diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 4e8b7539d..154762772 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -73,4 +73,12 @@ class vector { return wit::span(data_, length); } }; + +template class ResourceExportBase { + public: + int32_t handle; + + ResourceExportBase() : handle(R::resource_new(this)) {} + ~ResourceExportBase() { R::resource_drop(handle); } +}; } // namespace wit diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index bdfaeaaba..098d41458 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -4,6 +4,7 @@ #include #include #include +#include #include "wit-common.h" #ifndef WIT_HOST_DIRECT @@ -31,6 +32,7 @@ typedef uint32_t guest_size; #ifdef WIT_HOST_WAMR #include +#include #endif namespace wit { @@ -179,4 +181,29 @@ template class guest_owned : public T { // } #endif }; + +template class ResourceExportBase { + static std::map 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(id, std::move(value))); + return id; + } + static std::optional remove_resource(int32_t id) { + auto iter = resources.find(id); + std::optional result; + if (iter!=resources.end()) { + result = std::move(*iter); + resources.erase(iter); + } + return std::move(result); + } +}; } // namespace wit diff --git a/crates/cpp/test_headers/wasm_export.h b/crates/cpp/test_headers/wasm_export.h index e37c4ae5a..0b4b48aff 100644 --- a/crates/cpp/test_headers/wasm_export.h +++ b/crates/cpp/test_headers/wasm_export.h @@ -6,23 +6,6 @@ typedef WASMExecEnv* wasm_exec_env_t; struct WASMModuleInstanceCommon; typedef WASMModuleInstanceCommon* wasm_module_inst_t; typedef void* wasm_function_inst_t; -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; 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 { @@ -32,10 +15,5 @@ struct NativeSymbol { 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, wasm_val_t*, uint32_t, wasm_val_t*); +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*); -#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)}} From 7ea664d55864b006edcc038b6a4eb8490087a8ab Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Mar 2024 16:58:56 +0100 Subject: [PATCH 094/672] move extern "C" outside of definition, fix im+export of the same resource --- crates/cpp/src/lib.rs | 106 ++++++--------------------- crates/cpp/test_headers/wasm_c_api.h | 25 +++++++ 2 files changed, 46 insertions(+), 85 deletions(-) create mode 100644 crates/cpp/test_headers/wasm_c_api.h diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index dd9fa8627..ff589dc6d 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -82,6 +82,8 @@ struct Cpp { opts: Opts, c_src: SourceWithState, h_src: SourceWithState, + c_src_head: Source, + extern_c_decls: Source, dependencies: Includes, includes: Vec, host_functions: HashMap>, @@ -198,7 +200,7 @@ impl WorldGenerator for Cpp { // self.sizes.fill(resolve); if !self.opts.host_side() { uwriteln!( - self.c_src.src, + self.c_src_head, r#"#include "{}_cpp.h" #include // realloc @@ -255,6 +257,7 @@ impl WorldGenerator for Cpp { 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)); @@ -318,10 +321,12 @@ impl WorldGenerator for Cpp { &mut self, _resolve: &Resolve, _world: WorldId, - _types: &[(&str, TypeId)], + types: &[(&str, TypeId)], _files: &mut Files, ) { - todo!() + for i in types.iter() { + uwriteln!(self.h_src.src, "// import_type {}", i.0); + } } fn finish( @@ -436,94 +441,20 @@ impl WorldGenerator for Cpp { } } if self.opts.host_side() && self.dependencies.needs_exported_resources { - // let world_name = &self.world; uwriteln!(c_str.src, "template std::map wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}::resources;"); } - // if self.dependencies.needs_exported_resources { - // let namespace = namespace(resolve, &TypeOwner::World(world_id), false); - // h_str.change_namespace(&namespace); - // // this is export, not host - // uwriteln!( - // h_str.src, - // "template - // class {RESOURCE_EXPORT_BASE_CLASS_NAME} {{ - // static std::map 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(id, std::move(value))); - // return id; - // }} - // static void remove_resource(int32_t id) {{ - // resources.erase(id); - // }} - // }}; - // template struct {OWNED_CLASS_NAME} {{ - // T *ptr; - // }};" - // ); - // } - // if self.dependencies.needs_imported_resources { - // // somehow spaces get removed, newlines remain (problem occurs before const&) - // // TODO: should into_handle become && ??? - // let namespace = namespace(resolve, &TypeOwner::World(world_id), false); - // h_str.change_namespace(&namespace); - // uwriteln!( - // h_str.src, - // "class {RESOURCE_IMPORT_BASE_CLASS_NAME} {{ - // static const int32_t invalid = -1; - // protected: - // int32_t handle; - // public: - // {RESOURCE_IMPORT_BASE_CLASS_NAME}(int32_t h=invalid) : handle(h) {{}} - // {RESOURCE_IMPORT_BASE_CLASS_NAME}({RESOURCE_IMPORT_BASE_CLASS_NAME}&&r) - // : handle(r.handle) {{ - // r.handle=invalid; - // }} - // {RESOURCE_IMPORT_BASE_CLASS_NAME}({RESOURCE_IMPORT_BASE_CLASS_NAME} - // const&) = delete; - // void set_handle(int32_t h) {{ handle=h; }} - // int32_t get_handle() const {{ return handle; }} - // int32_t into_handle() {{ - // int32_t h= handle; - // handle= invalid; - // return h; - // }} - // {RESOURCE_IMPORT_BASE_CLASS_NAME}& operator=({RESOURCE_IMPORT_BASE_CLASS_NAME}&&r) {{ - // assert(handle<0); - // handle= r.handle; - // r.handle= invalid; - // return *this; - // }} - // {RESOURCE_IMPORT_BASE_CLASS_NAME}& operator=({RESOURCE_IMPORT_BASE_CLASS_NAME} - // const&r) = delete; - // }};" - // ); - // } 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); - // c_str.push_str(&self.src.c_fns); - - // if self.src.h_defs.len() > 0 { - // h_str.push_str(&self.src.h_defs); - // } - - // h_str.push_str(&self.src.h_fns); uwriteln!(c_str.src, "\n// Component Adapters"); - // c_str.push_str(&self.src.c_adapters); - if !self.opts.short_cut && self.opts.host { uwriteln!( h_str.src, @@ -1303,7 +1234,7 @@ impl CppInterfaceGenerator<'_> { result: &str, ) -> (String, String) { let extern_name = Self::export_name2(module_name, name, AbiVariant::GuestImport); - let import = format!("extern __attribute__((import_module(\"{module_name}\")))\n __attribute__((import_name(\"{name}\")))\n {result} {extern_name}({args});\n"); + let import = format!("extern \"C\" __attribute__((import_module(\"{module_name}\")))\n __attribute__((import_name(\"{name}\")))\n {result} {extern_name}({args});\n"); (extern_name, import) } @@ -1327,7 +1258,7 @@ impl CppInterfaceGenerator<'_> { wasm_type(results[0]) }; let (name, code) = Self::declare_import2(module_name, name, &args, result); - self.gen.c_src.src.push_str(&code); + self.gen.extern_c_decls.push_str(&code); name } @@ -1406,13 +1337,14 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } else { self.gen.dependencies.needs_exported_resources = true; } + self.gen.dependencies.needs_wit = true; let base_type = if definition { format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}<{pascal}>") } else { String::from_str("wit::").unwrap() + RESOURCE_IMPORT_BASE_CLASS_NAME }; - let derive = format!(" : public {world_name}{base_type}"); + 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 { @@ -1611,7 +1543,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> _ty: &wit_bindgen_core::wit_parser::Type, _docs: &wit_bindgen_core::wit_parser::Docs, ) { - todo!() + // I assume I don't need to do anything ... we could create a typedef though } fn type_builtin( @@ -2655,8 +2587,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } self.src.push_str("}\n"); } - abi::Instruction::PointerLoad { offset } => self.load("uintptr_t", *offset, operands, results), - abi::Instruction::LengthLoad { offset } => self.load("size_t", *offset, operands, results), + abi::Instruction::PointerLoad { offset } => { + self.load("uintptr_t", *offset, operands, results) + } + abi::Instruction::LengthLoad { offset } => { + self.load("size_t", *offset, operands, results) + } abi::Instruction::PointerStore { offset } => self.store("uintptr_t", *offset, operands), abi::Instruction::LengthStore { offset } => self.store("size_t", *offset, operands), } 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..cd3012e50 --- /dev/null +++ b/crates/cpp/test_headers/wasm_c_api.h @@ -0,0 +1,25 @@ +#include + +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)}} From eecc85647f74fa26ff57baba6323af258de224f8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 3 Mar 2024 08:53:32 +0100 Subject: [PATCH 095/672] prepare for proper guest resource logic --- crates/cpp/src/lib.rs | 161 +++++++++++++++++++++++++++++------------- 1 file changed, 113 insertions(+), 48 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index ff589dc6d..04734d763 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -651,15 +651,18 @@ impl CppInterfaceGenerator<'_> { .unwrap_or(TypeOwner::World(self.gen.world_id.unwrap())), )); let mut namespace = namespace(self.resolve, &owner, export); - let is_drop = is_drop_method(func); + 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 { object.clone() - } else if is_drop { - "~".to_string() + &object } else { - func.item_name().to_pascal_case() + match is_drop { + SpecialMethod::Drop => "~".to_string() + &object, + SpecialMethod::Dtor => "Dtor".to_string(), + SpecialMethod::ResourceNew => "ResourceNew".to_string(), + SpecialMethod::None => func.item_name().to_pascal_case(), + } } } else { func.name.to_pascal_case() @@ -669,17 +672,31 @@ impl CppInterfaceGenerator<'_> { // print the signature of the guest export (lowered (wasm) function calling into highlevel) fn print_export_signature(&mut self, func: &Function) -> Vec { - let is_drop = is_drop_method(func); - let signature = if is_drop { - WasmSignature { + let is_drop = is_special_method(func); + let signature = match is_drop { + SpecialMethod::Drop => WasmSignature { params: vec![WasmType::I32], results: Vec::new(), indirect_params: false, retptr: false, - } - } else { + }, + SpecialMethod::Dtor => WasmSignature { + params: vec![WasmType::Pointer], + results: Vec::new(), + indirect_params: false, + retptr: false, + }, + SpecialMethod::ResourceNew => WasmSignature { + params: vec![WasmType::Pointer], + results: vec![WasmType::I32], + indirect_params: false, + retptr: false, + }, + SpecialMethod::None => // TODO perhaps remember better names for the arguments - self.resolve.wasm_signature(AbiVariant::GuestExport, func) + { + self.resolve.wasm_signature(AbiVariant::GuestExport, func) + } }; let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); if self.gen.opts.short_cut { @@ -776,10 +793,12 @@ impl CppInterfaceGenerator<'_> { self.func_namespace_name(func, !(import ^ self.gen.opts.host_side())); res.name = func_name_h; res.namespace = namespace; - let is_drop = is_drop_method(func); + let is_drop = is_special_method(func); // we might want to separate c_sig and h_sig // let mut sig = String::new(); - if !matches!(&func.kind, FunctionKind::Constructor(_)) && !is_drop { + if !matches!(&func.kind, FunctionKind::Constructor(_)) + && matches!(is_drop, SpecialMethod::None) + { match &func.results { wit_bindgen_core::wit_parser::Results::Named(n) => { if n.is_empty() { @@ -804,7 +823,7 @@ impl CppInterfaceGenerator<'_> { } } } - if matches!(func.kind, FunctionKind::Static(_)) && !is_drop { + if matches!(func.kind, FunctionKind::Static(_)) && matches!(is_drop, SpecialMethod::None) { res.static_member = true; } for (i, (name, param)) in func.params.iter().enumerate() { @@ -919,8 +938,8 @@ impl CppInterfaceGenerator<'_> { } else { LiftLower::LowerArgsLiftResults }; - if is_drop_method(func) { - match lift_lower { + match is_special_method(func) { + SpecialMethod::Drop => match lift_lower { LiftLower::LiftArgsLowerResults => { let owner = &self.resolve.types[match &func.kind { FunctionKind::Static(id) => *id, @@ -946,38 +965,45 @@ impl CppInterfaceGenerator<'_> { }}" ); } + }, + SpecialMethod::Dtor => self.gen.c_src.src.push_str("// dtor logic\n"), + SpecialMethod::ResourceNew => { + self.gen.c_src.src.push_str("// new logic: call r-new\n"); + //let f = Function { name: String::new(), kind: FunctionKind::Static(Id), params: Vec::new(), results: Vec::new(), docs: Docs::default() }; + } + SpecialMethod::None => { + // normal methods + let namespace = if matches!(func.kind, FunctionKind::Freestanding) { + namespace( + self.resolve, + owner, + matches!(variant, AbiVariant::GuestExport), + ) + } else { + let owner = &self.resolve.types[match &func.kind { + FunctionKind::Static(id) => *id, + FunctionKind::Constructor(id) => *id, + FunctionKind::Method(id) => *id, + FunctionKind::Freestanding => unreachable!(), + }] + .clone(); + let mut namespace = namespace( + self.resolve, + &owner.owner, + matches!(variant, AbiVariant::GuestExport), + ); + namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); + namespace + }; + let mut f = FunctionBindgen::new(self, params); + if !export { + f.namespace = namespace; + f.wamr_signature = Some(wamr::wamr_signature(&f.gen.resolve, func)); + } + abi::call(f.gen.resolve, variant, lift_lower, func, &mut f); + let code = String::from(f.src); + self.gen.c_src.src.push_str(&code); } - } else { - let namespace = if matches!(func.kind, FunctionKind::Freestanding) { - namespace( - self.resolve, - owner, - matches!(variant, AbiVariant::GuestExport), - ) - } else { - let owner = &self.resolve.types[match &func.kind { - FunctionKind::Static(id) => *id, - FunctionKind::Constructor(id) => *id, - FunctionKind::Method(id) => *id, - FunctionKind::Freestanding => unreachable!(), - }] - .clone(); - let mut namespace = namespace( - self.resolve, - &owner.owner, - matches!(variant, AbiVariant::GuestExport), - ); - namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); - namespace - }; - let mut f = FunctionBindgen::new(self, params); - if !export { - f.namespace = namespace; - f.wamr_signature = Some(wamr::wamr_signature(&f.gen.resolve, func)); - } - abi::call(f.gen.resolve, variant, lift_lower, func, &mut f); - let code = String::from(f.src); - self.gen.c_src.src.push_str(&code); } self.gen.c_src.src.push_str("}\n"); // cabi_post @@ -1383,6 +1409,26 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> "{pascal}(wit::{RESOURCE_IMPORT_BASE_CLASS_NAME}&&);\n" ); uwriteln!(self.gen.h_src.src, "{pascal}({pascal}&&) = default;\n"); + } else { + if !self.gen.opts.host_side() { + let func = Function { + name: "[resource-new]".to_string() + &name, + kind: FunctionKind::Static(id), + params: vec![("self".into(), Type::Id(id))], + results: Results::Named(vec![]), + docs: Docs::default(), + }; + self.generate_function(&func, &TypeOwner::Interface(intf), variant); + + let func2 = Function { + name: "[dtor]".to_string() + &name, + kind: FunctionKind::Static(id), + params: vec![("self".into(), Type::Id(id))], + results: Results::Named(vec![]), + docs: Docs::default(), + }; + self.generate_function(&func2, &TypeOwner::Interface(intf), variant); + } } uwriteln!(self.gen.h_src.src, "}};\n"); if definition { @@ -2647,8 +2693,27 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } -fn is_drop_method(func: &Function) -> bool { - matches!(func.kind, FunctionKind::Static(_)) && func.name.starts_with("[resource-drop]") +enum SpecialMethod { + None, + Drop, // ([export]) [resource-drop] + ResourceNew, // [export][resource-new] + Dtor, // [dtor] (guest export only) +} + +fn is_special_method(func: &Function) -> SpecialMethod { + if matches!(func.kind, FunctionKind::Static(_)) { + if func.name.starts_with("[resource-drop]") { + SpecialMethod::Drop + } else if func.name.starts_with("[resource-new]") { + SpecialMethod::ResourceNew + } else if func.name.starts_with("[dtor]") { + SpecialMethod::Dtor + } else { + SpecialMethod::None + } + } else { + SpecialMethod::None + } } // fn is_arg_by_pointer(resolve: &Resolve, ty: &Type) -> bool { From 23f26b1b3579ed081a9407ee709d44d40b713704 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 3 Mar 2024 16:01:59 +0100 Subject: [PATCH 096/672] new and dtor calls --- crates/cpp/src/lib.rs | 73 +++++++++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 04734d763..5bee3842a 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -511,16 +511,21 @@ impl WorldGenerator for Cpp { files.push(&format!("{snake}_cpp_host.h"), h_str.src.as_bytes()); } for (name, content) in self.user_class_files.iter() { - files.push(&name, content.as_bytes()); + // 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()); + } } Ok(()) } } // determine namespace (for the lifted C++ function) -fn namespace(resolve: &Resolve, owner: &TypeOwner, export: bool) -> Vec { +fn namespace(resolve: &Resolve, owner: &TypeOwner, guest_export: bool) -> Vec { let mut result = Vec::default(); - if export { + if guest_export { result.push(String::from("exports")); } match owner { @@ -633,7 +638,7 @@ impl CppInterfaceGenerator<'_> { } } - fn func_namespace_name(&self, func: &Function, export: bool) -> (Vec, String) { + fn func_namespace_name(&self, func: &Function, guest_export: bool) -> (Vec, String) { let (object, owner) = match &func.kind { FunctionKind::Freestanding => None, FunctionKind::Method(i) => Some(i), @@ -650,7 +655,7 @@ impl CppInterfaceGenerator<'_> { .map(|id| TypeOwner::Interface(id)) .unwrap_or(TypeOwner::World(self.gen.world_id.unwrap())), )); - let mut namespace = namespace(self.resolve, &owner, export); + let mut namespace = namespace(self.resolve, &owner, guest_export); let is_drop = is_special_method(func); let func_name_h = if !matches!(&func.kind, FunctionKind::Freestanding) { namespace.push(object.clone()); @@ -658,9 +663,17 @@ impl CppInterfaceGenerator<'_> { object.clone() } else { match is_drop { - SpecialMethod::Drop => "~".to_string() + &object, + SpecialMethod::ResourceDrop => { + if guest_export { + "ResourceDrop".to_string() + } else { + "~".to_string() + &object + } + } SpecialMethod::Dtor => "Dtor".to_string(), SpecialMethod::ResourceNew => "ResourceNew".to_string(), + SpecialMethod::Allocate => "New".to_string(), + // SpecialMethod::Deallocate => "Deallocate".to_string(), SpecialMethod::None => func.item_name().to_pascal_case(), } } @@ -674,7 +687,7 @@ impl CppInterfaceGenerator<'_> { fn print_export_signature(&mut self, func: &Function) -> Vec { let is_drop = is_special_method(func); let signature = match is_drop { - SpecialMethod::Drop => WasmSignature { + SpecialMethod::ResourceDrop => WasmSignature { params: vec![WasmType::I32], results: Vec::new(), indirect_params: false, @@ -692,11 +705,22 @@ impl CppInterfaceGenerator<'_> { indirect_params: false, retptr: false, }, - SpecialMethod::None => - // TODO perhaps remember better names for the arguments - { + SpecialMethod::None => { + // TODO perhaps remember better names for the arguments self.resolve.wasm_signature(AbiVariant::GuestExport, func) } + SpecialMethod::Allocate => WasmSignature { + params: vec![], + results: vec![WasmType::Pointer], + indirect_params: false, + retptr: false, + }, + // SpecialMethod::Deallocate => WasmSignature { + // params: vec![WasmType::Pointer], + // results: vec![], + // indirect_params: false, + // retptr: false, + // }, }; let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); if self.gen.opts.short_cut { @@ -939,7 +963,7 @@ impl CppInterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults }; match is_special_method(func) { - SpecialMethod::Drop => match lift_lower { + SpecialMethod::ResourceDrop => match lift_lower { LiftLower::LiftArgsLowerResults => { let owner = &self.resolve.types[match &func.kind { FunctionKind::Static(id) => *id, @@ -971,6 +995,8 @@ impl CppInterfaceGenerator<'_> { self.gen.c_src.src.push_str("// new logic: call r-new\n"); //let f = Function { name: String::new(), kind: FunctionKind::Static(Id), params: Vec::new(), results: Vec::new(), docs: Docs::default() }; } + SpecialMethod::Allocate => self.gen.c_src.src.push_str("// allocate\n"), + // SpecialMethod::Deallocate => self.gen.c_src.src.push_str("// deallocate\n"), SpecialMethod::None => { // normal methods let namespace = if matches!(func.kind, FunctionKind::Freestanding) { @@ -1429,6 +1455,15 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> }; self.generate_function(&func2, &TypeOwner::Interface(intf), variant); } + let func3 = Function { + name: "$alloc".to_string(), + kind: FunctionKind::Static(id), + // same params as constructor ...template? + params: vec![], + results: Results::Named(vec![]), + docs: Docs::default(), + }; + self.generate_function(&func3, &TypeOwner::Interface(intf), variant); } uwriteln!(self.gen.h_src.src, "}};\n"); if definition { @@ -1441,7 +1476,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } self.gen .user_class_files - .insert(user_filename + ".template", headerfile.src.to_string()); + .insert(user_filename, headerfile.src.to_string()); } } } @@ -2695,19 +2730,25 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { enum SpecialMethod { None, - Drop, // ([export]) [resource-drop] - ResourceNew, // [export][resource-new] - Dtor, // [dtor] (guest export only) + ResourceDrop, // ([export]) [resource-drop] + ResourceNew, // [export][resource-new] + Dtor, // [dtor] (guest export only) + Allocate, // internal: allocate new object (called from generated code) + // Deallocate, // internal: de-allocate object - now Dtor } fn is_special_method(func: &Function) -> SpecialMethod { if matches!(func.kind, FunctionKind::Static(_)) { if func.name.starts_with("[resource-drop]") { - SpecialMethod::Drop + SpecialMethod::ResourceDrop } else if func.name.starts_with("[resource-new]") { SpecialMethod::ResourceNew } else if func.name.starts_with("[dtor]") { SpecialMethod::Dtor + } else if func.name == "$alloc" { + SpecialMethod::Allocate + // } else if func.name == "$dealloc" { + // SpecialMethod::Deallocate } else { SpecialMethod::None } From a6854d64c0ed3502cfa63be005ad6868cbb7c7aa Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 4 Mar 2024 00:03:37 +0100 Subject: [PATCH 097/672] mostly correct special function signatures --- crates/cpp/src/lib.rs | 68 ++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 5bee3842a..548b01a1b 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -12,7 +12,7 @@ use wit_bindgen_core::{ uwrite, uwriteln, wit_parser::{ Docs, Function, FunctionKind, Handle, Int, InterfaceId, Resolve, Results, SizeAlign, Type, - TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, + TypeDefKind, TypeId, TypeOwner, Variant, WorldId, WorldKey, }, Files, InterfaceGenerator, Source, WorldGenerator, }; @@ -722,7 +722,13 @@ impl CppInterfaceGenerator<'_> { // retptr: false, // }, }; - let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + let mut module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + if matches!( + is_drop, + SpecialMethod::ResourceNew | SpecialMethod::ResourceDrop + ) { + module_name = String::from("[export]") + &module_name; + } if self.gen.opts.short_cut { } else if self.gen.opts.host { self.gen.c_src.src.push_str("static "); @@ -821,7 +827,8 @@ impl CppInterfaceGenerator<'_> { // we might want to separate c_sig and h_sig // let mut sig = String::new(); if !matches!(&func.kind, FunctionKind::Constructor(_)) - && matches!(is_drop, SpecialMethod::None) + && !(matches!(is_drop, SpecialMethod::ResourceDrop) + && matches!(abi_variant, AbiVariant::GuestImport)) { match &func.results { wit_bindgen_core::wit_parser::Results::Named(n) => { @@ -847,11 +854,19 @@ impl CppInterfaceGenerator<'_> { } } } - if matches!(func.kind, FunctionKind::Static(_)) && matches!(is_drop, SpecialMethod::None) { + if matches!(func.kind, FunctionKind::Static(_)) + && !(matches!(is_drop, SpecialMethod::ResourceDrop) + && matches!(abi_variant, AbiVariant::GuestImport)) + { res.static_member = true; } for (i, (name, param)) in func.params.iter().enumerate() { - if i == 0 && name == "self" { + if i == 0 + && name == "self" + && (matches!(&func.kind, FunctionKind::Method(_)) + || (matches!(is_drop, SpecialMethod::ResourceDrop) + && matches!(abi_variant, AbiVariant::GuestImport))) + { res.implicit_self = true; continue; } @@ -881,7 +896,7 @@ impl CppInterfaceGenerator<'_> { self.gen.h_src.src.push_str("("); if /*import &&*/ - self.gen.opts.host { + 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(", "); @@ -917,7 +932,7 @@ impl CppInterfaceGenerator<'_> { 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 { + 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(", "); @@ -1406,7 +1421,12 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> }; // destructor { - let name = "[resource-drop]".to_string() + &name; + let name = match variant { + AbiVariant::GuestImport => "[resource-drop]", + AbiVariant::GuestExport => "[dtor]", + } + .to_string() + + &name; let func = Function { name: name, kind: FunctionKind::Static(id), @@ -1425,6 +1445,21 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> FunctionKind::Constructor(mid) => *mid == id, } { 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(), + results: Results::Anon(Type::Id(id)), + docs: Docs::default(), + }; + self.generate_function(&func2, &TypeOwner::Interface(intf), variant); + } } } @@ -1441,29 +1476,20 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> name: "[resource-new]".to_string() + &name, kind: FunctionKind::Static(id), params: vec![("self".into(), Type::Id(id))], - results: Results::Named(vec![]), + results: Results::Anon(Type::S32), docs: Docs::default(), }; self.generate_function(&func, &TypeOwner::Interface(intf), variant); let func2 = Function { - name: "[dtor]".to_string() + &name, + name: "[resource-drop]".to_string() + &name, kind: FunctionKind::Static(id), - params: vec![("self".into(), Type::Id(id))], + params: vec![("id".into(), Type::S32)], results: Results::Named(vec![]), docs: Docs::default(), }; self.generate_function(&func2, &TypeOwner::Interface(intf), variant); } - let func3 = Function { - name: "$alloc".to_string(), - kind: FunctionKind::Static(id), - // same params as constructor ...template? - params: vec![], - results: Results::Named(vec![]), - docs: Docs::default(), - }; - self.generate_function(&func3, &TypeOwner::Interface(intf), variant); } uwriteln!(self.gen.h_src.src, "}};\n"); if definition { @@ -2734,7 +2760,7 @@ enum SpecialMethod { ResourceNew, // [export][resource-new] Dtor, // [dtor] (guest export only) Allocate, // internal: allocate new object (called from generated code) - // Deallocate, // internal: de-allocate object - now Dtor + // Deallocate, // internal: de-allocate object - now Dtor } fn is_special_method(func: &Function) -> SpecialMethod { From e0888a23458431b2385f83bade64d126b9c76dcf Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 4 Mar 2024 00:11:15 +0100 Subject: [PATCH 098/672] fix strings test --- crates/cpp/src/lib.rs | 3 ++- crates/cpp/test_headers/wasm_c_api.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 548b01a1b..c7c44ac94 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -427,6 +427,7 @@ impl WorldGenerator for Cpp { uwriteln!( c_str.src, "#include // wasm-micro-runtime header\n\ + #include \n\ #include " ); @@ -1419,8 +1420,8 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } else { AbiVariant::GuestExport }; - // destructor { + // destructor let name = match variant { AbiVariant::GuestImport => "[resource-drop]", AbiVariant::GuestExport => "[dtor]", diff --git a/crates/cpp/test_headers/wasm_c_api.h b/crates/cpp/test_headers/wasm_c_api.h index cd3012e50..6cb1a09bf 100644 --- a/crates/cpp/test_headers/wasm_c_api.h +++ b/crates/cpp/test_headers/wasm_c_api.h @@ -1,3 +1,4 @@ +#pragma once #include typedef uint8_t wasm_valkind_t; From 1e41196a099503f76617cf0f09768aeb2d496567 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 4 Mar 2024 00:30:18 +0100 Subject: [PATCH 099/672] now all methods have correct signature --- crates/cpp/src/lib.rs | 61 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index c7c44ac94..26284f046 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -32,6 +32,16 @@ enum Flavor { InStruct, } +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 => false, + } + } +} + #[derive(Default)] struct HighlevelSignature { /// this is a constructor or destructor without a written type @@ -427,8 +437,8 @@ impl WorldGenerator for Cpp { uwriteln!( c_str.src, "#include // wasm-micro-runtime header\n\ - #include \n\ - #include " + #include \n\ + #include " ); if c_str.src.len() > 0 { @@ -852,6 +862,9 @@ impl CppInterfaceGenerator<'_> { } wit_bindgen_core::wit_parser::Results::Anon(ty) => { res.result = self.type_name(ty, from_namespace, Flavor::Result(abi_variant)); + if matches!(is_drop, SpecialMethod::Allocate) { + res.result.push('*'); + } } } } @@ -871,10 +884,17 @@ impl CppInterfaceGenerator<'_> { res.implicit_self = true; continue; } - res.arguments.push(( - name.to_snake_case(), - self.type_name(param, &res.namespace, Flavor::Argument(abi_variant)), - )); + if matches!(is_drop, SpecialMethod::Dtor | SpecialMethod::ResourceNew) { + 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 if matches!(func.kind, FunctionKind::Method(_)) && import { @@ -1141,9 +1161,14 @@ impl CppInterfaceGenerator<'_> { } } - fn scoped_type_name(&self, id: TypeId, from_namespace: &Vec) -> String { + fn scoped_type_name( + &self, + id: TypeId, + from_namespace: &Vec, + guest_export: bool, + ) -> String { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner, false); + let namespc = namespace(self.resolve, &ty.owner, guest_export); let mut relative = SourceWithState::default(); relative.namespace = from_namespace.clone(); relative.qualify(&namespc); @@ -1187,8 +1212,12 @@ impl CppInterfaceGenerator<'_> { } }, Type::Id(id) => match &self.resolve.types[*id].kind { - TypeDefKind::Record(_r) => self.scoped_type_name(*id, from_namespace), - TypeDefKind::Resource => self.scoped_type_name(*id, from_namespace), + TypeDefKind::Record(_r) => { + self.scoped_type_name(*id, from_namespace, flavor.is_guest_export()) + } + TypeDefKind::Resource => { + self.scoped_type_name(*id, from_namespace, flavor.is_guest_export()) + } TypeDefKind::Handle(Handle::Own(id)) => { self.type_name(&Type::Id(*id), from_namespace, flavor) } @@ -1197,7 +1226,9 @@ impl CppInterfaceGenerator<'_> { + &self.type_name(&Type::Id(*id), from_namespace, flavor) + ">" } - TypeDefKind::Flags(_f) => self.scoped_type_name(*id, from_namespace), + TypeDefKind::Flags(_f) => { + self.scoped_type_name(*id, from_namespace, flavor.is_guest_export()) + } TypeDefKind::Tuple(t) => { let types = t.types.iter().fold(String::new(), |mut a, b| { if !a.is_empty() { @@ -1208,8 +1239,12 @@ impl CppInterfaceGenerator<'_> { self.gen.dependencies.needs_tuple = true; String::from("std::tuple<") + &types + ">" } - TypeDefKind::Variant(_v) => self.scoped_type_name(*id, from_namespace), - TypeDefKind::Enum(_e) => self.scoped_type_name(*id, from_namespace), + TypeDefKind::Variant(_v) => { + self.scoped_type_name(*id, from_namespace, flavor.is_guest_export()) + } + TypeDefKind::Enum(_e) => { + self.scoped_type_name(*id, from_namespace, flavor.is_guest_export()) + } TypeDefKind::Option(o) => { self.gen.dependencies.needs_optional = true; "std::optional<".to_string() + &self.type_name(o, from_namespace, flavor) + ">" From 984c770a07b764ef2fddf894a94ceef990ded3e2 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 4 Mar 2024 08:48:37 +0100 Subject: [PATCH 100/672] standardize on ResourceNew/Drop name --- crates/cpp/helper-types/wit-guest.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 154762772..937a4be36 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -78,7 +78,7 @@ template class ResourceExportBase { public: int32_t handle; - ResourceExportBase() : handle(R::resource_new(this)) {} - ~ResourceExportBase() { R::resource_drop(handle); } + ResourceExportBase() : handle(R::ResourceNew(this)) {} + ~ResourceExportBase() { R::ResourceDrop(handle); } }; } // namespace wit From e4b9cc20fd4158d9d8d1fcf3d04f22800fecee26 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 4 Mar 2024 23:45:32 +0100 Subject: [PATCH 101/672] special function implementation --- crates/cpp/src/lib.rs | 130 +++++++++++++++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 28 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 26284f046..b74740841 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -649,7 +649,12 @@ impl CppInterfaceGenerator<'_> { } } - fn func_namespace_name(&self, func: &Function, guest_export: bool) -> (Vec, String) { + fn func_namespace_name( + &self, + func: &Function, + guest_export: bool, + cpp_file: bool, + ) -> (Vec, String) { let (object, owner) = match &func.kind { FunctionKind::Freestanding => None, FunctionKind::Method(i) => Some(i), @@ -671,7 +676,11 @@ impl CppInterfaceGenerator<'_> { let func_name_h = if !matches!(&func.kind, FunctionKind::Freestanding) { namespace.push(object.clone()); if let FunctionKind::Constructor(_i) = &func.kind { - object.clone() + if guest_export && cpp_file { + String::from("New") + } else { + object.clone() + } } else { match is_drop { SpecialMethod::ResourceDrop => { @@ -831,7 +840,7 @@ impl CppInterfaceGenerator<'_> { }; let (namespace, func_name_h) = - self.func_namespace_name(func, !(import ^ self.gen.opts.host_side())); + self.func_namespace_name(func, !(import ^ self.gen.opts.host_side()), false); res.name = func_name_h; res.namespace = namespace; let is_drop = is_special_method(func); @@ -935,6 +944,35 @@ impl CppInterfaceGenerator<'_> { if cpp_sig.const_member { self.gen.h_src.src.push_str(" const"); } + let is_special = is_special_method(func); + match is_special { + SpecialMethod::Allocate => { + uwrite!( + self.gen.h_src.src, + "{{\ + return new {};\ + }}", + cpp_sig.namespace.join("::") + ); + // body is inside the header + return; + } + SpecialMethod::Dtor => { + uwrite!( + self.gen.h_src.src, + "{{\ + delete {};\ + }}", + cpp_sig.arguments.get(0).unwrap().0 + ); + // body is inside the header + return; + } + // SpecialMethod::None => todo!(), + // SpecialMethod::ResourceDrop => todo!(), + // SpecialMethod::ResourceNew => todo!(), + _ => (), + } self.gen.h_src.src.push_str(";\n"); drop(cpp_sig); @@ -987,6 +1025,24 @@ impl CppInterfaceGenerator<'_> { //interface: InterfaceId, variant: AbiVariant, ) { + fn class_namespace( + cifg: &CppInterfaceGenerator, + func: &Function, + variant: AbiVariant, + ) -> Vec { + 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), + ); + 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(), @@ -1001,19 +1057,9 @@ impl CppInterfaceGenerator<'_> { match is_special_method(func) { SpecialMethod::ResourceDrop => match lift_lower { LiftLower::LiftArgsLowerResults => { - let owner = &self.resolve.types[match &func.kind { - FunctionKind::Static(id) => *id, - _ => panic!("drop should be static"), - }]; - self.gen.c_src.src.push_str(" "); - let mut namespace = namespace( - self.resolve, - &owner.owner, - matches!(variant, AbiVariant::GuestExport), - ); - namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); + let namespace = class_namespace(self, func, variant); self.gen.c_src.qualify(&namespace); - uwriteln!(self.gen.c_src.src, "remove_resource({});", params[0]); + uwriteln!(self.gen.c_src.src, " remove_resource({});", params[0]); } LiftLower::LowerArgsLiftResults => { let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); @@ -1026,9 +1072,23 @@ impl CppInterfaceGenerator<'_> { ); } }, - SpecialMethod::Dtor => self.gen.c_src.src.push_str("// dtor logic\n"), + SpecialMethod::Dtor => { + let classname = class_namespace(self, func, variant).join("::"); + uwriteln!(self.gen.c_src.src, "{0}::Dtor(({0}*)arg0);", classname); + } SpecialMethod::ResourceNew => { - self.gen.c_src.src.push_str("// new logic: call r-new\n"); + let classname = class_namespace(self, func, variant).join("::"); + let args = func + .params + .iter() + .map(|(nm, _ty)| nm.clone()) + .collect::>() + .join(","); + uwriteln!( + self.gen.c_src.src, + "return {classname}::New({args})->handle;" + ); + // self.gen.c_src.src.push_str("// new logic: call r-new\n"); //let f = Function { name: String::new(), kind: FunctionKind::Static(Id), params: Vec::new(), results: Vec::new(), docs: Docs::default() }; } SpecialMethod::Allocate => self.gen.c_src.src.push_str("// allocate\n"), @@ -2148,7 +2208,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // code.push_str(&n); // code.push_str("::"); // } - results.push(format!("{op}.store_resource(std::move({op}))")); + if self.gen.gen.opts.host_side() { + results.push(format!("{op}.store_resource(std::move({op}))")); + } else { + results.push(format!("{op}->handle")); + } } abi::Instruction::HandleLower { handle: Handle::Borrow(_), @@ -2613,20 +2677,28 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // dbg!(func); self.let_results(func.results.len(), results); let (mut namespace, func_name_h) = - self.gen.func_namespace_name(func, !self.gen.gen.opts.host); + self.gen + .func_namespace_name(func, !self.gen.gen.opts.host, true); if matches!(func.kind, FunctionKind::Method(_)) { let this = operands.remove(0); //self.gen.gen.c_src.qualify(&namespace); - let mut relative = SourceWithState::default(); // relative.namespace = self.namespace.clone(); - relative.qualify(&namespace); - uwrite!( - self.src, - "{}lookup_resource({this})->", - relative.src.to_string() - ); + if self.gen.gen.opts.host_side() { + let mut relative = SourceWithState::default(); + relative.qualify(&namespace); + uwrite!( + self.src, + "{}lookup_resource({this})->", + relative.src.to_string() + ); + } else { + let objtype = namespace.join("::"); + uwrite!(self.src, "(({objtype}*){this})->",); + } } else { - if matches!(func.kind, FunctionKind::Constructor(_)) { + if matches!(func.kind, FunctionKind::Constructor(_)) + && self.gen.gen.opts.host_side() + { let _ = namespace.pop(); } let mut relative = SourceWithState::default(); @@ -2655,7 +2727,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { _ => { assert!(*amt == operands.len()); match &func.kind { - FunctionKind::Constructor(_) if import => { + FunctionKind::Constructor(_) + if import && self.gen.gen.opts.host_side() => + { // strange but works self.src.push_str("this->handle = "); } From 4f05f8b61229b7659d45e3164232fe9ea2ca76c0 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 5 Mar 2024 00:16:38 +0100 Subject: [PATCH 102/672] correct even more guest export --- crates/cpp/src/lib.rs | 228 ++++++++++++++++++++++-------------------- 1 file changed, 119 insertions(+), 109 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index b74740841..c4c0ecd65 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -950,12 +950,18 @@ impl CppInterfaceGenerator<'_> { uwrite!( self.gen.h_src.src, "{{\ - return new {};\ + return new {}({});\ }}", - cpp_sig.namespace.join("::") + cpp_sig.namespace.join("::"), + cpp_sig + .arguments + .iter() + .map(|(arg, _)| arg.clone()) + .collect::>() + .join(", ") ); // body is inside the header - return; + return Vec::default(); } SpecialMethod::Dtor => { uwrite!( @@ -965,8 +971,6 @@ impl CppInterfaceGenerator<'_> { }}", cpp_sig.arguments.get(0).unwrap().0 ); - // body is inside the header - return; } // SpecialMethod::None => todo!(), // SpecialMethod::ResourceDrop => todo!(), @@ -977,7 +981,7 @@ impl CppInterfaceGenerator<'_> { drop(cpp_sig); // we want to separate the lowered signature (wasm) and the high level signature - if !import { + if !import && !matches!(is_special, SpecialMethod::ResourceDrop|SpecialMethod::ResourceNew) { self.print_export_signature(func) } else { // recalulate with c file namespace @@ -1048,119 +1052,125 @@ impl CppInterfaceGenerator<'_> { AbiVariant::GuestExport => !self.gen.opts.host_side(), }; let params = self.print_signature(func, !export); - self.gen.c_src.src.push_str("{\n"); - let lift_lower = if export { - LiftLower::LiftArgsLowerResults - } else { - LiftLower::LowerArgsLiftResults - }; - match is_special_method(func) { - SpecialMethod::ResourceDrop => match lift_lower { - LiftLower::LiftArgsLowerResults => { - let namespace = class_namespace(self, func, variant); - self.gen.c_src.qualify(&namespace); - uwriteln!(self.gen.c_src.src, " remove_resource({});", params[0]); + let special = is_special_method(func); + if !matches!(special, SpecialMethod::Allocate) { + self.gen.c_src.src.push_str("{\n"); + let lift_lower = if export { + LiftLower::LiftArgsLowerResults + } else { + LiftLower::LowerArgsLiftResults + }; + match is_special_method(func) { + SpecialMethod::ResourceDrop => match lift_lower { + LiftLower::LiftArgsLowerResults => { + let namespace = class_namespace(self, func, variant); + self.gen.c_src.qualify(&namespace); + uwriteln!(self.gen.c_src.src, " remove_resource({});", params[0]); + } + LiftLower::LowerArgsLiftResults => { + 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); + }}" + ); + } + }, + SpecialMethod::Dtor => { + let classname = class_namespace(self, func, variant).join("::"); + uwriteln!(self.gen.c_src.src, "{0}::Dtor(({0}*)arg0);", classname); } - LiftLower::LowerArgsLiftResults => { - 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], &[]); + SpecialMethod::ResourceNew => { + + let classname = class_namespace(self, func, variant).join("::"); + let args = func + .params + .iter() + .map(|(nm, _ty)| nm.clone()) + .collect::>() + .join(","); uwriteln!( self.gen.c_src.src, - " if (handle>=0) {{ - {name}(handle); - }}" + "return {classname}::New({args})->handle;" ); + // self.gen.c_src.src.push_str("// new logic: call r-new\n"); + //let f = Function { name: String::new(), kind: FunctionKind::Static(Id), params: Vec::new(), results: Vec::new(), docs: Docs::default() }; } - }, - SpecialMethod::Dtor => { - let classname = class_namespace(self, func, variant).join("::"); - uwriteln!(self.gen.c_src.src, "{0}::Dtor(({0}*)arg0);", classname); - } - SpecialMethod::ResourceNew => { - let classname = class_namespace(self, func, variant).join("::"); - let args = func - .params - .iter() - .map(|(nm, _ty)| nm.clone()) - .collect::>() - .join(","); + SpecialMethod::Allocate => unreachable!(), + // SpecialMethod::Deallocate => self.gen.c_src.src.push_str("// deallocate\n"), + SpecialMethod::None => { + // normal methods + let namespace = if matches!(func.kind, FunctionKind::Freestanding) { + namespace( + self.resolve, + owner, + matches!(variant, AbiVariant::GuestExport), + ) + } else { + let owner = &self.resolve.types[match &func.kind { + FunctionKind::Static(id) => *id, + FunctionKind::Constructor(id) => *id, + FunctionKind::Method(id) => *id, + FunctionKind::Freestanding => unreachable!(), + }] + .clone(); + let mut namespace = namespace( + self.resolve, + &owner.owner, + matches!(variant, AbiVariant::GuestExport), + ); + namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); + namespace + }; + let mut f = FunctionBindgen::new(self, params); + if !export { + f.namespace = namespace; + f.wamr_signature = Some(wamr::wamr_signature(&f.gen.resolve, func)); + } + abi::call(f.gen.resolve, variant, lift_lower, func, &mut f); + 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!(variant, AbiVariant::GuestExport) + && abi::guest_export_needs_post_return(self.resolve, func) + { + let sig = self.resolve.wasm_signature(variant, func); + let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + let export_name = func.core_export_name(Some(&module_name)); + let import_name = CppInterfaceGenerator::export_name2( + &module_name, + &func.name, + AbiVariant::GuestExport, + ); uwriteln!( self.gen.c_src.src, - "return {classname}::New({args})->handle;" + "__attribute__((__weak__, __export_name__(\"cabi_post_{export_name}\")))" ); - // self.gen.c_src.src.push_str("// new logic: call r-new\n"); - //let f = Function { name: String::new(), kind: FunctionKind::Static(Id), params: Vec::new(), results: Vec::new(), docs: Docs::default() }; - } - SpecialMethod::Allocate => self.gen.c_src.src.push_str("// allocate\n"), - // SpecialMethod::Deallocate => self.gen.c_src.src.push_str("// deallocate\n"), - SpecialMethod::None => { - // normal methods - let namespace = if matches!(func.kind, FunctionKind::Freestanding) { - namespace( - self.resolve, - owner, - matches!(variant, AbiVariant::GuestExport), - ) - } else { - let owner = &self.resolve.types[match &func.kind { - FunctionKind::Static(id) => *id, - FunctionKind::Constructor(id) => *id, - FunctionKind::Method(id) => *id, - FunctionKind::Freestanding => unreachable!(), - }] - .clone(); - let mut namespace = namespace( - self.resolve, - &owner.owner, - matches!(variant, AbiVariant::GuestExport), - ); - namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); - namespace - }; - let mut f = FunctionBindgen::new(self, params); - if !export { - f.namespace = namespace; - f.wamr_signature = Some(wamr::wamr_signature(&f.gen.resolve, func)); + 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}", wasm_type(*result)); + params.push(name); } - abi::call(f.gen.resolve, variant, lift_lower, func, &mut f); - 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!(variant, AbiVariant::GuestExport) - && abi::guest_export_needs_post_return(self.resolve, func) - { - let sig = self.resolve.wasm_signature(variant, func); - let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); - let export_name = func.core_export_name(Some(&module_name)); - let import_name = CppInterfaceGenerator::export_name2( - &module_name, - &func.name, - AbiVariant::GuestExport, - ); - uwriteln!( - self.gen.c_src.src, - "__attribute__((__weak__, __export_name__(\"cabi_post_{export_name}\")))" - ); - uwrite!(self.gen.c_src.src, "void cabi_post_{import_name}("); + self.gen.c_src.src.push_str(") {\n"); - 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}", wasm_type(*result)); - params.push(name); - } - 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); - let FunctionBindgen { src, .. } = f; - self.gen.c_src.src.push_str(&src); - 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); + let FunctionBindgen { src, .. } = f; + self.gen.c_src.src.push_str(&src); + self.gen.c_src.src.push_str("}\n"); + } } } From 4c1b79ead369a6eaaf2f2b05fccc62cc73a39fbe Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 5 Mar 2024 00:36:02 +0100 Subject: [PATCH 103/672] proper resource infra calls --- crates/cpp/src/lib.rs | 59 +++++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index c4c0ecd65..a5625a0cb 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -981,7 +981,12 @@ impl CppInterfaceGenerator<'_> { drop(cpp_sig); // we want to separate the lowered signature (wasm) and the high level signature - if !import && !matches!(is_special, SpecialMethod::ResourceDrop|SpecialMethod::ResourceNew) { + if !import + && !matches!( + is_special, + SpecialMethod::ResourceDrop | SpecialMethod::ResourceNew + ) + { self.print_export_signature(func) } else { // recalulate with c file namespace @@ -1063,9 +1068,25 @@ impl CppInterfaceGenerator<'_> { match is_special_method(func) { SpecialMethod::ResourceDrop => match lift_lower { LiftLower::LiftArgsLowerResults => { - let namespace = class_namespace(self, func, variant); - self.gen.c_src.qualify(&namespace); - uwriteln!(self.gen.c_src.src, " remove_resource({});", params[0]); + 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({});", params[0]); + } 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 => { let module_name = @@ -1085,18 +1106,30 @@ impl CppInterfaceGenerator<'_> { uwriteln!(self.gen.c_src.src, "{0}::Dtor(({0}*)arg0);", classname); } SpecialMethod::ResourceNew => { - - let classname = class_namespace(self, func, variant).join("::"); - let args = func - .params - .iter() - .map(|(nm, _ty)| nm.clone()) - .collect::>() - .join(","); + 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 {classname}::New({args})->handle;" + "return {wasm_sig}((uintptr_t){});", + func.params.get(0).unwrap().0 ); + // let classname = class_namespace(self, func, variant).join("::"); + // let args = func + // .params + // .iter() + // .map(|(nm, _ty)| nm.clone()) + // .collect::>() + // .join(","); + // uwriteln!( + // self.gen.c_src.src, + // "return {classname}::New({args})->handle;" + // ); // self.gen.c_src.src.push_str("// new logic: call r-new\n"); //let f = Function { name: String::new(), kind: FunctionKind::Static(Id), params: Vec::new(), results: Vec::new(), docs: Docs::default() }; } From b57cd005ce193ab0c7413bf4275e056c4a20cb09 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 5 Mar 2024 07:51:40 +0100 Subject: [PATCH 104/672] fix import confusion --- crates/cpp/src/lib.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index a5625a0cb..245582b50 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1164,6 +1164,7 @@ impl CppInterfaceGenerator<'_> { f.namespace = namespace; f.wamr_signature = Some(wamr::wamr_signature(&f.gen.resolve, func)); } + f.variant = variant; abi::call(f.gen.resolve, variant, lift_lower, func, &mut f); let code = String::from(f.src); self.gen.c_src.src.push_str(&code); @@ -1817,6 +1818,7 @@ struct FunctionBindgen<'a, 'b> { payloads: Vec, // caching for wasm wamr_signature: Option, + variant: AbiVariant, } impl<'a, 'b> FunctionBindgen<'a, 'b> { @@ -1833,6 +1835,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { blocks: Default::default(), payloads: Default::default(), wamr_signature: None, + variant: AbiVariant::GuestImport, } } @@ -2764,15 +2767,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.push_str(");\n"); } abi::Instruction::Return { amt, func } => { - let import = !self.gen.gen.opts.host; + let import = matches!(self.variant, AbiVariant::GuestImport); match amt { 0 => {} _ => { assert!(*amt == operands.len()); match &func.kind { - FunctionKind::Constructor(_) - if import && self.gen.gen.opts.host_side() => - { + FunctionKind::Constructor(_) if import => { // strange but works self.src.push_str("this->handle = "); } From a504723a5b2c23c82777b685ad7de20b3a8a4e53 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 5 Mar 2024 07:55:54 +0100 Subject: [PATCH 105/672] make all cabi methods extern "C" --- crates/cpp/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 245582b50..aaa548f5b 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -756,7 +756,7 @@ impl CppInterfaceGenerator<'_> { let func_name = &func.name; uwriteln!( self.gen.c_src.src, - r#"__attribute__((__export_name__("{module_name}#{func_name}")))"# + r#"extern "C" __attribute__((__export_name__("{module_name}#{func_name}")))"# ); } let return_via_pointer = signature.retptr && self.gen.opts.host_side(); @@ -1186,7 +1186,7 @@ impl CppInterfaceGenerator<'_> { ); uwriteln!( self.gen.c_src.src, - "__attribute__((__weak__, __export_name__(\"cabi_post_{export_name}\")))" + "extern \"C\" __attribute__((__weak__, __export_name__(\"cabi_post_{export_name}\")))" ); uwrite!(self.gen.c_src.src, "void cabi_post_{import_name}("); From 288dad8a273a52c44d643091e7b751e78e2adc28 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 5 Mar 2024 19:59:32 +0100 Subject: [PATCH 106/672] use const references and define WIT_HOST_DIRECT solves #13 --- crates/cpp/src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index aaa548f5b..7004e3865 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -420,6 +420,9 @@ impl WorldGenerator for Cpp { } } + if self.opts.short_cut { + uwriteln!(h_str.src, "#define WIT_HOST_DIRECT"); + } for include in self.includes.iter() { uwriteln!(h_str.src, "#include {include}"); } @@ -1326,7 +1329,7 @@ impl CppInterfaceGenerator<'_> { self.type_name(&Type::Id(*id), from_namespace, flavor) } TypeDefKind::Handle(Handle::Borrow(id)) => { - "std::reference_wrapper<".to_string() + "std::reference_wrapper" } From fa3e1f9ee668eb0cd24b5155d1a798d4cb0f2dce Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 7 Mar 2024 19:41:47 +0100 Subject: [PATCH 107/672] remove old code --- crates/cpp/src/lib.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7004e3865..13e887972 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -738,12 +738,6 @@ impl CppInterfaceGenerator<'_> { indirect_params: false, retptr: false, }, - // SpecialMethod::Deallocate => WasmSignature { - // params: vec![WasmType::Pointer], - // results: vec![], - // indirect_params: false, - // retptr: false, - // }, }; let mut module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); if matches!( From 6a89dd86c4529c006a1495ef3aada87d0becb034 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 5 Mar 2024 23:53:03 +0100 Subject: [PATCH 108/672] initial rust native prototype --- Cargo.toml | 3 ++ crates/core/src/lib.rs | 45 ++++++++++++++++++++++++++ crates/cpp/src/lib.rs | 62 ++++-------------------------------- crates/rust/src/bindgen.rs | 17 ++++------ crates/rust/src/interface.rs | 57 +++++++++++++++++++-------------- wasm-tools.patch | 15 +++++++++ 6 files changed, 109 insertions(+), 90 deletions(-) create mode 100644 wasm-tools.patch diff --git a/Cargo.toml b/Cargo.toml index b6e714b71..d9fd67386 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,3 +90,6 @@ test-artifacts = { path = 'crates/test-rust-wasm/artifacts' } wit-parser = { workspace = true } wasmparser = { workspace = true } wasm-encoder = { workspace = true } + +[patch.crates-io] +wit-parser = { path = "/home/christof/src/wasm-tools/crates/wit-parser" } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 378d4f340..010bc2c52 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -413,3 +413,48 @@ 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 { + 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/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 13e887972..b592d2f08 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -9,10 +9,10 @@ use std::{ use wit_bindgen_c::{to_c_ident, wasm_type}; use wit_bindgen_core::{ abi::{self, AbiVariant, Bindgen, Bitcast, LiftLower, WasmSignature, WasmType}, - uwrite, uwriteln, + make_external_symbol, uwrite, uwriteln, wit_parser::{ Docs, Function, FunctionKind, Handle, Int, InterfaceId, Resolve, Results, SizeAlign, Type, - TypeDefKind, TypeId, TypeOwner, Variant, WorldId, WorldKey, + TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, }, Files, InterfaceGenerator, Source, WorldGenerator, }; @@ -243,9 +243,7 @@ impl WorldGenerator for Cpp { let binding = Some(name); let mut gen = self.interface(resolve, binding, true, Some(wasm_import_module)); gen.interface = Some(id); - // if self.gen.interfaces_with_types_printed.insert(id) { gen.types(id); - // } let namespace = namespace(resolve, &TypeOwner::Interface(id), false); for (_name, func) in resolve.interfaces[id].functions.iter() { @@ -254,7 +252,6 @@ impl WorldGenerator for Cpp { gen.generate_function(func, &TypeOwner::Interface(id), AbiVariant::GuestImport); } } - // gen.finish(); } fn export_interface( @@ -766,8 +763,7 @@ impl CppInterfaceGenerator<'_> { wasm_type(signature.results[0]) }); self.gen.c_src.src.push_str(" "); - let export_name = - CppInterfaceGenerator::export_name2(&module_name, &func.name, AbiVariant::GuestExport); + let export_name = make_external_symbol(&module_name, &func.name, AbiVariant::GuestExport); self.gen.c_src.src.push_str(&export_name); self.gen.c_src.src.push_str("("); let mut first_arg = true; @@ -1176,11 +1172,8 @@ impl CppInterfaceGenerator<'_> { let sig = self.resolve.wasm_signature(variant, func); let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); let export_name = func.core_export_name(Some(&module_name)); - let import_name = CppInterfaceGenerator::export_name2( - &module_name, - &func.name, - AbiVariant::GuestExport, - ); + let import_name = + make_external_symbol(&module_name, &func.name, AbiVariant::GuestExport); uwriteln!( self.gen.c_src.src, "extern \"C\" __attribute__((__weak__, __export_name__(\"cabi_post_{export_name}\")))" @@ -1388,56 +1381,13 @@ impl CppInterfaceGenerator<'_> { } } - 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() - } - } - - fn make_export_name(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_str("X").unwrap(); - s.push(Self::hexdigit((c as u32 & 0xf0) >> 4)); - s.push(Self::hexdigit(c as u32 & 0xf)); - s - } - }) - .collect() - } - - fn export_name2(module_name: &str, name: &str, variant: AbiVariant) -> String { - let mut res = Self::make_export_name(module_name); - res.push_str(if matches!(variant, AbiVariant::GuestExport) { - "X23" // Hash character - } else { - "X00" // NUL character (some tools use '.' for display) - }); - res.push_str(&Self::make_export_name(name)); - res - } - fn declare_import2( module_name: &str, name: &str, args: &str, result: &str, ) -> (String, String) { - let extern_name = Self::export_name2(module_name, name, AbiVariant::GuestImport); + let extern_name = make_external_symbol(module_name, name, AbiVariant::GuestImport); let import = format!("extern \"C\" __attribute__((import_module(\"{module_name}\")))\n __attribute__((import_name(\"{name}\")))\n {result} {extern_name}({args});\n"); (extern_name, import) } diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 9a3ae3c7b..de95ec83a 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, InterfaceGenerator, RustFlagsRep 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 gen: &'b mut InterfaceGenerator<'a>, @@ -78,21 +78,18 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { sig.push_str(" -> "); sig.push_str(wasm_type(*result)); } + let export_name = make_external_symbol(module_name, name, AbiVariant::GuestImport); uwrite!( self.src, " - #[cfg(target_arch = \"wasm32\")] #[link(wasm_import_module = \"{module_name}\")] extern \"C\" {{ - #[link_name = \"{name}\"] - fn wit_import{sig}; + #[cfg_attr(target_arch = \"wasm32\", link_name = \"{name}\")] + fn {export_name}{sig}; }} - - #[cfg(not(target_arch = \"wasm32\"))] - fn wit_import{sig} {{ unreachable!() }} " ); - "wit_import".to_string() + export_name } fn let_results(&mut self, amt: usize, results: &mut Vec) { @@ -441,7 +438,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { .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};")); diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 5dc4851f7..da5b700a3 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -9,7 +9,10 @@ use std::collections::{BTreeMap, BTreeSet}; 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::*, Source, TypeInfo}; +use wit_bindgen_core::{ + dealias, make_external_component, make_external_symbol, uwrite, uwriteln, wit_parser::*, + Source, TypeInfo, +}; pub struct InterfaceGenerator<'a> { pub src: Source, @@ -196,6 +199,16 @@ impl InterfaceGenerator<'_> { let resource_name = self.resolve.types[resource].name.as_ref().unwrap(); let (_, interface_name) = interface.unwrap(); let module = self.resolve.name_world_key(interface_name); + 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, + ); uwriteln!( self.src, r#" @@ -203,17 +216,13 @@ impl InterfaceGenerator<'_> { unsafe fn _resource_new(val: *mut u8) -> u32 where Self: Sized {{ - #[cfg(not(target_arch = "wasm32"))] - unreachable!(); - - #[cfg(target_arch = "wasm32")] {{ #[link(wasm_import_module = "[export]{module}")] extern "C" {{ - #[link_name = "[resource-new]{resource_name}"] - fn new(_: *mut u8) -> u32; + #[cfg_attr(target_arch = "wasm32", link_name = "[resource-new]{resource_name}")] + fn {external_new}(_: *mut u8) -> u32; }} - new(val) + {external_new}(val) }} }} @@ -221,18 +230,14 @@ unsafe fn _resource_new(val: *mut u8) -> u32 fn _resource_rep(handle: u32) -> *mut u8 where Self: Sized {{ - #[cfg(not(target_arch = "wasm32"))] - unreachable!(); - - #[cfg(target_arch = "wasm32")] {{ #[link(wasm_import_module = "[export]{module}")] extern "C" {{ - #[link_name = "[resource-rep]{resource_name}"] - fn rep(_: u32) -> *mut u8; + #[cfg_attr(target_arch = "wasm32", link_name = "[resource-rep]{resource_name}")] + fn {external_rep}(_: u32) -> *mut u8; }} unsafe {{ - rep(handle) + {external_rep}(handle) }} }} }} @@ -573,11 +578,12 @@ macro_rules! {macro_name} {{ }; let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); let export_name = func.core_export_name(wasm_module_export_name.as_deref()); + let external_name = make_external_component(&(String::from(export_prefix) + &export_name)); uwrite!( self.src, "\ #[export_name = \"{export_prefix}{export_name}\"] - unsafe extern \"C\" fn export_{name_snake}\ + unsafe extern \"C\" fn {external_name}\ ", ); @@ -592,11 +598,13 @@ macro_rules! {macro_name} {{ if abi::guest_export_needs_post_return(self.resolve, func) { let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); + let external_name = + make_external_component(&(String::from(export_prefix) + &export_name)); uwrite!( self.src, "\ #[export_name = \"{export_prefix}cabi_post_{export_name}\"] - unsafe extern \"C\" fn _post_return_{name_snake}\ + unsafe extern \"C\" fn {external_name}\ " ); let params = self.print_post_return_sig(func); @@ -2098,24 +2106,25 @@ impl<'a> {camel}Borrow<'a>{{ }; 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")] {{ #[link(wasm_import_module = "{wasm_import_module}")] extern "C" {{ - #[link_name = "[resource-drop]{name}"] - fn drop(_: u32); + #[cfg_attr(target_arch = "wasm32", link_name = "[resource-drop]{name}")] + fn {export_name}(_: u32); }} - drop(_handle); + {export_name}(_handle); }} }} }} 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(); From 73e5c68d88714e5073bfa93836f8dd6d456b4b0b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 7 Mar 2024 20:35:52 +0100 Subject: [PATCH 109/672] correct export_name --- crates/rust/src/interface.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index da5b700a3..30f7266d8 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -582,9 +582,10 @@ macro_rules! {macro_name} {{ uwrite!( self.src, "\ - #[export_name = \"{export_prefix}{export_name}\"] + #[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); @@ -598,14 +599,16 @@ macro_rules! {macro_name} {{ if abi::guest_export_needs_post_return(self.resolve, func) { let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); - let external_name = - make_external_component(&(String::from(export_prefix) + &export_name)); + let external_name = make_external_component( + &(String::from(export_prefix) + "cabi_post_" + &export_name), + ); uwrite!( self.src, "\ - #[export_name = \"{export_prefix}cabi_post_{export_name}\"] + #[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"); From 0e8874f75bf8a26b7f7410313cd4a84df2e6766b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 7 Mar 2024 23:49:50 +0100 Subject: [PATCH 110/672] fully operational native example, also with Rust guest --- Cargo.lock | 19 +- Cargo.toml | 2 +- crates/cpp/tests/native_strings/main.cpp | 9 +- .../cpp/tests/native_strings/rust/Cargo.lock | 7 + .../cpp/tests/native_strings/rust/Cargo.toml | 12 + .../cpp/tests/native_strings/rust/src/lib.rs | 53 ++++ .../native_strings/rust/src/the_world.rs | 251 ++++++++++++++++++ crates/cpp/tests/native_strings/the_world.cpp | 77 +++--- .../tests/native_strings/the_world_native.cpp | 44 ++- crates/rust/src/interface.rs | 8 +- 10 files changed, 399 insertions(+), 83 deletions(-) create mode 100644 crates/cpp/tests/native_strings/rust/Cargo.lock create mode 100644 crates/cpp/tests/native_strings/rust/Cargo.toml create mode 100644 crates/cpp/tests/native_strings/rust/src/lib.rs create mode 100644 crates/cpp/tests/native_strings/rust/src/the_world.rs diff --git a/Cargo.lock b/Cargo.lock index c1682a97d..bffd852c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1705,7 +1705,7 @@ dependencies = [ "serde_json", "spdx", "wasm-encoder 0.201.0", - "wasmparser 0.201.0", + "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1729,6 +1729,15 @@ dependencies = [ "semver", ] +[[package]] +name = "wasmparser" +version = "0.201.0" +dependencies = [ + "bitflags 2.4.2", + "indexmap", + "semver", +] + [[package]] name = "wasmparser" version = "0.201.0" @@ -2387,7 +2396,7 @@ dependencies = [ "heck", "test-artifacts", "wasm-encoder 0.201.0", - "wasmparser 0.201.0", + "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmtime", "wasmtime-wasi", "wit-bindgen-c", @@ -2435,7 +2444,7 @@ dependencies = [ "test-helpers", "wasm-encoder 0.201.0", "wasm-metadata", - "wasmparser 0.201.0", + "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", "wit-bindgen-core", "wit-component", ] @@ -2525,7 +2534,7 @@ dependencies = [ "serde_json", "wasm-encoder 0.201.0", "wasm-metadata", - "wasmparser 0.201.0", + "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", "wat", "wit-parser 0.201.0", ] @@ -2550,8 +2559,6 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.201.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "196d3ecfc4b759a8573bf86a9b3f8996b304b3732e4c7de81655f875f6efdca6" dependencies = [ "anyhow", "id-arena", diff --git a/Cargo.toml b/Cargo.toml index d9fd67386..e667f1372 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,4 +92,4 @@ wasmparser = { workspace = true } wasm-encoder = { workspace = true } [patch.crates-io] -wit-parser = { path = "/home/christof/src/wasm-tools/crates/wit-parser" } +wit-parser = { path = "../wasm-tools/crates/wit-parser" } diff --git a/crates/cpp/tests/native_strings/main.cpp b/crates/cpp/tests/native_strings/main.cpp index 7c989a17f..0f86198e5 100644 --- a/crates/cpp/tests/native_strings/main.cpp +++ b/crates/cpp/tests/native_strings/main.cpp @@ -19,12 +19,15 @@ 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; + { + 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; //valgrind complains + 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..c896587ed --- /dev/null +++ b/crates/cpp/tests/native_strings/rust/src/lib.rs @@ -0,0 +1,53 @@ +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); +// println!("a received {x}"); + } + + fn b() -> String { + the_world::foo::foo::strings::b() +// "b".into() + } + + fn c(a: String,b: String,) -> String { + the_world::foo::foo::strings::c(&a, &b) + // println!("c received {a}, {b}"); + // "c".into() + } +} + +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..023023764 --- /dev/null +++ b/crates/cpp/tests/native_strings/rust/src/the_world.rs @@ -0,0 +1,251 @@ +// Generated by `wit-bindgen` 0.21.0. DO NOT EDIT! +// Options used: +pub mod foo { + pub mod foo { + #[allow(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::(); + 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::; 16]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 16]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); + #[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::(); + 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 { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit::; 16]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 16]); + let vec0 = a; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let vec1 = b; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + let ptr2 = ret_area.0.as_mut_ptr().cast::(); + #[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::(); + let len5 = l4; + let bytes5 = _rt::Vec::from_raw_parts(l3.cast(), len5, len5); + _rt::string_lift(bytes5) + } + } + + } + + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(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(arg0: *mut u8,arg1: usize,) {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() -> *mut u8 {let result0 = T::b(); + let ptr1 = _RET_AREA.0.as_mut_ptr().cast::(); + let vec2 = (result0.into_bytes()).into_boxed_slice(); + let ptr2 = vec2.as_ptr().cast::(); + let len2 = vec2.len(); + ::core::mem::forget(vec2); + *ptr1.add(8).cast::() = len2; + *ptr1.add(0).cast::<*mut u8>() = ptr2.cast_mut(); + ptr1 + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __post_return_b(arg0: *mut u8,) { + let l0 = *arg0.add(0).cast::<*mut u8>(); + let l1 = *arg0.add(8).cast::(); + _rt::cabi_dealloc(l0, l1, 1); + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_c_cabi(arg0: *mut u8,arg1: usize,arg2: *mut u8,arg3: usize,) -> *mut u8 {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::(); + let vec4 = (result2.into_bytes()).into_boxed_slice(); + let ptr4 = vec4.as_ptr().cast::(); + let len4 = vec4.len(); + ::core::mem::forget(vec4); + *ptr3.add(8).cast::() = len4; + *ptr3.add(0).cast::<*mut u8>() = ptr4.cast_mut(); + ptr3 + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __post_return_c(arg0: *mut u8,) { + let l0 = *arg0.add(0).cast::<*mut u8>(); + let l1 = *arg0.add(8).cast::(); + _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::; 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) -> 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 as *mut u8, 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.21.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.201.0\x10wit-bindgen-rust\x060.21.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 index b323da7c3..4bc8c8aa2 100644 --- a/crates/cpp/tests/native_strings/the_world.cpp +++ b/crates/cpp/tests/native_strings/the_world.cpp @@ -17,79 +17,70 @@ cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { } extern "C" __attribute__((import_module("foo:foo/strings"))) - __attribute__((import_name("a"))) void fooX3AfooX2FstringsX00a(intptr_t, - size_t); +__attribute__((import_name("a"))) void fooX3AfooX2FstringsX00a(uintptr_t, + size_t); +extern "C" __attribute__((import_module("foo:foo/strings"))) +__attribute__((import_name("b"))) void fooX3AfooX2FstringsX00b(uintptr_t); +extern "C" __attribute__((import_module("foo:foo/strings"))) +__attribute__((import_name("c"))) void + fooX3AfooX2FstringsX00c(uintptr_t, size_t, uintptr_t, size_t, uintptr_t); void foo::foo::strings::A(std::string_view x) { auto const &vec0 = x; - auto ptr0 = (intptr_t)(vec0.data()); + auto ptr0 = (uintptr_t)(vec0.data()); auto len0 = (size_t)(vec0.size()); fooX3AfooX2FstringsX00a(ptr0, len0); } -extern "C" __attribute__((import_module("foo:foo/strings"))) - __attribute__((import_name("b"))) void fooX3AfooX2FstringsX00b(intptr_t); wit::string foo::foo::strings::B() { uintptr_t ret_area[2]; intptr_t ptr0 = intptr_t(&ret_area); fooX3AfooX2FstringsX00b(ptr0); - intptr_t l1 = *((intptr_t const *)(ptr0 + 0)); - size_t l2 = *((size_t const *)(ptr0 + 8)); - auto len3 = l2; + auto len1 = *((size_t *)(ptr0 + 8)); - return wit::string((char const *)(l1), len3); + return wit::string((char const *)(*((uintptr_t *)(ptr0 + 0))), len1); } -extern "C" __attribute__((import_module("foo:foo/strings"))) - __attribute__((import_name("c"))) void fooX3AfooX2FstringsX00c( - intptr_t, size_t, intptr_t, size_t, intptr_t); wit::string foo::foo::strings::C(std::string_view a, std::string_view b) { auto const &vec0 = a; - auto ptr0 = (intptr_t)(vec0.data()); + auto ptr0 = (uintptr_t)(vec0.data()); auto len0 = (size_t)(vec0.size()); auto const &vec1 = b; - auto ptr1 = (intptr_t)(vec1.data()); + auto ptr1 = (uintptr_t)(vec1.data()); auto len1 = (size_t)(vec1.size()); uintptr_t ret_area[2]; intptr_t ptr2 = intptr_t(&ret_area); fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2); - intptr_t l3 = *((intptr_t const *)(ptr2 + 0)); - size_t l4 = *((size_t const *)(ptr2 + 4)); - auto len5 = l4; + auto len3 = *((size_t *)(ptr2 + 8)); - return wit::string((char const *)(l3), len5); + return wit::string((char const *)(*((uintptr_t *)(ptr2 + 0))), len3); } -extern "C" -__attribute__((__export_name__("foo:foo/strings#a"))) void -fooX3AfooX2FstringsX23a(intptr_t arg0, size_t arg1) { +extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void +fooX3AfooX2FstringsX23a(uintptr_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"))) intptr_t +extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) uintptr_t fooX3AfooX2FstringsX23b() { auto result0 = exports::foo::foo::strings::B(); static uintptr_t ret_area[2]; intptr_t ptr1 = intptr_t(&ret_area); auto const &vec2 = result0; - auto ptr2 = (intptr_t)(vec2.data()); + auto ptr2 = (uintptr_t)(vec2.data()); auto len2 = (size_t)(vec2.size()); result0.leak(); *((size_t *)(ptr1 + 8)) = len2; - *((intptr_t *)(ptr1 + 0)) = ptr2; + *((uintptr_t *)(ptr1 + 0)) = ptr2; return ptr1; } -extern "C" -__attribute__((__weak__, __export_name__("cabi_post_foo:foo/strings#b"))) void -cabi_post_fooX3AfooX2FstringsX23b(intptr_t arg0) { - intptr_t l0 = *((intptr_t const *)(arg0 + 0)); - size_t l1 = *((size_t const *)(arg0 + 8)); - if ((l1) > 0) { - wit::string::drop_raw((void *)(l0)); +extern "C" __attribute__((__weak__, + __export_name__("cabi_post_foo:foo/strings#b"))) void +cabi_post_fooX3AfooX2FstringsX23b(uintptr_t arg0) { + if ((*((size_t *)(arg0 + 8))) > 0) { + wit::string::drop_raw((void *)(*((uintptr_t *)(arg0 + 0)))); } } -extern "C" -__attribute__((__export_name__("foo:foo/strings#c"))) intptr_t -fooX3AfooX2FstringsX23c(intptr_t arg0, size_t arg1, intptr_t arg2, +extern "C" __attribute__((__export_name__("foo:foo/strings#c"))) uintptr_t +fooX3AfooX2FstringsX23c(uintptr_t arg0, size_t arg1, uintptr_t arg2, size_t arg3) { auto len0 = arg1; @@ -101,21 +92,19 @@ fooX3AfooX2FstringsX23c(intptr_t arg0, size_t arg1, intptr_t arg2, static uintptr_t ret_area[2]; intptr_t ptr3 = intptr_t(&ret_area); auto const &vec4 = result2; - auto ptr4 = (intptr_t)(vec4.data()); + auto ptr4 = (uintptr_t)(vec4.data()); auto len4 = (size_t)(vec4.size()); result2.leak(); *((size_t *)(ptr3 + 8)) = len4; - *((intptr_t *)(ptr3 + 0)) = ptr4; + *((uintptr_t *)(ptr3 + 0)) = ptr4; return ptr3; } -extern "C" -__attribute__((__weak__, __export_name__("cabi_post_foo:foo/strings#c"))) void -cabi_post_fooX3AfooX2FstringsX23c(intptr_t arg0) { - intptr_t l0 = *((intptr_t const *)(arg0 + 0)); - size_t l1 = *((size_t const *)(arg0 + 8)); - if ((l1) > 0) { - wit::string::drop_raw((void *)(l0)); +extern "C" __attribute__((__weak__, + __export_name__("cabi_post_foo:foo/strings#c"))) void +cabi_post_fooX3AfooX2FstringsX23c(uintptr_t arg0) { + if ((*((size_t *)(arg0 + 8))) > 0) { + wit::string::drop_raw((void *)(*((uintptr_t *)(arg0 + 0)))); } } diff --git a/crates/cpp/tests/native_strings/the_world_native.cpp b/crates/cpp/tests/native_strings/the_world_native.cpp index 96288835a..4e8d34822 100644 --- a/crates/cpp/tests/native_strings/the_world_native.cpp +++ b/crates/cpp/tests/native_strings/the_world_native.cpp @@ -1,7 +1,16 @@ // 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(uintptr_t, + size_t); +extern "C" __attribute__((import_module("foo:foo/strings"))) +__attribute__((import_name("b"))) uintptr_t +fooX3AfooX2FstringsX23b(); +extern "C" __attribute__((import_module("foo:foo/strings"))) +__attribute__((import_name("c"))) +uintptr_t fooX3AfooX2FstringsX23c(uintptr_t, size_t, uintptr_t, size_t); extern "C" -void fooX3AfooX2FstringsX00a(intptr_t arg0, size_t arg1) { +void fooX3AfooX2FstringsX00a(uintptr_t arg0, size_t arg1) { auto len0 = arg1; foo::foo::strings::A(std::string_view((char const*)(arg0), len0)); @@ -13,10 +22,10 @@ void fooX3AfooX2FstringsX00b(intptr_t resultptr) { auto ptr1 = vec1.data(); auto len1 = vec1.size(); *((size_t *)(resultptr + 8)) = len1; - *((intptr_t *)(resultptr + 0)) = ptr1; + *((uintptr_t *)(resultptr + 0)) = (uintptr_t)ptr1; } extern "C" -void fooX3AfooX2FstringsX00c(intptr_t arg0, size_t arg1, intptr_t arg2, +void fooX3AfooX2FstringsX00c(uintptr_t arg0, size_t arg1, uintptr_t arg2, size_t arg3, intptr_t resultptr) { auto len0 = arg1; @@ -29,39 +38,27 @@ void fooX3AfooX2FstringsX00c(intptr_t arg0, size_t arg1, intptr_t arg2, auto ptr3 = vec3.data(); auto len3 = vec3.size(); *((size_t *)(resultptr + 8)) = len3; - *((intptr_t *)(resultptr + 0)) = ptr3; + *((uintptr_t *)(resultptr + 0)) = (uintptr_t)ptr3; } -extern "C" __attribute__((import_module("foo:foo/strings"))) - __attribute__((import_name("a"))) void fooX3AfooX2FstringsX23a(intptr_t, - size_t); void exports::foo::foo::strings::A(wit::string x) { auto const &vec0 = x; auto ptr0 = vec0.data(); auto len0 = vec0.size(); fooX3AfooX2FstringsX23a(ptr0, len0); } -extern "C" __attribute__((import_module("foo:foo/strings"))) - __attribute__((import_name("b"))) intptr_t - fooX3AfooX2FstringsX23b(); extern "C" __attribute__((__weak__, __export_name__("cabi_post_foo:foo/strings#b"))) void cabi_post_fooX3AfooX2FstringsX23b(intptr_t arg0); -wit::guest_owned exports::foo::foo::strings::B() { +wit::guest_owned exports::foo::foo::strings::B() { auto ret = fooX3AfooX2FstringsX23b(); - intptr_t l0 = *((intptr_t const *)(ret + 0)); - size_t l1 = *((size_t const *)(ret + 8)); - auto len2 = l1; + auto len0 = *((size_t *)(ret + 8)); - return wit::guest_owned (std::string_view((char const*)l0, len2), ret, cabi_post_fooX3AfooX2FstringsX23b); -// wit::string((l0), len2); + return wit::guest_owned (std::string_view((char const*)*((uintptr_t *)(ret + 0)), len0), ret, cabi_post_fooX3AfooX2FstringsX23b); } extern "C" __attribute__((__weak__, __export_name__("cabi_post_foo:foo/strings#c"))) void cabi_post_fooX3AfooX2FstringsX23c(intptr_t arg0); -extern "C" __attribute__((import_module("foo:foo/strings"))) - __attribute__((import_name("c"))) - intptr_t fooX3AfooX2FstringsX23c(intptr_t, size_t, intptr_t, size_t); -wit::guest_owned exports::foo::foo::strings::C(wit::string a, wit::string b) { +wit::guest_owned exports::foo::foo::strings::C(wit::string a, wit::string b) { auto const &vec0 = a; auto ptr0 = vec0.data(); auto len0 = vec0.size(); @@ -69,12 +66,9 @@ wit::guest_owned exports::foo::foo::strings::C(wit::string a, auto ptr1 = vec1.data(); auto len1 = vec1.size(); auto ret = fooX3AfooX2FstringsX23c(ptr0, len0, ptr1, len1); - intptr_t l2 = *((intptr_t const *)(ret + 0)); - size_t l3 = *((size_t const *)(ret + 8)); - auto len4 = l3; + auto len2 = *((size_t *)(ret + 8)); - return wit::guest_owned (std::string_view((char const*)l2, len4), ret, cabi_post_fooX3AfooX2FstringsX23c); -// return wit::string((char const *)(l2), len4); + return wit::guest_owned (std::string_view((char const*)*((uintptr_t *)(ret + 0)), len2), ret, cabi_post_fooX3AfooX2FstringsX23c); } // Component Adapters diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 30f7266d8..9d183e127 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -599,13 +599,13 @@ macro_rules! {macro_name} {{ if abi::guest_export_needs_post_return(self.resolve, func) { let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); - let external_name = make_external_component( - &(String::from(export_prefix) + "cabi_post_" + &export_name), - ); + let external_name = make_external_component(export_prefix) + + "cabi_post_" + + &make_external_component(&export_name); uwrite!( self.src, "\ - #[target_arch = \"wasm32\", export_name = \"{export_prefix}cabi_post_{export_name}\")] + #[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}\ " From f90809f0734d66d8d8a2ad6db12319acd5427683 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 11 Mar 2024 21:09:36 +0100 Subject: [PATCH 111/672] remove dead code --- crates/cpp/tests/native_strings/rust/src/lib.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/cpp/tests/native_strings/rust/src/lib.rs b/crates/cpp/tests/native_strings/rust/src/lib.rs index c896587ed..0fb041a18 100644 --- a/crates/cpp/tests/native_strings/rust/src/lib.rs +++ b/crates/cpp/tests/native_strings/rust/src/lib.rs @@ -9,18 +9,14 @@ struct MyWorld; impl Guest for MyWorld { fn a(x: String,) { the_world::foo::foo::strings::a(&x); -// println!("a received {x}"); } fn b() -> String { the_world::foo::foo::strings::b() -// "b".into() } fn c(a: String,b: String,) -> String { the_world::foo::foo::strings::c(&a, &b) - // println!("c received {a}, {b}"); - // "c".into() } } From 98c3d7ec0dd6d5a26619ed0eff88f2af0e77d99b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 11 Mar 2024 22:48:35 +0100 Subject: [PATCH 112/672] less import export confusion with direct/host --- crates/cpp/src/lib.rs | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index b592d2f08..db9c71407 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -704,7 +704,7 @@ impl CppInterfaceGenerator<'_> { } // print the signature of the guest export (lowered (wasm) function calling into highlevel) - fn print_export_signature(&mut self, func: &Function) -> Vec { + fn print_export_signature(&mut self, func: &Function, variant: AbiVariant) -> Vec { let is_drop = is_special_method(func); let signature = match is_drop { SpecialMethod::ResourceDrop => WasmSignature { @@ -727,7 +727,7 @@ impl CppInterfaceGenerator<'_> { }, SpecialMethod::None => { // TODO perhaps remember better names for the arguments - self.resolve.wasm_signature(AbiVariant::GuestExport, func) + self.resolve.wasm_signature(variant, func) } SpecialMethod::Allocate => WasmSignature { params: vec![], @@ -744,6 +744,7 @@ impl CppInterfaceGenerator<'_> { module_name = String::from("[export]") + &module_name; } 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 { @@ -763,7 +764,7 @@ impl CppInterfaceGenerator<'_> { wasm_type(signature.results[0]) }); self.gen.c_src.src.push_str(" "); - let export_name = make_external_symbol(&module_name, &func.name, AbiVariant::GuestExport); + let export_name = make_external_symbol(&module_name, &func.name, variant); self.gen.c_src.src.push_str(&export_name); self.gen.c_src.src.push_str("("); let mut first_arg = true; @@ -822,18 +823,19 @@ impl CppInterfaceGenerator<'_> { fn high_level_signature( &mut self, func: &Function, - import: bool, + abi_variant: AbiVariant, + // import: bool, from_namespace: &Vec, ) -> HighlevelSignature { let mut res = HighlevelSignature::default(); - let abi_variant = if import ^ self.gen.opts.host_side() { - AbiVariant::GuestImport - } else { - AbiVariant::GuestExport - }; + // 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, !(import ^ self.gen.opts.host_side()), false); + 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); @@ -899,15 +901,21 @@ impl CppInterfaceGenerator<'_> { } } // 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, import: bool) -> Vec { + fn print_signature( + &mut self, + func: &Function, + variant: AbiVariant, + import: bool, + ) -> Vec { let from_namespace = self.gen.h_src.namespace.clone(); - let cpp_sig = self.high_level_signature(func, import, &from_namespace); + let cpp_sig = self.high_level_signature(func, variant, &from_namespace); if cpp_sig.static_member { self.gen.h_src.src.push_str("static "); } @@ -980,11 +988,11 @@ impl CppInterfaceGenerator<'_> { SpecialMethod::ResourceDrop | SpecialMethod::ResourceNew ) { - self.print_export_signature(func) + 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, import, &c_namespace); + let cpp_sig = self.high_level_signature(func, variant, &c_namespace); let mut params = Vec::new(); self.gen.c_src.src.push_str(&cpp_sig.result); if !cpp_sig.result.is_empty() { @@ -1049,7 +1057,7 @@ impl CppInterfaceGenerator<'_> { AbiVariant::GuestImport => self.gen.opts.host_side(), AbiVariant::GuestExport => !self.gen.opts.host_side(), }; - let params = self.print_signature(func, !export); + 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"); From 0f0c6c3c2bf91c8a59781b1b0bf9922f0a54ffc6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 12 Mar 2024 01:23:01 +0100 Subject: [PATCH 113/672] proper offset in strings --- crates/core/src/abi.rs | 16 ++++++++++++---- crates/cpp/src/lib.rs | 14 +++++++++----- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index ef4e5e10e..d20c89d10 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -1558,7 +1558,9 @@ impl<'a, B: Bindgen> Generator<'a, B> { // and the length into the high address. self.lower(ty); self.stack.push(addr.clone()); - self.emit(&Instruction::LengthStore { offset: offset + 4 }); + self.emit(&Instruction::LengthStore { + offset: offset + self.bindgen.sizes().align(ty) as i32, + }); self.stack.push(addr); self.emit(&Instruction::PointerStore { offset }); } @@ -1732,7 +1734,9 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.stack.push(addr.clone()); self.emit(&Instruction::PointerLoad { offset }); self.stack.push(addr); - self.emit(&Instruction::LengthLoad { offset: offset + 4 }); + self.emit(&Instruction::LengthLoad { + offset: offset + self.bindgen.sizes().align(ty) as i32, + }); self.lift(ty); } @@ -1785,7 +1789,9 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.stack.push(addr.clone()); self.emit(&Instruction::PointerLoad { offset }); self.stack.push(addr); - self.emit(&Instruction::LengthLoad { offset: offset + 4 }); + self.emit(&Instruction::LengthLoad { + offset: offset + self.bindgen.sizes().align(ty) as i32, + }); self.emit(&Instruction::GuestDeallocateString); } @@ -1815,7 +1821,9 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.stack.push(addr.clone()); self.emit(&Instruction::PointerLoad { offset }); self.stack.push(addr); - self.emit(&Instruction::LengthLoad { offset: offset + 4 }); + self.emit(&Instruction::LengthLoad { + offset: offset + self.bindgen.sizes().align(ty) as i32, + }); self.emit(&Instruction::GuestDeallocateList { element }); } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index db9c71407..3429dddb0 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -11,8 +11,8 @@ use wit_bindgen_core::{ abi::{self, AbiVariant, Bindgen, Bitcast, LiftLower, WasmSignature, WasmType}, make_external_symbol, uwrite, uwriteln, wit_parser::{ - Docs, Function, FunctionKind, Handle, Int, InterfaceId, Resolve, Results, SizeAlign, Type, - TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, + Docs, Function, FunctionKind, Handle, Int, InterfaceId, Resolve, Results, SizeAlign, + SizeAlignAbi, SizeAlignSelect, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, }, Files, InterfaceGenerator, Source, WorldGenerator, }; @@ -159,7 +159,11 @@ impl Cpp { in_import: bool, wasm_import_module: Option, ) -> CppInterfaceGenerator<'a> { - let mut sizes = SizeAlign::default(); + let mut sizes = if self.opts.wasm64 { + SizeAlignSelect::Wasm64(SizeAlignAbi::default()) + } else { + SizeAlignSelect::Wasm32(SizeAlignAbi::default()) + }; sizes.fill(resolve); CppInterfaceGenerator { @@ -609,7 +613,7 @@ struct CppInterfaceGenerator<'a> { resolve: &'a Resolve, interface: Option, _name: Option<&'a WorldKey>, - sizes: SizeAlign, + sizes: SizeAlignSelect, in_import: bool, // return_pointer_area_size: usize, // return_pointer_area_align: usize, @@ -2844,7 +2848,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // uwriteln!(self.src, "// finish_block()"); } - fn sizes(&self) -> &wit_bindgen_core::wit_parser::SizeAlign { + fn sizes(&self) -> &wit_bindgen_core::wit_parser::SizeAlignSelect { &self.gen.sizes } From 0dc46e5231a4e1970dd4883e353ed53e7f353c3a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 12 Mar 2024 01:32:55 +0100 Subject: [PATCH 114/672] support wasm64 variants fixes #10 --- Cargo.lock | 8 +++++--- Cargo.toml | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 354da6110..66fe370bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1732,6 +1732,8 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708" dependencies = [ "bitflags 2.4.2", "indexmap", @@ -1741,8 +1743,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.201.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708" +source = "git+https://github.com/cpetig/wasm-tools#f88fec6b55a59f966377dfabcf49fa9715b40069" dependencies = [ "bitflags 2.4.2", "indexmap", @@ -2559,6 +2560,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.201.0" +source = "git+https://github.com/cpetig/wasm-tools#f88fec6b55a59f966377dfabcf49fa9715b40069" dependencies = [ "anyhow", "id-arena", @@ -2569,7 +2571,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.201.0", + "wasmparser 0.201.0 (git+https://github.com/cpetig/wasm-tools)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b9da5af67..11fbb3cdc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,4 +92,4 @@ wasmparser = { workspace = true } wasm-encoder = { workspace = true } [patch.crates-io] -wit-parser = { path = "../wasm-tools/crates/wit-parser" } +wit-parser = { git = "https://github.com/cpetig/wasm-tools" } From 1a90bd83f98328881e6265917a3ed67bd50be00b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 12 Mar 2024 22:02:26 +0100 Subject: [PATCH 115/672] wasm64 option for Rust --- crates/cpp/tests/native_strings/rust/src/the_world.rs | 6 +++--- crates/guest-rust/macro/src/lib.rs | 8 ++++++++ crates/rust/src/lib.rs | 10 +++++++++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/crates/cpp/tests/native_strings/rust/src/the_world.rs b/crates/cpp/tests/native_strings/rust/src/the_world.rs index 023023764..84964deaa 100644 --- a/crates/cpp/tests/native_strings/rust/src/the_world.rs +++ b/crates/cpp/tests/native_strings/rust/src/the_world.rs @@ -1,4 +1,4 @@ -// Generated by `wit-bindgen` 0.21.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.22.0. DO NOT EDIT! // Options used: pub mod foo { pub mod foo { @@ -231,7 +231,7 @@ macro_rules! __export_the_world_impl { pub(crate) use __export_the_world_impl as export; #[cfg(target_arch = "wasm32")] -#[link_section = "component-type:wit-bindgen:0.21.0:the-world:encoded world"] +#[link_section = "component-type:wit-bindgen:0.22.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\ @@ -240,7 +240,7 @@ A\x04\x01B\x06\x01@\x01\x01xs\x01\0\x04\0\x01a\x01\0\x01@\0\0s\x04\0\x01b\x01\x0 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.201.0\x10wit-bindgen-rust\x060.21.0"; +processed-by\x02\x0dwit-component\x070.201.0\x10wit-bindgen-rust\x060.22.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs index 374af5da2..1dd267e69 100644 --- a/crates/guest-rust/macro/src/lib.rs +++ b/crates/guest-rust/macro/src/lib.rs @@ -90,6 +90,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 @@ -231,6 +234,7 @@ mod kw { syn::custom_keyword!(default_bindings_module); syn::custom_keyword!(export_macro_name); syn::custom_keyword!(pub_export_macro); + syn::custom_keyword!(wasm64); } #[derive(Clone)] @@ -280,6 +284,7 @@ enum Opt { DefaultBindingsModule(syn::LitStr), ExportMacroName(syn::LitStr), PubExportMacro(syn::LitBool), + Wasm64, } impl Parse for Opt { @@ -359,6 +364,9 @@ impl Parse for Opt { } else if l.peek(kw::stubs) { input.parse::()?; Ok(Opt::Stubs) + } else if l.peek(kw::wasm64) { + input.parse::()?; + Ok(Opt::Wasm64) } else if l.peek(kw::export_prefix) { input.parse::()?; input.parse::()?; diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 3fe265fb5..80e9dcd92 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -182,6 +182,10 @@ pub struct Opts { /// candidate for being exported outside of the crate. #[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, } impl Opts { @@ -205,7 +209,11 @@ impl RustWasm { resolve: &'a Resolve, in_import: bool, ) -> InterfaceGenerator<'a> { - let mut sizes = SizeAlign::default(); + let mut sizes = SizeAlign::new(if self.opts.wasm64 { + AddressSize::Wasm64 + } else { + AddressSize::Wasm32 + }); sizes.fill(resolve); InterfaceGenerator { From 0f6251ddbefbda567de5a2f5e5e8292c5426ba5c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 12 Mar 2024 22:08:09 +0100 Subject: [PATCH 116/672] wasm64 update --- Cargo.lock | 4 ++-- Cargo.toml | 1 + crates/cpp/src/lib.rs | 16 ++++++++-------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66fe370bb..30b78cd95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1743,7 +1743,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.201.0" -source = "git+https://github.com/cpetig/wasm-tools#f88fec6b55a59f966377dfabcf49fa9715b40069" +source = "git+https://github.com/cpetig/wasm-tools#dcd74ecc05a0a21c52b7fac8a219eaab69658990" dependencies = [ "bitflags 2.4.2", "indexmap", @@ -2560,7 +2560,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.201.0" -source = "git+https://github.com/cpetig/wasm-tools#f88fec6b55a59f966377dfabcf49fa9715b40069" +source = "git+https://github.com/cpetig/wasm-tools#dcd74ecc05a0a21c52b7fac8a219eaab69658990" dependencies = [ "anyhow", "id-arena", diff --git a/Cargo.toml b/Cargo.toml index 11fbb3cdc..52ddd3deb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,3 +93,4 @@ wasm-encoder = { workspace = true } [patch.crates-io] wit-parser = { git = "https://github.com/cpetig/wasm-tools" } +#wit-parser = { path = "/home/christof/src/wasm-tools/crates/wit-parser" } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 3429dddb0..aba9e3e39 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -11,8 +11,8 @@ use wit_bindgen_core::{ abi::{self, AbiVariant, Bindgen, Bitcast, LiftLower, WasmSignature, WasmType}, make_external_symbol, uwrite, uwriteln, wit_parser::{ - Docs, Function, FunctionKind, Handle, Int, InterfaceId, Resolve, Results, SizeAlign, - SizeAlignAbi, SizeAlignSelect, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, + AddressSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, Resolve, Results, + SizeAlign, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, }, Files, InterfaceGenerator, Source, WorldGenerator, }; @@ -159,11 +159,11 @@ impl Cpp { in_import: bool, wasm_import_module: Option, ) -> CppInterfaceGenerator<'a> { - let mut sizes = if self.opts.wasm64 { - SizeAlignSelect::Wasm64(SizeAlignAbi::default()) + let mut sizes = SizeAlign::new(if self.opts.wasm64 { + AddressSize::Wasm64 } else { - SizeAlignSelect::Wasm32(SizeAlignAbi::default()) - }; + AddressSize::Wasm32 + }); sizes.fill(resolve); CppInterfaceGenerator { @@ -613,7 +613,7 @@ struct CppInterfaceGenerator<'a> { resolve: &'a Resolve, interface: Option, _name: Option<&'a WorldKey>, - sizes: SizeAlignSelect, + sizes: SizeAlign, in_import: bool, // return_pointer_area_size: usize, // return_pointer_area_align: usize, @@ -2848,7 +2848,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // uwriteln!(self.src, "// finish_block()"); } - fn sizes(&self) -> &wit_bindgen_core::wit_parser::SizeAlignSelect { + fn sizes(&self) -> &wit_bindgen_core::wit_parser::SizeAlign { &self.gen.sizes } From 18d1b1d6204ab749b76debcc550f991457f11d66 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 13 Mar 2024 09:19:35 +0100 Subject: [PATCH 117/672] use the upstream version of wit-parser again --- Cargo.lock | 6 +++--- Cargo.toml | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30b78cd95..6768e9bb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1743,7 +1743,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.201.0" -source = "git+https://github.com/cpetig/wasm-tools#dcd74ecc05a0a21c52b7fac8a219eaab69658990" +source = "git+https://github.com/bytecodealliance/wasm-tools#031fc6141878b3be9c37f2ea1247814fb3451640" dependencies = [ "bitflags 2.4.2", "indexmap", @@ -2560,7 +2560,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.201.0" -source = "git+https://github.com/cpetig/wasm-tools#dcd74ecc05a0a21c52b7fac8a219eaab69658990" +source = "git+https://github.com/bytecodealliance/wasm-tools#031fc6141878b3be9c37f2ea1247814fb3451640" dependencies = [ "anyhow", "id-arena", @@ -2571,7 +2571,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.201.0 (git+https://github.com/cpetig/wasm-tools)", + "wasmparser 0.201.0 (git+https://github.com/bytecodealliance/wasm-tools)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 52ddd3deb..4ad2ffe8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,5 +92,6 @@ wasmparser = { workspace = true } wasm-encoder = { workspace = true } [patch.crates-io] -wit-parser = { git = "https://github.com/cpetig/wasm-tools" } +wit-parser = { git = "https://github.com/bytecodealliance/wasm-tools" } +#wit-parser = { git = "https://github.com/cpetig/wasm-tools" } #wit-parser = { path = "/home/christof/src/wasm-tools/crates/wit-parser" } From 46c41bf4e85ea810428c32744c38d75f3159cccb Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 14 Mar 2024 23:02:45 +0100 Subject: [PATCH 118/672] guest owned added to host result --- crates/cpp/src/lib.rs | 12 ++++++++++++ .../cpp/tests/native_strings/the_world_cpp_native.h | 6 +----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index aba9e3e39..dd2026692 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -53,6 +53,7 @@ struct HighlevelSignature { name: String, namespace: Vec, implicit_self: bool, + post_return: bool, } // follows https://google.github.io/styleguide/cppguide.html @@ -875,6 +876,11 @@ impl CppInterfaceGenerator<'_> { } } } + 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) @@ -923,7 +929,13 @@ impl CppInterfaceGenerator<'_> { if cpp_sig.static_member { self.gen.h_src.src.push_str("static "); } + if cpp_sig.post_return { + 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.h_src.src.push_str(">"); + } if !cpp_sig.result.is_empty() { self.gen.h_src.src.push_str(" "); } diff --git a/crates/cpp/tests/native_strings/the_world_cpp_native.h b/crates/cpp/tests/native_strings/the_world_cpp_native.h index 984741e97..18546c4cb 100644 --- a/crates/cpp/tests/native_strings/the_world_cpp_native.h +++ b/crates/cpp/tests/native_strings/the_world_cpp_native.h @@ -1,15 +1,11 @@ // 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 #include #include -#define WIT_HOST_DIRECT #include - -extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align, - size_t new_size); - namespace foo { namespace foo { namespace strings { From 5463264d0d0a0a00b942c568d292c21e7cde04b6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 15 Mar 2024 00:14:13 +0100 Subject: [PATCH 119/672] more uintptr to uint8_t* --- crates/cpp/src/lib.rs | 20 ++++++++++---------- crates/cpp/tests/native_strings/Makefile | 5 ++++- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index dd2026692..08eaacbf3 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -798,7 +798,7 @@ impl CppInterfaceGenerator<'_> { // first_arg = false; // } let ptrtype = if !self.gen.opts.host { - "intptr_t" + "uint8_t*" } else if self.gen.opts.wasm64 { "int64_t" } else { @@ -929,11 +929,11 @@ impl CppInterfaceGenerator<'_> { if cpp_sig.static_member { self.gen.h_src.src.push_str("static "); } - if cpp_sig.post_return { + 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 { + if cpp_sig.post_return && self.gen.opts.host_side() { self.gen.h_src.src.push_str(">"); } if !cpp_sig.result.is_empty() { @@ -1133,7 +1133,7 @@ impl CppInterfaceGenerator<'_> { ); uwriteln!( self.gen.c_src.src, - "return {wasm_sig}((uintptr_t){});", + "return {wasm_sig}((uint8_t*){});", func.params.get(0).unwrap().0 ); // let classname = class_namespace(self, func, variant).join("::"); @@ -2057,7 +2057,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); self.push_str(&format!("auto {} = {}.size();\n", len, val)); } else { - self.push_str(&format!("auto {} = (uintptr_t)({}.data());\n", ptr, val)); + self.push_str(&format!("auto {} = (uint8_t*)({}.data());\n", ptr, val)); self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val)); } if realloc.is_none() { @@ -2081,7 +2081,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); self.push_str(&format!("auto {} = {}.size();\n", len, val)); } else { - self.push_str(&format!("auto {} = (uintptr_t)({}.data());\n", ptr, val)); + self.push_str(&format!("auto {} = (uint8_t*)({}.data());\n", ptr, val)); self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val)); } if realloc.is_none() { @@ -2108,7 +2108,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); self.push_str(&format!("auto {} = {}.size();\n", len, val)); } else { - self.push_str(&format!("auto {} = (uintptr_t)({}.data());\n", ptr, val)); + self.push_str(&format!("auto {} = (uint8_t*)({}.data());\n", ptr, val)); self.push_str(&format!("auto {} = (size_t)({}.size());\n", len, val)); } if realloc.is_none() { @@ -2820,12 +2820,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src.push_str("}\n"); } abi::Instruction::PointerLoad { offset } => { - self.load("uintptr_t", *offset, operands, results) + self.load("uint8_t*", *offset, operands, results) } abi::Instruction::LengthLoad { offset } => { self.load("size_t", *offset, operands, results) } - abi::Instruction::PointerStore { offset } => self.store("uintptr_t", *offset, operands), + abi::Instruction::PointerStore { offset } => self.store("uint8_t*", *offset, operands), abi::Instruction::LengthStore { offset } => self.store("size_t", *offset, operands), } } @@ -2842,7 +2842,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { }; let static_var = if self.gen.in_import { "" } else { "static " }; uwriteln!(self.src, "{static_var}{tp} ret_area[{elems}];"); - uwriteln!(self.src, "intptr_t ptr{tmp} = intptr_t(&ret_area);"); + uwriteln!(self.src, "uint8_t* ptr{tmp} = (uint8_t*)(&ret_area);"); format!("ptr{}", tmp) } diff --git a/crates/cpp/tests/native_strings/Makefile b/crates/cpp/tests/native_strings/Makefile index f3f024d1e..74a0a894e 100644 --- a/crates/cpp/tests/native_strings/Makefile +++ b/crates/cpp/tests/native_strings/Makefile @@ -17,4 +17,7 @@ bindgen: wit/strings.wit $(WIT_BINDGEN) cpp wit --wasm64 --format --direct guest.wasm: the_world.cpp guest.cpp - /opt/wasi-sdk/bin/clang++ -o $@ $^ $(CXXFLAGS) \ No newline at end of file + /opt/wasi-sdk/bin/clang++ -o $@ $^ $(CXXFLAGS) + +clean: + -rm *.o libstrings.so app-strings From 7c097cc8fb05f6f7e9b3f2eb4860afb90eeb74e0 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 16 Mar 2024 00:10:46 +0100 Subject: [PATCH 120/672] strings working besides host with cabi_post --- crates/cpp/src/lib.rs | 93 ++++++++++++++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 24 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 08eaacbf3..43fb2675b 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -6,7 +6,7 @@ use std::{ process::{Command, Stdio}, str::FromStr, }; -use wit_bindgen_c::{to_c_ident, wasm_type}; +use wit_bindgen_c::to_c_ident; use wit_bindgen_core::{ abi::{self, AbiVariant, Bindgen, Bitcast, LiftLower, WasmSignature, WasmType}, make_external_symbol, uwrite, uwriteln, @@ -132,6 +132,24 @@ impl Opts { fn host_side(&self) -> bool { self.short_cut || self.host } + + 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 { @@ -766,7 +784,7 @@ impl CppInterfaceGenerator<'_> { .push_str(if signature.results.is_empty() || return_via_pointer { "void" } else { - wasm_type(signature.results[0]) + self.gen.opts.wasm_type(signature.results[0]) }); self.gen.c_src.src.push_str(" "); let export_name = make_external_symbol(&module_name, &func.name, variant); @@ -785,7 +803,7 @@ impl CppInterfaceGenerator<'_> { } else { first_arg = false; } - self.gen.c_src.src.push_str(wasm_type(*ty)); + 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); @@ -797,14 +815,7 @@ impl CppInterfaceGenerator<'_> { // else { // first_arg = false; // } - let ptrtype = if !self.gen.opts.host { - "uint8_t*" - } else if self.gen.opts.wasm64 { - "int64_t" - } else { - "int32_t" - }; - self.gen.c_src.src.push_str(ptrtype); + self.gen.c_src.src.push_str(self.gen.opts.ptr_type()); self.gen.c_src.src.push_str(" resultptr"); params.push("resultptr".into()); } @@ -1010,7 +1021,13 @@ impl CppInterfaceGenerator<'_> { 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(" "); } @@ -1133,7 +1150,8 @@ impl CppInterfaceGenerator<'_> { ); uwriteln!( self.gen.c_src.src, - "return {wasm_sig}((uint8_t*){});", + "return {wasm_sig}(({}){});", + self.gen.opts.ptr_type(), func.params.get(0).unwrap().0 ); // let classname = class_namespace(self, func, variant).join("::"); @@ -1207,7 +1225,11 @@ impl CppInterfaceGenerator<'_> { 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}", wasm_type(*result)); + uwrite!( + self.gen.c_src.src, + "{} {name}", + self.gen.opts.wasm_type(*result) + ); params.push(name); } self.gen.c_src.src.push_str(") {\n"); @@ -1425,7 +1447,7 @@ impl CppInterfaceGenerator<'_> { ) -> String { let mut args = String::default(); for (n, param) in params.iter().enumerate() { - args.push_str(wasm_type(*param)); + args.push_str(self.gen.opts.wasm_type(*param)); if n + 1 != params.len() { args.push_str(", "); } @@ -1433,7 +1455,7 @@ impl CppInterfaceGenerator<'_> { let result = if results.is_empty() { "void" } else { - wasm_type(results[0]) + self.gen.opts.wasm_type(results[0]) }; let (name, code) = Self::declare_import2(module_name, name, &args, result); self.gen.extern_c_decls.push_str(&code); @@ -2057,7 +2079,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); self.push_str(&format!("auto {} = {}.size();\n", len, val)); } else { - self.push_str(&format!("auto {} = (uint8_t*)({}.data());\n", ptr, val)); + 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() { @@ -2081,7 +2108,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); self.push_str(&format!("auto {} = {}.size();\n", len, val)); } else { - self.push_str(&format!("auto {} = (uint8_t*)({}.data());\n", ptr, val)); + 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() { @@ -2108,7 +2140,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.push_str(&format!("auto {} = {}.data();\n", ptr, val)); self.push_str(&format!("auto {} = {}.size();\n", len, val)); } else { - self.push_str(&format!("auto {} = (uint8_t*)({}.data());\n", ptr, val)); + 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() { @@ -2326,7 +2363,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { for ty in result_types.iter() { let name = format!("variant{}", self.tmp()); results.push(name.clone()); - self.src.push_str(wasm_type(*ty)); + 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"); @@ -2441,7 +2478,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let tmp = self.tmp(); let name = self.tempname("option", tmp); results.push(name.clone()); - self.src.push_str(wasm_type(*ty)); + 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"); @@ -2506,7 +2543,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let tmp = self.tmp(); let name = self.tempname("result", tmp); results.push(name.clone()); - self.src.push_str(wasm_type(*ty)); + 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"); @@ -2820,12 +2857,16 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src.push_str("}\n"); } abi::Instruction::PointerLoad { offset } => { - self.load("uint8_t*", *offset, operands, results) + 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 } => self.store("uint8_t*", *offset, operands), + 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), } } @@ -2842,7 +2883,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { }; let static_var = if self.gen.in_import { "" } else { "static " }; uwriteln!(self.src, "{static_var}{tp} ret_area[{elems}];"); - uwriteln!(self.src, "uint8_t* ptr{tmp} = (uint8_t*)(&ret_area);"); + uwriteln!( + self.src, + "{} ptr{tmp} = ({0})(&ret_area);", + self.gen.gen.opts.ptr_type(), + ); format!("ptr{}", tmp) } From 8da2c8caeb89441b7daf1dfe3de7f056aa70592e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 16 Mar 2024 00:56:09 +0100 Subject: [PATCH 121/672] strings test works again --- crates/cpp/helper-types/wit-host.h | 2 +- crates/cpp/src/lib.rs | 50 ++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 098d41458..73ab21c7d 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -139,7 +139,7 @@ template class guest_owned : public T { #ifdef WIT_HOST_WAMR wasm_val_t *wasm_results = nullptr; wasm_val_t wasm_args[1] = { - WASM_I32_VAL(data_), + WASM_I32_VAL((int32_t)data_), }; wasm_runtime_call_wasm_a(exec_env, free_func, 0, wasm_results, 1, wasm_args); diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 43fb2675b..3701b5f00 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1196,10 +1196,31 @@ impl CppInterfaceGenerator<'_> { }; let mut f = FunctionBindgen::new(self, params); if !export { - f.namespace = namespace; + f.namespace = namespace.clone(); f.wamr_signature = Some(wamr::wamr_signature(&f.gen.resolve, func)); } f.variant = variant; + 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 export_name = func.core_export_name(Some(&module_name)); + let import_name = + make_external_symbol(&module_name, &func.name, AbiVariant::GuestExport); + let cpp_sig = f.gen.high_level_signature(func, variant, &namespace); + Some(CabiPostInformation { + name: format!("cabi_post_{import_name}"), + ret_type: cpp_sig.result, + }) + } else { + None + }; abi::call(f.gen.resolve, variant, lift_lower, func, &mut f); let code = String::from(f.src); self.gen.c_src.src.push_str(&code); @@ -1797,6 +1818,11 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } } +struct CabiPostInformation { + name: String, + ret_type: String, +} + struct FunctionBindgen<'a, 'b> { gen: &'b mut CppInterfaceGenerator<'a>, params: Vec, @@ -1812,6 +1838,7 @@ struct FunctionBindgen<'a, 'b> { // caching for wasm wamr_signature: Option, variant: AbiVariant, + cabi_post: Option, } impl<'a, 'b> FunctionBindgen<'a, 'b> { @@ -1829,6 +1856,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { payloads: Default::default(), wamr_signature: None, variant: AbiVariant::GuestImport, + cabi_post: None, } } @@ -2787,9 +2815,17 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } _ => self.src.push_str("return "), } + if let Some(CabiPostInformation { + 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 { self.src.push_str(&operands[0]); - self.src.push_str(";\n"); } else { self.src.push_str("std::tuple<"); if let Results::Named(params) = &func.results { @@ -2804,8 +2840,16 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } self.src.push_str(">("); self.src.push_str(&operands.join(", ")); - self.src.push_str(");\n"); + self.src.push_str(")"); } + if let Some(CabiPostInformation { + name: cabi_post_name, + ret_type: _cabi_post_type, + }) = self.cabi_post.as_ref() + { + self.src.push_str(&format!(", wasm_results[0].of.i32, wasm_runtime_lookup_function(wasm_runtime_get_module_inst(exec_env), \"{}\", \"(i)\"), exec_env)", cabi_post_name)); + } + self.src.push_str(";\n"); } } } From 2c7d663ab7f57ef87dced97116a3570098396ed3 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 16 Mar 2024 01:03:51 +0100 Subject: [PATCH 122/672] native guest bindgen fully ok --- crates/cpp/tests/native_strings/the_world.cpp | 59 ++++++++++--------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/crates/cpp/tests/native_strings/the_world.cpp b/crates/cpp/tests/native_strings/the_world.cpp index 4bc8c8aa2..c90a75210 100644 --- a/crates/cpp/tests/native_strings/the_world.cpp +++ b/crates/cpp/tests/native_strings/the_world.cpp @@ -17,70 +17,71 @@ cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { } extern "C" __attribute__((import_module("foo:foo/strings"))) -__attribute__((import_name("a"))) void fooX3AfooX2FstringsX00a(uintptr_t, - size_t); +__attribute__((import_name("a"))) void +fooX3AfooX2FstringsX00a(uint8_t *, size_t); extern "C" __attribute__((import_module("foo:foo/strings"))) -__attribute__((import_name("b"))) void fooX3AfooX2FstringsX00b(uintptr_t); +__attribute__((import_name("b"))) void +fooX3AfooX2FstringsX00b(uint8_t *); extern "C" __attribute__((import_module("foo:foo/strings"))) __attribute__((import_name("c"))) void - fooX3AfooX2FstringsX00c(uintptr_t, size_t, uintptr_t, size_t, uintptr_t); +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 = (uintptr_t)(vec0.data()); + auto ptr0 = (uint8_t *)(vec0.data()); auto len0 = (size_t)(vec0.size()); fooX3AfooX2FstringsX00a(ptr0, len0); } wit::string foo::foo::strings::B() { - uintptr_t ret_area[2]; - intptr_t ptr0 = intptr_t(&ret_area); + uint64_t ret_area[2]; + uint8_t *ptr0 = (uint8_t *)(&ret_area); fooX3AfooX2FstringsX00b(ptr0); auto len1 = *((size_t *)(ptr0 + 8)); - return wit::string((char const *)(*((uintptr_t *)(ptr0 + 0))), len1); + 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 = (uintptr_t)(vec0.data()); + auto ptr0 = (uint8_t *)(vec0.data()); auto len0 = (size_t)(vec0.size()); auto const &vec1 = b; - auto ptr1 = (uintptr_t)(vec1.data()); + auto ptr1 = (uint8_t *)(vec1.data()); auto len1 = (size_t)(vec1.size()); - uintptr_t ret_area[2]; - intptr_t ptr2 = intptr_t(&ret_area); + uint64_t ret_area[2]; + uint8_t *ptr2 = (uint8_t *)(&ret_area); fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2); auto len3 = *((size_t *)(ptr2 + 8)); - return wit::string((char const *)(*((uintptr_t *)(ptr2 + 0))), len3); + return wit::string((char const *)(*((uint8_t **)(ptr2 + 0))), len3); } extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void -fooX3AfooX2FstringsX23a(uintptr_t arg0, size_t arg1) { +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"))) uintptr_t +extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) uint8_t * fooX3AfooX2FstringsX23b() { auto result0 = exports::foo::foo::strings::B(); - static uintptr_t ret_area[2]; - intptr_t ptr1 = intptr_t(&ret_area); + static uint64_t ret_area[2]; + uint8_t *ptr1 = (uint8_t *)(&ret_area); auto const &vec2 = result0; - auto ptr2 = (uintptr_t)(vec2.data()); + auto ptr2 = (uint8_t *)(vec2.data()); auto len2 = (size_t)(vec2.size()); result0.leak(); *((size_t *)(ptr1 + 8)) = len2; - *((uintptr_t *)(ptr1 + 0)) = ptr2; + *((uint8_t **)(ptr1 + 0)) = ptr2; return ptr1; } extern "C" __attribute__((__weak__, __export_name__("cabi_post_foo:foo/strings#b"))) void -cabi_post_fooX3AfooX2FstringsX23b(uintptr_t arg0) { +cabi_post_fooX3AfooX2FstringsX23b(uint8_t *arg0) { if ((*((size_t *)(arg0 + 8))) > 0) { - wit::string::drop_raw((void *)(*((uintptr_t *)(arg0 + 0)))); + wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); } } -extern "C" __attribute__((__export_name__("foo:foo/strings#c"))) uintptr_t -fooX3AfooX2FstringsX23c(uintptr_t arg0, size_t arg1, uintptr_t arg2, +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; @@ -89,22 +90,22 @@ fooX3AfooX2FstringsX23c(uintptr_t arg0, size_t arg1, uintptr_t arg2, auto result2 = exports::foo::foo::strings::C(wit::string((char const *)(arg0), len0), wit::string((char const *)(arg2), len1)); - static uintptr_t ret_area[2]; - intptr_t ptr3 = intptr_t(&ret_area); + static uint64_t ret_area[2]; + uint8_t *ptr3 = (uint8_t *)(&ret_area); auto const &vec4 = result2; - auto ptr4 = (uintptr_t)(vec4.data()); + auto ptr4 = (uint8_t *)(vec4.data()); auto len4 = (size_t)(vec4.size()); result2.leak(); *((size_t *)(ptr3 + 8)) = len4; - *((uintptr_t *)(ptr3 + 0)) = ptr4; + *((uint8_t **)(ptr3 + 0)) = ptr4; return ptr3; } extern "C" __attribute__((__weak__, __export_name__("cabi_post_foo:foo/strings#c"))) void -cabi_post_fooX3AfooX2FstringsX23c(uintptr_t arg0) { +cabi_post_fooX3AfooX2FstringsX23c(uint8_t *arg0) { if ((*((size_t *)(arg0 + 8))) > 0) { - wit::string::drop_raw((void *)(*((uintptr_t *)(arg0 + 0)))); + wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); } } From a12c53c99d402e7461813034d65c4987c661b33f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 16 Mar 2024 01:41:58 +0100 Subject: [PATCH 123/672] close to working direct code (strings) --- crates/cpp/helper-types/wit-host.h | 2 +- crates/cpp/src/lib.rs | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 73ab21c7d..9a10b2a8d 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -17,7 +17,7 @@ namespace wit { #ifdef WIT_HOST_DIRECT -typedef intptr_t guest_address; +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); diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 3701b5f00..1bdbcfe02 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1453,8 +1453,9 @@ impl CppInterfaceGenerator<'_> { name: &str, args: &str, result: &str, + variant: AbiVariant, ) -> (String, String) { - let extern_name = make_external_symbol(module_name, name, AbiVariant::GuestImport); + let extern_name = make_external_symbol(module_name, name, variant); let import = format!("extern \"C\" __attribute__((import_module(\"{module_name}\")))\n __attribute__((import_name(\"{name}\")))\n {result} {extern_name}({args});\n"); (extern_name, import) } @@ -1478,7 +1479,12 @@ impl CppInterfaceGenerator<'_> { } else { self.gen.opts.wasm_type(results[0]) }; - let (name, code) = Self::declare_import2(module_name, name, &args, result); + 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 } @@ -2760,7 +2766,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.let_results(func.results.len(), results); let (mut namespace, func_name_h) = self.gen - .func_namespace_name(func, !self.gen.gen.opts.host, true); + .func_namespace_name(func, !self.gen.gen.opts.host_side(), true); if matches!(func.kind, FunctionKind::Method(_)) { let this = operands.remove(0); //self.gen.gen.c_src.qualify(&namespace); @@ -2847,7 +2853,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { ret_type: _cabi_post_type, }) = self.cabi_post.as_ref() { - self.src.push_str(&format!(", wasm_results[0].of.i32, wasm_runtime_lookup_function(wasm_runtime_get_module_inst(exec_env), \"{}\", \"(i)\"), exec_env)", cabi_post_name)); + if self.gen.gen.opts.host { + self.src.push_str(&format!(", wasm_results[0].of.i32, wasm_runtime_lookup_function(wasm_runtime_get_module_inst(exec_env), \"{}\", \"(i)\"), exec_env)", cabi_post_name)); + } else { + self.src.push_str(&format!(", ret, {})", cabi_post_name)); + } } self.src.push_str(";\n"); } From a5cd1763ff61cbb81638af24be1f199b39a68bdd Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 16 Mar 2024 09:43:17 +0100 Subject: [PATCH 124/672] proper string lifting on direct --- crates/cpp/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 1bdbcfe02..3bdcf7e09 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2220,6 +2220,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let result = 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 { + format!("std::string_view((char const*)({}), {len})", operands[0]) } else { format!("wit::string((char const*)({}), {len})", operands[0]) }; From 2691f70c5b708dc282037065970b62d1ca5fab2c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 16 Mar 2024 10:07:20 +0100 Subject: [PATCH 125/672] fully operational native string code --- crates/core/src/lib.rs | 2 +- crates/cpp/src/lib.rs | 24 ++++++-- .../tests/native_strings/the_world_native.cpp | 61 ++++++++++--------- 3 files changed, 51 insertions(+), 36 deletions(-) diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 010bc2c52..415a60934 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -427,7 +427,7 @@ pub fn make_external_component(input: &str) -> String { input .chars() .map(|c| match c { - 'A'..='Z' | 'a'..='z' | '0'..='9' => { + 'A'..='Z' | 'a'..='z' | '0'..='9' | '_' => { let mut s = String::new(); s.push(c); s diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 3bdcf7e09..88b645f28 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1210,12 +1210,10 @@ impl CppInterfaceGenerator<'_> { .as_ref() .map(|e| e.clone()) .unwrap(); - // let export_name = func.core_export_name(Some(&module_name)); - let import_name = - make_external_symbol(&module_name, &func.name, AbiVariant::GuestExport); let cpp_sig = f.gen.high_level_signature(func, variant, &namespace); Some(CabiPostInformation { - name: format!("cabi_post_{import_name}"), + module: module_name, + name: func.name.clone(), ret_type: cpp_sig.result, }) } else { @@ -1825,6 +1823,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } struct CabiPostInformation { + module: String, name: String, ret_type: String, } @@ -2824,6 +2823,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { _ => self.src.push_str("return "), } if let Some(CabiPostInformation { + module: _, name: _cabi_post_name, ret_type: cabi_post_type, }) = self.cabi_post.as_ref() @@ -2851,13 +2851,25 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src.push_str(")"); } if let Some(CabiPostInformation { - name: cabi_post_name, + module: func_module, + name: func_name, ret_type: _cabi_post_type, }) = self.cabi_post.as_ref() { if self.gen.gen.opts.host { - self.src.push_str(&format!(", wasm_results[0].of.i32, wasm_runtime_lookup_function(wasm_runtime_get_module_inst(exec_env), \"{}\", \"(i)\"), exec_env)", cabi_post_name)); + 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)); } } diff --git a/crates/cpp/tests/native_strings/the_world_native.cpp b/crates/cpp/tests/native_strings/the_world_native.cpp index 4e8d34822..b5b17521e 100644 --- a/crates/cpp/tests/native_strings/the_world_native.cpp +++ b/crates/cpp/tests/native_strings/the_world_native.cpp @@ -1,44 +1,48 @@ // 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(uintptr_t, - size_t); +__attribute__((import_name("a"))) void +fooX3AfooX2FstringsX23a(uint8_t *, size_t); extern "C" __attribute__((import_module("foo:foo/strings"))) -__attribute__((import_name("b"))) uintptr_t +__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"))) -uintptr_t fooX3AfooX2FstringsX23c(uintptr_t, size_t, uintptr_t, size_t); -extern "C" -void fooX3AfooX2FstringsX00a(uintptr_t arg0, size_t arg1) { +__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)); + foo::foo::strings::A(std::string_view((char const *)(arg0), len0)); } -extern "C" -void fooX3AfooX2FstringsX00b(intptr_t resultptr) { +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 *)(resultptr + 8)) = len1; - *((uintptr_t *)(resultptr + 0)) = (uintptr_t)ptr1; + *((size_t *)(arg0 + 8)) = len1; + *((uint8_t **)(arg0 + 0)) = ptr1; } -extern "C" -void fooX3AfooX2FstringsX00c(uintptr_t arg0, size_t arg1, uintptr_t arg2, - size_t arg3, intptr_t resultptr) { +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)); + 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 *)(resultptr + 8)) = len3; - *((uintptr_t *)(resultptr + 0)) = (uintptr_t)ptr3; + *((size_t *)(arg4 + 8)) = len3; + *((uint8_t **)(arg4 + 0)) = ptr3; } void exports::foo::foo::strings::A(wit::string x) { auto const &vec0 = x; @@ -46,19 +50,16 @@ void exports::foo::foo::strings::A(wit::string x) { auto len0 = vec0.size(); fooX3AfooX2FstringsX23a(ptr0, len0); } -extern "C" -__attribute__((__weak__, __export_name__("cabi_post_foo:foo/strings#b"))) void -cabi_post_fooX3AfooX2FstringsX23b(intptr_t arg0); -wit::guest_owned exports::foo::foo::strings::B() { +wit::guest_owned exports::foo::foo::strings::B() { auto ret = fooX3AfooX2FstringsX23b(); auto len0 = *((size_t *)(ret + 8)); - return wit::guest_owned (std::string_view((char const*)*((uintptr_t *)(ret + 0)), len0), ret, cabi_post_fooX3AfooX2FstringsX23b); + return wit::guest_owned( + std::string_view((char const *)(*((uint8_t **)(ret + 0))), len0), ret, + cabi_post_fooX3AfooX2FstringsX23b); } -extern "C" -__attribute__((__weak__, __export_name__("cabi_post_foo:foo/strings#c"))) void -cabi_post_fooX3AfooX2FstringsX23c(intptr_t arg0); -wit::guest_owned exports::foo::foo::strings::C(wit::string a, wit::string b) { +wit::guest_owned +exports::foo::foo::strings::C(wit::string a, wit::string b) { auto const &vec0 = a; auto ptr0 = vec0.data(); auto len0 = vec0.size(); @@ -68,7 +69,9 @@ wit::guest_owned exports::foo::foo::strings::C(wit::string a, auto ret = fooX3AfooX2FstringsX23c(ptr0, len0, ptr1, len1); auto len2 = *((size_t *)(ret + 8)); - return wit::guest_owned (std::string_view((char const*)*((uintptr_t *)(ret + 0)), len2), ret, cabi_post_fooX3AfooX2FstringsX23c); + return wit::guest_owned( + std::string_view((char const *)(*((uint8_t **)(ret + 0))), len2), ret, + cabi_post_fooX3AfooX2FstringsX23c); } // Component Adapters From 375b5be5e22d2d893c6eff035e0960df7a3710ac Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 16 Mar 2024 11:40:50 +0100 Subject: [PATCH 126/672] rust native resource example --- crates/cpp/tests/native_resources/Makefile | 24 + .../tests/native_resources/libresources.so | 1 + .../tests/native_resources/rust/Cargo.lock | 7 + .../tests/native_resources/rust/Cargo.toml | 13 + .../tests/native_resources/rust/src/lib.rs | 44 ++ .../native_resources/rust/src/the_world.rs | 612 ++++++++++++++++++ 6 files changed, 701 insertions(+) create mode 100644 crates/cpp/tests/native_resources/Makefile create mode 120000 crates/cpp/tests/native_resources/libresources.so create mode 100644 crates/cpp/tests/native_resources/rust/Cargo.lock create mode 100644 crates/cpp/tests/native_resources/rust/Cargo.toml create mode 100644 crates/cpp/tests/native_resources/rust/src/lib.rs create mode 100644 crates/cpp/tests/native_resources/rust/src/the_world.rs diff --git a/crates/cpp/tests/native_resources/Makefile b/crates/cpp/tests/native_resources/Makefile new file mode 100644 index 000000000..8f0b81869 --- /dev/null +++ b/crates/cpp/tests/native_resources/Makefile @@ -0,0 +1,24 @@ +CXXFLAGS=-g -O0 -I../../helper-types +WIT_BINDGEN=../../../../target/debug/wit-bindgen + +all: libresources.so app-resources + +libresources.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-resources: the_world_native.o main.o + $(CXX) $(CXXFLAGS) -o $@ $^ -L. -lresources + +bindgen: wit/resources_simple.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) + +clean: + -rm *.o libresources.so app-resources 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/rust/Cargo.lock b/crates/cpp/tests/native_resources/rust/Cargo.lock new file mode 100644 index 000000000..87ef1c08d --- /dev/null +++ b/crates/cpp/tests/native_resources/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 = "resources" +version = "0.1.0" 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..bbcef57c9 --- /dev/null +++ b/crates/cpp/tests/native_resources/rust/Cargo.toml @@ -0,0 +1,13 @@ +[workspace] + +[package] +name = "resources" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] +#wit-bindgen = { path = "../../../../guest-rust" } + +[dependencies] +# wit-bindgen-rt = "0.21.0" 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..ea20e2d33 --- /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); + +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); + } + fn consume(o: resources::R) { + println!("resource consumed with {:?}", o); + + 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..3c8daf8dc --- /dev/null +++ b/crates/cpp/tests/native_resources/rust/src/the_world.rs @@ -0,0 +1,612 @@ +// Generated by `wit-bindgen` 0.22.0. DO NOT EDIT! +// Options used: +pub mod foo { + pub mod foo { + #[allow(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, + } + + 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); + } + } + } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(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, + } + + type _RRep = Option; + + 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(val: T) -> Self { + Self::type_guard::(); + let val: _RRep = Some(val); + let ptr: *mut _RRep = _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(&self) -> &T { + let ptr = unsafe { &*self.as_ptr::() }; + ptr.as_ref().unwrap() + } + + /// Gets mutable access to the underlying `T` which represents this + /// resource. + pub fn get_mut(&mut self) -> &mut T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.as_mut().unwrap() + } + + /// Consumes this resource and returns the underlying `T`. + pub fn into_inner(self) -> T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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() { + use core::any::TypeId; + static mut LAST_TYPE: Option = None; + unsafe { + assert!(!cfg!(target_feature = "threads")); + let id = TypeId::of::(); + 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(handle: *mut u8) { + Self::type_guard::(); + let _ = _rt::Box::from_raw(handle as *mut _RRep); + } + + fn as_ptr(&self) -> *mut _RRep { + R::type_guard::(); + unsafe { 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(&self) -> &T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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(&self) -> *mut _RRep { + R::type_guard::(); + 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(arg0: i32) -> i32 { + 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(arg0: *mut u8, arg1: i32) { + T::add(RBorrow::lift(arg0 as usize).get(), arg1 as u32); + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_create_cabi() -> i32 { + let result0 = T::create(); + (result0).take_handle() as i32 + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_borrows_cabi(arg0: i32) { + T::borrows(RBorrow::lift(arg0 as usize)); + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_consume_cabi(arg0: i32) { + 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: i32,) { + $($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 { + // 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, + } + + /// 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 Resource { + #[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` 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` 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) -> u32 { + resource.handle.swap(u32::MAX, Relaxed) + } + + #[doc(hidden)] + pub fn handle(resource: &Resource) -> u32 { + resource.handle.load(Relaxed) + } + } + + impl fmt::Debug for Resource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Resource") + .field("handle", &self.handle) + .finish() + } + } + + impl Drop for Resource { + 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: 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; + 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.22.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\ +1.0\x10wit-bindgen-rust\x060.22.0"; + +#[inline(never)] +#[doc(hidden)] +#[cfg(target_arch = "wasm32")] +pub fn __link_custom_section_describing_imports() { + wit_bindgen::rt::maybe_link_cabi_realloc(); +} From f1e245b5c94c81d449af4654ceab87c4c4a25a79 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 16 Mar 2024 11:41:42 +0100 Subject: [PATCH 127/672] quality of life improvements for c++ components --- crates/cpp/helper-types/wit-common.h | 6 ++++++ crates/cpp/helper-types/wit-guest.h | 17 +++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h index 89fa31df0..4e5e86d94 100644 --- a/crates/cpp/helper-types/wit-common.h +++ b/crates/cpp/helper-types/wit-common.h @@ -5,6 +5,8 @@ #include #if __cplusplus > 202001L #include +#else +#include #endif namespace wit { @@ -25,10 +27,14 @@ template class span { const_iterator begin() const { return address; } const_iterator end() const { return address + length; } T const &operator[](size_t index) { return address[index]; } + // create from any compatible vector (borrows data!) + template + span(std::vector const&vec) : address(vec.data()), length(v.size()) {} }; #endif class ResourceImportBase { +public: static const int32_t invalid = -1; protected: diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 937a4be36..6682e358c 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -1,6 +1,7 @@ #include #include #include +#include #include "wit-common.h" namespace wit { @@ -36,6 +37,9 @@ class string { 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); + } }; template @@ -59,6 +63,8 @@ class vector { vector(T *d, size_t l) : data_(d), length(l) {} 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; } ~vector() { if (data_) { @@ -76,9 +82,16 @@ class vector { template class ResourceExportBase { public: + static const int32_t invalid = -1; + int32_t handle; - ResourceExportBase() : handle(R::ResourceNew(this)) {} - ~ResourceExportBase() { R::ResourceDrop(handle); } + ResourceExportBase() : handle(R::ResourceNew((R*)this)) {} + ~ResourceExportBase() { if (handle>=0) { R::ResourceDrop(handle); } } + ResourceExportBase(ResourceExportBase const&) = delete; + ResourceExportBase(ResourceExportBase &&) = delete; + ResourceExportBase& operator=(ResourceExportBase &&b) = delete; + ResourceExportBase& operator=(ResourceExportBase const&) = delete; + int32_t get_handle() const { return handle; } }; } // namespace wit From b7fab795542c3b681ac863fb2341be81343e4ee2 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 16 Mar 2024 11:51:38 +0100 Subject: [PATCH 128/672] generate proper native dtor name --- .../native_resources/rust/src/the_world.rs | 969 +++++++++--------- crates/rust/src/interface.rs | 10 +- 2 files changed, 497 insertions(+), 482 deletions(-) diff --git a/crates/cpp/tests/native_resources/rust/src/the_world.rs b/crates/cpp/tests/native_resources/rust/src/the_world.rs index 3c8daf8dc..e3bbbc059 100644 --- a/crates/cpp/tests/native_resources/rust/src/the_world.rs +++ b/crates/cpp/tests/native_resources/rust/src/the_world.rs @@ -1,347 +1,347 @@ // Generated by `wit-bindgen` 0.22.0. DO NOT EDIT! // Options used: pub mod foo { - pub mod foo { - #[allow(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, - } + pub mod foo { + #[allow(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, + } - 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) - } + 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); } - 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); - } - } + 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); + } + } + + } - 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) - } - } + } +} +pub mod exports { + pub mod foo { + pub mod foo { + #[allow(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, + } + + type _RRep = Option; + + 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(val: T) -> Self { + Self::type_guard::(); + let val: _RRep = Some(val); + let ptr: *mut _RRep = + _rt::Box::into_raw(_rt::Box::new(val)); + unsafe { + Self::from_handle(T::_resource_new(ptr.cast())) } - 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), - ); - } - } + } + + /// Gets access to the underlying `T` which represents this resource. + pub fn get(&self) -> &T { + let ptr = unsafe { &*self.as_ptr::() }; + ptr.as_ref().unwrap() + } + + /// Gets mutable access to the underlying `T` which represents this + /// resource. + pub fn get_mut(&mut self) -> &mut T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.as_mut().unwrap() + } + + /// Consumes this resource and returns the underlying `T`. + pub fn into_inner(self) -> T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.take().unwrap() + } + + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + Self { + handle: _rt::Resource::from_handle(handle), } - #[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) - } + } + + #[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() { + use core::any::TypeId; + static mut LAST_TYPE: Option = None; + unsafe { + assert!(!cfg!(target_feature = "threads")); + let id = TypeId::of::(); + match LAST_TYPE { + Some(ty) => assert!(ty == id, "cannot use two types with this resource type"), + None => LAST_TYPE = Some(id), + } } - #[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); - } + } + + #[doc(hidden)] + pub unsafe fn dtor(handle: *mut u8) { + Self::type_guard::(); + let _ = _rt::Box::from_raw(handle as *mut _RRep); + } + + fn as_ptr(&self) -> *mut _RRep { + R::type_guard::(); + unsafe { 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, } - #[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); - } + } + + /// Gets access to the underlying `T` in this resource. + pub fn get(&self) -> &T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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(&self) -> *mut _RRep { + R::type_guard::(); + 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(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(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() -> 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(arg0: i32,) {#[cfg(target_arch="wasm32")] + _rt::run_ctors_once();T::borrows(RBorrow::lift(arg0 as usize)); } -pub mod exports { - pub mod foo { - pub mod foo { - #[allow(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, - } - - type _RRep = Option; - - 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(val: T) -> Self { - Self::type_guard::(); - let val: _RRep = Some(val); - let ptr: *mut _RRep = _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(&self) -> &T { - let ptr = unsafe { &*self.as_ptr::() }; - ptr.as_ref().unwrap() - } - - /// Gets mutable access to the underlying `T` which represents this - /// resource. - pub fn get_mut(&mut self) -> &mut T { - let ptr = unsafe { &mut *self.as_ptr::() }; - ptr.as_mut().unwrap() - } - - /// Consumes this resource and returns the underlying `T`. - pub fn into_inner(self) -> T { - let ptr = unsafe { &mut *self.as_ptr::() }; - 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() { - use core::any::TypeId; - static mut LAST_TYPE: Option = None; - unsafe { - assert!(!cfg!(target_feature = "threads")); - let id = TypeId::of::(); - 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(handle: *mut u8) { - Self::type_guard::(); - let _ = _rt::Box::from_raw(handle as *mut _RRep); - } - - fn as_ptr(&self) -> *mut _RRep { - R::type_guard::(); - unsafe { 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(&self) -> &T { - let ptr = unsafe { &mut *self.as_ptr::() }; - 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(&self) -> *mut _RRep { - R::type_guard::(); - 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(arg0: i32) -> i32 { - 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(arg0: *mut u8, arg1: i32) { - T::add(RBorrow::lift(arg0 as usize).get(), arg1 as u32); - } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn _export_create_cabi() -> i32 { - let result0 = T::create(); - (result0).take_handle() as i32 - } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn _export_borrows_cabi(arg0: i32) { - T::borrows(RBorrow::lift(arg0 as usize)); - } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn _export_consume_cabi(arg0: i32) { - 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{ +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn _export_consume_cabi(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")] @@ -381,182 +381,190 @@ pub mod exports { >(rep) } }; - + };); } - #[doc(hidden)] - pub(crate) use __export_foo_foo_resources_cabi; - } - } - } +#[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 { - // 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, + 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 { + // 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, + } + + /// 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 Resource { + #[doc(hidden)] + pub unsafe fn from_handle(handle: u32) -> Self { + debug_assert!(handle != u32::MAX); + Self { + handle: AtomicU32::new(handle), + _marker: marker::PhantomData, + } } - /// A trait which all wasm resources implement, namely providing the ability to - /// drop a resource. + /// Takes ownership of the handle owned by `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); + /// Note that this ideally would be `into_handle` taking `Resource` 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` 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) -> u32 { + resource.handle.swap(u32::MAX, Relaxed) } - impl Resource { - #[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` 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` 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) -> u32 { - resource.handle.swap(u32::MAX, Relaxed) - } - - #[doc(hidden)] - pub fn handle(resource: &Resource) -> u32 { - resource.handle.load(Relaxed) - } + #[doc(hidden)] + pub fn handle(resource: &Resource) -> u32 { + resource.handle.load(Relaxed) } + } - impl fmt::Debug for Resource { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Resource") - .field("handle", &self.handle) - .finish() - } + impl fmt::Debug for Resource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Resource") + .field("handle", &self.handle) + .finish() } - - impl Drop for Resource { - 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), - } - } + } + + impl Drop for Resource { + 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: T) -> i32 { - t.as_i32() + } + + pub fn as_i32(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() } - - pub trait AsI32 { - fn as_i32(self) -> i32; + } + + impl AsI32 for i32 { + #[inline] + fn as_i32(self) -> i32 { + self as i32 } - - impl<'a, T: Copy + AsI32> AsI32 for &'a T { - fn as_i32(self) -> i32 { - (*self).as_i32() - } + } + + impl AsI32 for u32 { + #[inline] + 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 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 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 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 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 char { + #[inline] + fn as_i32(self) -> i32 { + self as i32 } - - impl AsI32 for usize { - #[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; - extern crate alloc as alloc_crate; + } + 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 @@ -608,5 +616,6 @@ e-world\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x070.20 #[doc(hidden)] #[cfg(target_arch = "wasm32")] pub fn __link_custom_section_describing_imports() { - wit_bindgen::rt::maybe_link_cabi_realloc(); + wit_bindgen::rt::maybe_link_cabi_realloc(); } + diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 57d8bc82e..30265ae53 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -310,14 +310,20 @@ macro_rules! {macro_name} {{ Identifier::World(_) => unreachable!(), }; let camel = name.to_upper_camel_case(); + let dtor_symbol = make_external_symbol( + &module, + &(String::from("[dtor]") + &name), + AbiVariant::GuestExport, + ); uwriteln!( self.src, r#" const _: () = {{ #[doc(hidden)] - #[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) {{ $($path_to_types)*::{camel}::dtor::< <$ty as $($path_to_types)*::Guest>::{camel} >(rep) From 6a439f6853a2e8ccc2cfd7e399c3fd6603ab093b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 16 Mar 2024 14:43:46 +0100 Subject: [PATCH 129/672] using unique_ptr for resources feels right --- crates/cpp/helper-types/wit-common.h | 2 +- crates/cpp/tests/native_resources/Makefile | 16 +-- .../exports-foo-foo-resources-R.h | 20 ++++ .../cpp/tests/native_resources/guest/Makefile | 15 +++ .../guest/exports-foo-foo-resources-R.h | 31 ++++++ .../tests/native_resources/guest/guest.cpp | 21 ++++ .../tests/native_resources/guest/guest.verscr | 0 .../native_resources/guest/the_world.cpp | 103 ++++++++++++++++++ .../native_resources/guest/the_world_cpp.h | 44 ++++++++ crates/cpp/tests/native_resources/main.cpp | 12 ++ .../native_resources/rust/src/the_world.rs | 4 +- .../native_resources/the_world_cpp_native.h | 44 ++++++++ .../native_resources/the_world_native.cpp | 63 +++++++++++ .../native_resources/wit/resources_simple.wit | 16 +++ 14 files changed, 380 insertions(+), 11 deletions(-) create mode 100644 crates/cpp/tests/native_resources/exports-foo-foo-resources-R.h create mode 100644 crates/cpp/tests/native_resources/guest/Makefile create mode 100644 crates/cpp/tests/native_resources/guest/exports-foo-foo-resources-R.h create mode 100644 crates/cpp/tests/native_resources/guest/guest.cpp create mode 100644 crates/cpp/tests/native_resources/guest/guest.verscr create mode 100644 crates/cpp/tests/native_resources/guest/the_world.cpp create mode 100644 crates/cpp/tests/native_resources/guest/the_world_cpp.h create mode 100644 crates/cpp/tests/native_resources/main.cpp create mode 100644 crates/cpp/tests/native_resources/the_world_cpp_native.h create mode 100644 crates/cpp/tests/native_resources/the_world_native.cpp create mode 100644 crates/cpp/tests/native_resources/wit/resources_simple.wit diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h index 4e5e86d94..35f5a1428 100644 --- a/crates/cpp/helper-types/wit-common.h +++ b/crates/cpp/helper-types/wit-common.h @@ -29,7 +29,7 @@ template class span { T const &operator[](size_t index) { return address[index]; } // create from any compatible vector (borrows data!) template - span(std::vector const&vec) : address(vec.data()), length(v.size()) {} + span(std::vector const&vec) : address(vec.data()), length(vec.size()) {} }; #endif diff --git a/crates/cpp/tests/native_resources/Makefile b/crates/cpp/tests/native_resources/Makefile index 8f0b81869..d50e678da 100644 --- a/crates/cpp/tests/native_resources/Makefile +++ b/crates/cpp/tests/native_resources/Makefile @@ -3,22 +3,22 @@ WIT_BINDGEN=../../../../target/debug/wit-bindgen all: libresources.so app-resources -libresources.so: the_world.pie.o guest.pie.o - $(CXX) $(CXXFLAGS) -shared -o $@ $^ -Wl,--version-script=guest.lds +#libresources.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 $^ +# %.pie.o: %.cpp +# $(CXX) $(CXXFLAGS) -fPIE -o $@ -c $^ app-resources: the_world_native.o main.o $(CXX) $(CXXFLAGS) -o $@ $^ -L. -lresources bindgen: wit/resources_simple.wit - $(WIT_BINDGEN) cpp wit --wasm64 --format + cd guest; ../$(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.wasm: the_world.cpp guest.cpp +# /opt/wasi-sdk/bin/clang++ -o $@ $^ $(CXXFLAGS) clean: - -rm *.o libresources.so app-resources + -rm *.o app-resources diff --git a/crates/cpp/tests/native_resources/exports-foo-foo-resources-R.h b/crates/cpp/tests/native_resources/exports-foo-foo-resources-R.h new file mode 100644 index 000000000..fbc8f229e --- /dev/null +++ b/crates/cpp/tests/native_resources/exports-foo-foo-resources-R.h @@ -0,0 +1,20 @@ +/* User class definition file, autogenerated once, then user modified + * Updated versions of this file are generated into + * exports-foo-foo-resources-R.h.template. + */ +namespace exports { +namespace foo { +namespace foo { +namespace resources { +class R : public wit::ResourceExportBase { + +public: + static void Dtor(R *self) { delete self; }; + R(uint32_t a); + void Add(uint32_t b) const; +}; + +} // namespace resources +} // namespace foo +} // namespace foo +} // namespace exports 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..005b039cd --- /dev/null +++ b/crates/cpp/tests/native_resources/guest/exports-foo-foo-resources-R.h @@ -0,0 +1,31 @@ +/* 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 +namespace exports { +namespace foo { +namespace foo { +namespace resources { +class R : public wit::ResourceExportBase { + uint32_t value; + +public: + static void Dtor(R *self) { delete self; }; + struct Deleter { + void operator()(R* ptr) const { R::Dtor(ptr); } + }; + typedef std::unique_ptr Owned; + R(uint32_t a) : value(a) {} + static Owned New(uint32_t a) { return Owned(new exports::foo::foo::resources::R(a)); } + void Add(uint32_t b) { value += b; } + static int32_t ResourceNew(R *self); + 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..03d94e8fc --- /dev/null +++ b/crates/cpp/tests/native_resources/guest/guest.cpp @@ -0,0 +1,21 @@ +#include "the_world_cpp.h" +#include + +exports::foo::foo::resources::R exports::foo::foo::resources::Create() { + return exports::foo::foo::resources::R(1); +} + +void exports::foo::foo::resources::Borrows(std::reference_wrapper o) { + printf("resource borrowed with %d\n", o.get().GetValue()); +} + +void exports::foo::foo::resources::Consume(exports::foo::foo::resources::R&& o) { + printf("resource consumed with %d\n", o.GetValue()); + + 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..e69de29bb 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..a96e45b26 --- /dev/null +++ b/crates/cpp/tests/native_resources/guest/the_world.cpp @@ -0,0 +1,103 @@ +// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT! +#include "the_world_cpp.h" +#include // 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-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 = ret; +} +void foo::foo::resources::R::Add(uint32_t b) const { + fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd((*this).get_handle(), + (int32_t(b))); +} +foo::foo::resources::R foo::foo::resources::Create() { + auto ret = fooX3AfooX2FresourcesX00create(); + return ret; +} +void foo::foo::resources::Borrows(std::reference_wrapper 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::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->handle; +} +extern "C" + __attribute__((__export_name__("foo:foo/resources#[method]r.add"))) void + fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd(uint8_t *arg0, int32_t arg1) { + ((exports::foo::foo::resources::R *)arg0)->Add((uint32_t(arg1))); +} +int32_t exports::foo::foo::resources::R::ResourceNew(R *self) { + return X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr( + (uint8_t *)self); +} +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->handle; +} +extern "C" __attribute__((__export_name__("foo:foo/resources#borrows"))) void +fooX3AfooX2FresourcesX23borrows(int32_t arg0) { + exports::foo::foo::resources::Borrows(arg0); +} +extern "C" __attribute__((__export_name__("foo:foo/resources#consume"))) void +fooX3AfooX2FresourcesX23consume(int32_t arg0) { + exports::foo::foo::resources::Consume(arg0); +} + +// 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..2cd72ee9f --- /dev/null +++ b/crates/cpp/tests/native_resources/guest/the_world_cpp.h @@ -0,0 +1,44 @@ +// 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 +#include +#include +#include +#include +#include +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 Create(); +void Borrows(std::reference_wrapper 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 { +std::unique_ptr Create(); +void Borrows(std::reference_wrapper o); +void Consume(std::unique_ptr o); +} // namespace resources +} // namespace foo +} // namespace foo +} // namespace exports + +#endif diff --git a/crates/cpp/tests/native_resources/main.cpp b/crates/cpp/tests/native_resources/main.cpp new file mode 100644 index 000000000..c9efbf488 --- /dev/null +++ b/crates/cpp/tests/native_resources/main.cpp @@ -0,0 +1,12 @@ + +#include "the_world_cpp_native.h" +#include + +int main() { + 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}; + return 0; +} diff --git a/crates/cpp/tests/native_resources/rust/src/the_world.rs b/crates/cpp/tests/native_resources/rust/src/the_world.rs index e3bbbc059..3ce45e9dc 100644 --- a/crates/cpp/tests/native_resources/rust/src/the_world.rs +++ b/crates/cpp/tests/native_resources/rust/src/the_world.rs @@ -289,7 +289,7 @@ pub mod exports { } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_borrows_cabi(arg0: i32,) {#[cfg(target_arch="wasm32")] + pub unsafe fn _export_borrows_cabi(arg0: *mut u8,) {#[cfg(target_arch="wasm32")] _rt::run_ctors_once();T::borrows(RBorrow::lift(arg0 as usize)); } #[doc(hidden)] @@ -361,7 +361,7 @@ macro_rules! __export_foo_foo_resources_cabi{ } #[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: i32,) { + unsafe extern "C" fn fooX3AfooX2FresourcesX23borrows(arg0: *mut u8,) { $($path_to_types)*::_export_borrows_cabi::<$ty>(arg0) } #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/resources#consume")] 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..3fc82b483 --- /dev/null +++ b/crates/cpp/tests/native_resources/the_world_cpp_native.h @@ -0,0 +1,44 @@ +// 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 +#include +#include +#include +#include +namespace foo { +namespace foo { +namespace resources { +class R : public wit::ResourceImportBase { + +public: + ~R(); + R(uint32_t a); + void Add(uint32_t b); + R(wit::ResourceImportBase &&); + + R(R &&) = default; +}; + +R Create(); +void Borrows(std::reference_wrapper 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 Create(); +void Borrows(std::reference_wrapper 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..bda320ef9 --- /dev/null +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -0,0 +1,63 @@ +// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT! +#include "the_world_cpp_native.h" +template std::map wit::ResourceExportBase::resources; +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(int32_t); +extern "C" __attribute__((import_module("foo:foo/resources"))) +__attribute__((import_name("consume"))) void + fooX3AfooX2FresourcesX23consume(int32_t); +foo::foo::resources::R::~R() { + foo::foo::resources::R::remove_resource((*this)); +} +extern "C" int32_t fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t arg0) { + auto result0 = foo::foo::resources::R((uint32_t(arg0))); + this->handle = result0.store_resource(std::move(result0)); +} +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.store_resource(std::move(result0)); +} +extern "C" void fooX3AfooX2FresourcesX00borrows(int32_t arg0) { + foo::foo::resources::Borrows(arg0); +} +extern "C" void fooX3AfooX2FresourcesX00consume(int32_t arg0) { + foo::foo::resources::Consume(arg0); +} +void exports::foo::foo::resources::R::Dtor(R *self) { + exports::foo::foo::resources::R::Dtor( + (exports::foo::foo::resources::R *)arg0); +} +exports::foo::foo::resources::R::R(uint32_t a) { + auto ret = fooX3AfooX2FresourcesX23X5BconstructorX5Dr((int32_t(a))); + return ret; +} +void exports::foo::foo::resources::R::Add(uint32_t b) const { + fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd((*this).get_handle(), + (int32_t(b))); +} +exports::foo::foo::resources::R exports::foo::foo::resources::Create() { + auto ret = fooX3AfooX2FresourcesX23create(); + return ret; +} +void exports::foo::foo::resources::Borrows(std::reference_wrapper o) { + fooX3AfooX2FresourcesX23borrows(o.get_handle()); +} +void exports::foo::foo::resources::Consume(R o) { + fooX3AfooX2FresourcesX23consume(o.store_resource(std::move(o))); +} + +// 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); + consume: func(o: r); +} + +world the-world { + import resources; + export resources; +} From 1214c0fa8ea939d3a138db947317a4d89e965030 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 16 Mar 2024 17:43:54 +0100 Subject: [PATCH 130/672] fully operational Rust example, tested with jco --- .../tests/native_resources/rust/Cargo.lock | 303 ++++++++++++++++++ .../tests/native_resources/rust/Cargo.toml | 5 +- .../tests/native_resources/rust/generate.sh | 4 + .../native_resources/rust/html/index.html | 15 + .../tests/native_resources/rust/html/main.js | 8 + .../native_resources/rust/html/resources.js | 21 ++ .../tests/native_resources/rust/src/lib.rs | 6 +- .../native_resources/rust/src/the_world.rs | 8 +- 8 files changed, 362 insertions(+), 8 deletions(-) create mode 100755 crates/cpp/tests/native_resources/rust/generate.sh create mode 100644 crates/cpp/tests/native_resources/rust/html/index.html create mode 100644 crates/cpp/tests/native_resources/rust/html/main.js create mode 100644 crates/cpp/tests/native_resources/rust/html/resources.js diff --git a/crates/cpp/tests/native_resources/rust/Cargo.lock b/crates/cpp/tests/native_resources/rust/Cargo.lock index 87ef1c08d..fcfcb721c 100644 --- a/crates/cpp/tests/native_resources/rust/Cargo.lock +++ b/crates/cpp/tests/native_resources/rust/Cargo.lock @@ -2,6 +2,309 @@ # 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.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasm-metadata" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2" +dependencies = [ + "anyhow", + "indexmap", + "serde", + "serde_derive", + "serde_json", + "spdx", + "wasm-encoder", + "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasmparser" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708" +dependencies = [ + "bitflags", + "indexmap", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.201.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#9c01485c2713d926e8f5b67b3e6f62f792da8ba7" +dependencies = [ + "bitflags", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.22.0" +dependencies = [ + "bitflags", + "wit-bindgen-rt", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.22.0" +dependencies = [ + "anyhow", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.22.0" + +[[package]] +name = "wit-bindgen-rust" +version = "0.22.0" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.22.0" +dependencies = [ + "anyhow", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.201.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#9c01485c2713d926e8f5b67b3e6f62f792da8ba7" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser 0.201.0 (git+https://github.com/bytecodealliance/wasm-tools)", +] diff --git a/crates/cpp/tests/native_resources/rust/Cargo.toml b/crates/cpp/tests/native_resources/rust/Cargo.toml index bbcef57c9..3117f59f0 100644 --- a/crates/cpp/tests/native_resources/rust/Cargo.toml +++ b/crates/cpp/tests/native_resources/rust/Cargo.toml @@ -7,7 +7,10 @@ edition = "2021" [lib] crate-type = ["cdylib"] -#wit-bindgen = { path = "../../../../guest-rust" } [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/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 @@ + + + + + + + + + + +
Look into console
+ + + + \ 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 index ea20e2d33..ea5425a4e 100644 --- a/crates/cpp/tests/native_resources/rust/src/lib.rs +++ b/crates/cpp/tests/native_resources/rust/src/lib.rs @@ -26,10 +26,10 @@ impl Guest for MyWorld { resources::R::new(MyResource::new(1)) } fn borrows(o: RBorrow<'_>) { - println!("resource borrowed with {:?}", o); + println!("resource borrowed with {:?}", o.get::().0.lock().unwrap()); } - fn consume(o: resources::R) { - println!("resource consumed with {:?}", o); + fn consume(o: Self::R) { + println!("resource consumed with {:?}", o.0.lock().unwrap()); println!("exercise the other direction"); let obj = the_world::foo::foo::resources::create(); diff --git a/crates/cpp/tests/native_resources/rust/src/the_world.rs b/crates/cpp/tests/native_resources/rust/src/the_world.rs index 3ce45e9dc..cd96842ad 100644 --- a/crates/cpp/tests/native_resources/rust/src/the_world.rs +++ b/crates/cpp/tests/native_resources/rust/src/the_world.rs @@ -294,14 +294,14 @@ pub mod exports { } #[doc(hidden)] #[allow(non_snake_case)] -pub unsafe fn _export_consume_cabi(arg0: i32,) {#[cfg(target_arch="wasm32")] -_rt::run_ctors_once();T::consume(R::from_handle(arg0 as u32)); +pub unsafe fn _export_consume_cabi(arg0: *mut u8,) {#[cfg(target_arch="wasm32")] +_rt::run_ctors_once();T::consume(_rt::Box::<_RRep>::from_raw(arg0.cast()).unwrap()); } pub trait Guest { type R: GuestR; fn create() -> R; fn borrows(o: RBorrow<'_>,); - fn consume(o: R,); + fn consume(o: Self::R,); } pub trait GuestR: 'static { @@ -366,7 +366,7 @@ macro_rules! __export_foo_foo_resources_cabi{ } #[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,) { + unsafe extern "C" fn fooX3AfooX2FresourcesX23consume(arg0: *mut u8,) { $($path_to_types)*::_export_consume_cabi::<$ty>(arg0) } From 80bbc6c56c1a472374786ff524abd35091d1bd8f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 16 Mar 2024 18:39:35 +0100 Subject: [PATCH 131/672] getting C++ right is hard --- crates/cpp/helper-types/wit-guest.h | 1 + .../tests/native_resources/guest/guest.cpp | 9 +- .../tests/native_resources/guest/guest.verscr | 5 + .../native_resources/guest/the_world.cpp | 14 +- .../native_resources/rust/html/component.js | 1914 +++++++++++++++++ 5 files changed, 1933 insertions(+), 10 deletions(-) create mode 100644 crates/cpp/tests/native_resources/rust/html/component.js diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 6682e358c..e905d8c64 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -93,5 +93,6 @@ template class ResourceExportBase { ResourceExportBase& operator=(ResourceExportBase &&b) = delete; ResourceExportBase& operator=(ResourceExportBase const&) = delete; int32_t get_handle() const { return handle; } + int32_t into_handle() { int32_t result = handle; handle=invalid; return result; } }; } // namespace wit diff --git a/crates/cpp/tests/native_resources/guest/guest.cpp b/crates/cpp/tests/native_resources/guest/guest.cpp index 03d94e8fc..1cb4096cc 100644 --- a/crates/cpp/tests/native_resources/guest/guest.cpp +++ b/crates/cpp/tests/native_resources/guest/guest.cpp @@ -1,16 +1,17 @@ #include "the_world_cpp.h" #include -exports::foo::foo::resources::R exports::foo::foo::resources::Create() { - return exports::foo::foo::resources::R(1); +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 o) { printf("resource borrowed with %d\n", o.get().GetValue()); } -void exports::foo::foo::resources::Consume(exports::foo::foo::resources::R&& o) { - printf("resource consumed with %d\n", o.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(); diff --git a/crates/cpp/tests/native_resources/guest/guest.verscr b/crates/cpp/tests/native_resources/guest/guest.verscr index e69de29bb..e6862b74e 100644 --- a/crates/cpp/tests/native_resources/guest/guest.verscr +++ 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 index a96e45b26..7cbb91f73 100644 --- a/crates/cpp/tests/native_resources/guest/the_world.cpp +++ b/crates/cpp/tests/native_resources/guest/the_world.cpp @@ -72,7 +72,7 @@ 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->handle; + return result0->into_handle(); } extern "C" __attribute__((__export_name__("foo:foo/resources#[method]r.add"))) void @@ -89,15 +89,17 @@ void exports::foo::foo::resources::R::ResourceDrop(int32_t id) { extern "C" __attribute__((__export_name__("foo:foo/resources#create"))) int32_t fooX3AfooX2FresourcesX23create() { auto result0 = exports::foo::foo::resources::Create(); - return result0->handle; + return result0.release()->handle; } extern "C" __attribute__((__export_name__("foo:foo/resources#borrows"))) void -fooX3AfooX2FresourcesX23borrows(int32_t arg0) { - exports::foo::foo::resources::Borrows(arg0); +fooX3AfooX2FresourcesX23borrows(int8_t* arg0) { + exports::foo::foo::resources::Borrows(std::cref(*(exports::foo::foo::resources::R const*)arg0)); } extern "C" __attribute__((__export_name__("foo:foo/resources#consume"))) void -fooX3AfooX2FresourcesX23consume(int32_t arg0) { - exports::foo::foo::resources::Consume(arg0); +fooX3AfooX2FresourcesX23consume(int8_t* arg0) { + auto obj = exports::foo::foo::resources::R::Owned((exports::foo::foo::resources::R*)arg0); + obj->into_handle(); + exports::foo::foo::resources::Consume(std::move(obj)); } // Component Adapters 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 From 2be0d7a1fa33804a3d5bcdef8f9651455ef4bc51 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 16 Mar 2024 23:33:15 +0100 Subject: [PATCH 132/672] closer to a resource strategy --- crates/cpp/helper-types/wit-common.h | 29 ----------- crates/cpp/helper-types/wit-guest.h | 29 +++++++++++ crates/cpp/helper-types/wit-host.h | 31 +++++++++++- .../native_resources/foo-foo-resources-R.h | 27 ++++++++++ .../guest/exports-foo-foo-resources-R.h | 2 +- crates/cpp/tests/native_resources/main.cpp | 8 +-- .../native_resources/the_world_cpp_native.h | 40 +++++++++------ .../native_resources/the_world_native.cpp | 50 +++++++++++++------ 8 files changed, 149 insertions(+), 67 deletions(-) create mode 100644 crates/cpp/tests/native_resources/foo-foo-resources-R.h diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h index 35f5a1428..225f57755 100644 --- a/crates/cpp/helper-types/wit-common.h +++ b/crates/cpp/helper-types/wit-common.h @@ -33,35 +33,6 @@ template class span { }; #endif -class ResourceImportBase { -public: - static const int32_t invalid = -1; - -protected: - int32_t handle; - -public: - ResourceImportBase(int32_t h = invalid) : handle(h) {} - ResourceImportBase(ResourceImportBase &&r) : handle(r.handle) { - r.handle = invalid; - } - ResourceImportBase(ResourceImportBase const &) = delete; - void set_handle(int32_t h) { handle = h; } - int32_t get_handle() const { return handle; } - int32_t into_handle() { - int32_t h = handle; - handle = invalid; - return h; - } - ResourceImportBase &operator=(ResourceImportBase &&r) { - assert(handle < 0); - handle = r.handle; - r.handle = invalid; - return *this; - } - ResourceImportBase &operator=(ResourceImportBase const &r) = delete; -}; - template struct Owned { T *ptr; }; diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index e905d8c64..7f1f92781 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -95,4 +95,33 @@ template class ResourceExportBase { int32_t get_handle() const { return handle; } int32_t into_handle() { int32_t result = handle; handle=invalid; return result; } }; + +class ResourceImportBase { +public: + static const int32_t invalid = -1; + +protected: + int32_t handle; + +public: + ResourceImportBase(int32_t h = invalid) : handle(h) {} + ResourceImportBase(ResourceImportBase &&r) : handle(r.handle) { + r.handle = invalid; + } + ResourceImportBase(ResourceImportBase const &) = delete; + void set_handle(int32_t h) { handle = h; } + int32_t get_handle() const { return handle; } + int32_t into_handle() { + int32_t h = handle; + handle = invalid; + return h; + } + ResourceImportBase &operator=(ResourceImportBase &&r) { + assert(handle < 0); + 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 index 9a10b2a8d..46c76c1ea 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -182,7 +182,7 @@ template class guest_owned : public T { #endif }; -template class ResourceExportBase { +template class ResourceTable { static std::map resources; public: @@ -206,4 +206,33 @@ template class ResourceExportBase { return std::move(result); } }; + +// guest exported resource +class ResourceExportBase : public ResourceTable { + protected: + guest_address rep; + int32_t index; + public: + ResourceExportBase() : rep(0), index(-1) {} + ResourceExportBase(int32_t i) : rep(*lookup_resource(i)), index(i) {} + ResourceExportBase(ResourceExportBase &&) = default; + ResourceExportBase(ResourceExportBase const&) = delete; + ResourceExportBase& operator=(ResourceExportBase const&)=delete; + ResourceExportBase& operator=(ResourceExportBase &&)=delete; + int32_t get_handle() const { return index; } + guest_address get_rep() const { return rep; } +}; + +template +class ResourceImportBase : public ResourceTable { + int32_t index; + public: + static const int32_t invalid=-1; + ResourceImportBase() : index(invalid) {} + ResourceImportBase(ResourceImportBase &&) = default; + ResourceImportBase(ResourceImportBase const&) = delete; + ResourceImportBase& operator=(ResourceImportBase const&)=delete; + ResourceImportBase& operator=(ResourceImportBase &&)=delete; +}; + } // namespace wit 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..abd9e3b64 --- /dev/null +++ b/crates/cpp/tests/native_resources/foo-foo-resources-R.h @@ -0,0 +1,27 @@ +/* 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 +namespace foo { +namespace foo { +namespace resources { +class R : public wit::ResourceImportBase { + uint32_t value; + +public: + static void Dtor(R *self) { delete self; }; + struct Deleter { + void operator()(R* ptr) const { R::Dtor(ptr); } + }; + typedef std::unique_ptr Owned; + 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/exports-foo-foo-resources-R.h b/crates/cpp/tests/native_resources/guest/exports-foo-foo-resources-R.h index 005b039cd..6c7596402 100644 --- 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 @@ -17,7 +17,7 @@ class R : public wit::ResourceExportBase { }; typedef std::unique_ptr Owned; R(uint32_t a) : value(a) {} - static Owned New(uint32_t a) { return Owned(new exports::foo::foo::resources::R(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 void ResourceDrop(int32_t id); diff --git a/crates/cpp/tests/native_resources/main.cpp b/crates/cpp/tests/native_resources/main.cpp index c9efbf488..eba68a2ff 100644 --- a/crates/cpp/tests/native_resources/main.cpp +++ b/crates/cpp/tests/native_resources/main.cpp @@ -3,10 +3,10 @@ #include int main() { - auto obj = foo::foo::resources::Create(); + auto obj = exports::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}; + 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/the_world_cpp_native.h b/crates/cpp/tests/native_resources/the_world_cpp_native.h index 3fc82b483..96277a311 100644 --- a/crates/cpp/tests/native_resources/the_world_cpp_native.h +++ b/crates/cpp/tests/native_resources/the_world_cpp_native.h @@ -7,38 +7,46 @@ #include #include #include + +// guest imports (implemented here) +#include "foo-foo-resources-R.h" +namespace foo { +namespace foo { +namespace resources { +R::Owned Create(); +void Borrows(std::reference_wrapper o); +void Consume(R::Owned o); +} // namespace resources +} // namespace foo +} // namespace foo + +// guest exports +namespace exports { namespace foo { namespace foo { namespace resources { -class R : public wit::ResourceImportBase { +// only a proxy, no data, an import? +class R : public wit::ResourceExportBase { public: ~R(); R(uint32_t a); - void Add(uint32_t b); - R(wit::ResourceImportBase &&); + void Add(uint32_t b) const; + R(ResourceExportBase &&); R(R &&) = default; + R(R const&) = delete; + R& operator=(R const&)=delete; + R& operator=(R &&)=delete; }; R Create(); void Borrows(std::reference_wrapper o); -void Consume(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 Create(); -void Borrows(std::reference_wrapper 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 index bda320ef9..59d63f30a 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -1,6 +1,11 @@ // Generated by `wit-bindgen` 0.3.0. DO NOT EDIT! #include "the_world_cpp_native.h" -template std::map wit::ResourceExportBase::resources; +#include +template std::map wit::ResourceTable::resources; + +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); @@ -12,16 +17,20 @@ __attribute__((import_name("create"))) int32_t fooX3AfooX2FresourcesX23create(); extern "C" __attribute__((import_module("foo:foo/resources"))) __attribute__((import_name("borrows"))) void - fooX3AfooX2FresourcesX23borrows(int32_t); + fooX3AfooX2FresourcesX23borrows(uint8_t*); extern "C" __attribute__((import_module("foo:foo/resources"))) __attribute__((import_name("consume"))) void - fooX3AfooX2FresourcesX23consume(int32_t); + fooX3AfooX2FresourcesX23consume(uint8_t*); + +#if 0 foo::foo::resources::R::~R() { foo::foo::resources::R::remove_resource((*this)); } +#endif extern "C" int32_t fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t arg0) { auto result0 = foo::foo::resources::R((uint32_t(arg0))); - this->handle = result0.store_resource(std::move(result0)); + abort(); +// this->handle = result0.store_resource(std::move(result0)); } extern "C" void fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(int32_t arg0, int32_t arg1) { @@ -29,35 +38,44 @@ extern "C" void fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(int32_t arg0, } extern "C" int32_t fooX3AfooX2FresourcesX00create() { auto result0 = foo::foo::resources::Create(); - return result0.store_resource(std::move(result0)); + abort(); +// return result0.store_resource(std::move(result0)); } extern "C" void fooX3AfooX2FresourcesX00borrows(int32_t arg0) { - foo::foo::resources::Borrows(arg0); + foo::foo::resources::Borrows(*foo::foo::resources::R::lookup_resource(arg0)); } extern "C" void fooX3AfooX2FresourcesX00consume(int32_t arg0) { - foo::foo::resources::Consume(arg0); + foo::foo::resources::Consume(foo::foo::resources::R::Owned(foo::foo::resources::R::lookup_resource(arg0))); } -void exports::foo::foo::resources::R::Dtor(R *self) { - exports::foo::foo::resources::R::Dtor( - (exports::foo::foo::resources::R *)arg0); +extern "C" void fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t) {abort();} +extern "C" int32_t X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t*) {abort();} +extern "C" void X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t) {abort();} + +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))); - return ret; + this->index = ret; + this->rep = *lookup_resource(ret); } void exports::foo::foo::resources::R::Add(uint32_t b) const { - fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd((*this).get_handle(), + fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd((*this).get_rep(), (int32_t(b))); } exports::foo::foo::resources::R exports::foo::foo::resources::Create() { auto ret = fooX3AfooX2FresourcesX23create(); - return ret; + return wit::ResourceExportBase{ret}; } void exports::foo::foo::resources::Borrows(std::reference_wrapper o) { - fooX3AfooX2FresourcesX23borrows(o.get_handle()); + fooX3AfooX2FresourcesX23borrows(o.get().get_rep()); } -void exports::foo::foo::resources::Consume(R o) { - fooX3AfooX2FresourcesX23consume(o.store_resource(std::move(o))); +void exports::foo::foo::resources::Consume(R &&o) { + auto rep = o.get_rep(); + abort(); +// R::remove_resource(o.get_handle()); + fooX3AfooX2FresourcesX23consume(rep); } // Component Adapters From 650757943971d509707696473f47480d0899178d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 16 Mar 2024 23:41:05 +0100 Subject: [PATCH 133/672] fully linking --- crates/cpp/tests/native_resources/main.cpp | 4 ++++ crates/cpp/tests/native_resources/the_world_native.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/crates/cpp/tests/native_resources/main.cpp b/crates/cpp/tests/native_resources/main.cpp index eba68a2ff..6fc379825 100644 --- a/crates/cpp/tests/native_resources/main.cpp +++ b/crates/cpp/tests/native_resources/main.cpp @@ -2,6 +2,10 @@ #include "the_world_cpp_native.h" #include +foo::foo::resources::R::Owned foo::foo::resources::Create() { abort();} +void foo::foo::resources::Borrows(std::reference_wrapper) { abort(); } +void foo::foo::resources::Consume(R::Owned o) { abort(); } + int main() { auto obj = exports::foo::foo::resources::Create(); obj.Add(12); diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index 59d63f30a..4fc960b4f 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -79,3 +79,5 @@ void exports::foo::foo::resources::Consume(R &&o) { } // Component Adapters +exports::foo::foo::resources::R::R(wit::ResourceExportBase&&) +{ abort(); } \ No newline at end of file From 6abe6056f71800e99dd694cf47776740d90d7de5 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 17 Mar 2024 10:36:12 +0100 Subject: [PATCH 134/672] fully operational, but Rust guest has a leak, C++ guest accesses null pointer at main end --- crates/cpp/helper-types/wit-host.h | 22 ++++++++---- crates/cpp/tests/native_resources/.gitignore | 3 ++ .../tests/native_resources/guest/.gitignore | 2 ++ crates/cpp/tests/native_resources/main.cpp | 12 +++++-- .../native_resources/the_world_cpp_native.h | 2 +- .../native_resources/the_world_native.cpp | 36 +++++++++++-------- 6 files changed, 53 insertions(+), 24 deletions(-) create mode 100644 crates/cpp/tests/native_resources/.gitignore create mode 100644 crates/cpp/tests/native_resources/guest/.gitignore diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 46c76c1ea..cfae84f31 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -200,7 +200,7 @@ template class ResourceTable { auto iter = resources.find(id); std::optional result; if (iter!=resources.end()) { - result = std::move(*iter); + result = std::move(iter->second); resources.erase(iter); } return std::move(result); @@ -215,24 +215,34 @@ class ResourceExportBase : public ResourceTable { public: ResourceExportBase() : rep(0), index(-1) {} ResourceExportBase(int32_t i) : rep(*lookup_resource(i)), index(i) {} - ResourceExportBase(ResourceExportBase &&) = default; + ResourceExportBase(ResourceExportBase &&b) : rep(b.rep), index(b.index) {b.rep=0;} ResourceExportBase(ResourceExportBase const&) = delete; ResourceExportBase& operator=(ResourceExportBase const&)=delete; - ResourceExportBase& operator=(ResourceExportBase &&)=delete; + ResourceExportBase& operator=(ResourceExportBase &&b) { + assert(rep==0); + rep = b.rep; + index = b.index; + b.rep = 0; + } 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; } }; template -class ResourceImportBase : public ResourceTable { +class ResourceImportBase : public ResourceTable { int32_t index; public: static const int32_t invalid=-1; - ResourceImportBase() : index(invalid) {} - ResourceImportBase(ResourceImportBase &&) = default; + 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; + } }; } // namespace wit diff --git a/crates/cpp/tests/native_resources/.gitignore b/crates/cpp/tests/native_resources/.gitignore new file mode 100644 index 000000000..0830c813c --- /dev/null +++ b/crates/cpp/tests/native_resources/.gitignore @@ -0,0 +1,3 @@ +/*.o +/*.so +/app-resources diff --git a/crates/cpp/tests/native_resources/guest/.gitignore b/crates/cpp/tests/native_resources/guest/.gitignore new file mode 100644 index 000000000..925b4b450 --- /dev/null +++ b/crates/cpp/tests/native_resources/guest/.gitignore @@ -0,0 +1,2 @@ +/*.o +/*.so diff --git a/crates/cpp/tests/native_resources/main.cpp b/crates/cpp/tests/native_resources/main.cpp index 6fc379825..3ce2ea1b2 100644 --- a/crates/cpp/tests/native_resources/main.cpp +++ b/crates/cpp/tests/native_resources/main.cpp @@ -2,9 +2,15 @@ #include "the_world_cpp_native.h" #include -foo::foo::resources::R::Owned foo::foo::resources::Create() { abort();} -void foo::foo::resources::Borrows(std::reference_wrapper) { abort(); } -void foo::foo::resources::Consume(R::Owned o) { abort(); } +foo::foo::resources::R::Owned foo::foo::resources::Create() { + return R::New(1); +} +void foo::foo::resources::Borrows(std::reference_wrapper 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(); diff --git a/crates/cpp/tests/native_resources/the_world_cpp_native.h b/crates/cpp/tests/native_resources/the_world_cpp_native.h index 96277a311..05ef0121e 100644 --- a/crates/cpp/tests/native_resources/the_world_cpp_native.h +++ b/crates/cpp/tests/native_resources/the_world_cpp_native.h @@ -37,7 +37,7 @@ class R : public wit::ResourceExportBase { R(R &&) = default; R(R const&) = delete; R& operator=(R const&)=delete; - R& operator=(R &&)=delete; + R& operator=(R &&)=default; }; R Create(); diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index 4fc960b4f..453c98d6b 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -28,28 +28,38 @@ foo::foo::resources::R::~R() { } #endif extern "C" int32_t fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t arg0) { - auto result0 = foo::foo::resources::R((uint32_t(arg0))); - abort(); + auto result0 = foo::foo::resources::R::New((uint32_t(arg0))); + return result0.release()->get_handle(); // this->handle = result0.store_resource(std::move(result0)); } extern "C" void fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(int32_t arg0, int32_t arg1) { - foo::foo::resources::R::lookup_resource(arg0)->Add((uint32_t(arg1))); + (*foo::foo::resources::R::lookup_resource(arg0))->Add((uint32_t(arg1))); } extern "C" int32_t fooX3AfooX2FresourcesX00create() { auto result0 = foo::foo::resources::Create(); - abort(); + return result0.release()->get_handle(); +// return result0->get_handle(); +// abort(); // return result0.store_resource(std::move(result0)); } extern "C" void fooX3AfooX2FresourcesX00borrows(int32_t arg0) { - foo::foo::resources::Borrows(*foo::foo::resources::R::lookup_resource(arg0)); + foo::foo::resources::Borrows(**foo::foo::resources::R::lookup_resource(arg0)); } extern "C" void fooX3AfooX2FresourcesX00consume(int32_t arg0) { - foo::foo::resources::Consume(foo::foo::resources::R::Owned(foo::foo::resources::R::lookup_resource(arg0))); + auto objptr = foo::foo::resources::R::remove_resource(arg0); + assert(objptr.has_value()); + foo::foo::resources::Consume(foo::foo::resources::R::Owned(*objptr)); +} +extern "C" void fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t idx) { + foo::foo::resources::R::remove_resource(idx); +} +extern "C" int32_t X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t* rep) { + return exports::foo::foo::resources::R::store_resource(std::move(rep)); +} +extern "C" void X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t idx) { + exports::foo::foo::resources::R::remove_resource(idx); } -extern "C" void fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t) {abort();} -extern "C" int32_t X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t*) {abort();} -extern "C" void X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t) {abort();} exports::foo::foo::resources::R::~R() { if (this->rep) @@ -72,12 +82,10 @@ void exports::foo::foo::resources::Borrows(std::reference_wrapper o) { fooX3AfooX2FresourcesX23borrows(o.get().get_rep()); } void exports::foo::foo::resources::Consume(R &&o) { - auto rep = o.get_rep(); - abort(); -// R::remove_resource(o.get_handle()); + auto rep = o.take_rep(); + R::remove_resource(o.get_handle()); fooX3AfooX2FresourcesX23consume(rep); } // Component Adapters -exports::foo::foo::resources::R::R(wit::ResourceExportBase&&) -{ abort(); } \ No newline at end of file +exports::foo::foo::resources::R::R(wit::ResourceExportBase&& b) : wit::ResourceExportBase(std::move(b)) {} From 2910840158d90deb631cf5e82525cd528f0345bb Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 17 Mar 2024 11:21:12 +0100 Subject: [PATCH 135/672] remove dead code --- crates/cpp/tests/native_resources/the_world_cpp_native.h | 2 +- crates/cpp/tests/native_resources/the_world_native.cpp | 9 --------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/crates/cpp/tests/native_resources/the_world_cpp_native.h b/crates/cpp/tests/native_resources/the_world_cpp_native.h index 05ef0121e..c638e38e5 100644 --- a/crates/cpp/tests/native_resources/the_world_cpp_native.h +++ b/crates/cpp/tests/native_resources/the_world_cpp_native.h @@ -25,7 +25,7 @@ namespace exports { namespace foo { namespace foo { namespace resources { -// only a proxy, no data, an import? +// only a handle, no data, imported from guest class R : public wit::ResourceExportBase { public: diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index 453c98d6b..e0012cf70 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -22,15 +22,9 @@ extern "C" __attribute__((import_module("foo:foo/resources"))) __attribute__((import_name("consume"))) void fooX3AfooX2FresourcesX23consume(uint8_t*); -#if 0 -foo::foo::resources::R::~R() { - foo::foo::resources::R::remove_resource((*this)); -} -#endif extern "C" int32_t fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t arg0) { auto result0 = foo::foo::resources::R::New((uint32_t(arg0))); return result0.release()->get_handle(); -// this->handle = result0.store_resource(std::move(result0)); } extern "C" void fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(int32_t arg0, int32_t arg1) { @@ -39,9 +33,6 @@ extern "C" void fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(int32_t arg0, extern "C" int32_t fooX3AfooX2FresourcesX00create() { auto result0 = foo::foo::resources::Create(); return result0.release()->get_handle(); -// return result0->get_handle(); -// abort(); -// return result0.store_resource(std::move(result0)); } extern "C" void fooX3AfooX2FresourcesX00borrows(int32_t arg0) { foo::foo::resources::Borrows(**foo::foo::resources::R::lookup_resource(arg0)); From 062a23c6248944922a962473369b5a6f8bbbd357 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 17 Mar 2024 11:52:33 +0100 Subject: [PATCH 136/672] fix another nasty auto type conversion --- crates/cpp/tests/native_resources/guest/the_world.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/cpp/tests/native_resources/guest/the_world.cpp b/crates/cpp/tests/native_resources/guest/the_world.cpp index 7cbb91f73..39297d50c 100644 --- a/crates/cpp/tests/native_resources/guest/the_world.cpp +++ b/crates/cpp/tests/native_resources/guest/the_world.cpp @@ -55,7 +55,7 @@ void foo::foo::resources::R::Add(uint32_t b) const { } foo::foo::resources::R foo::foo::resources::Create() { auto ret = fooX3AfooX2FresourcesX00create(); - return ret; + return wit::ResourceImportBase{ret}; } void foo::foo::resources::Borrows(std::reference_wrapper o) { fooX3AfooX2FresourcesX00borrows(o.get().get_handle()); @@ -72,7 +72,7 @@ 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->into_handle(); + return result0.release()->into_handle(); } extern "C" __attribute__((__export_name__("foo:foo/resources#[method]r.add"))) void @@ -103,3 +103,4 @@ fooX3AfooX2FresourcesX23consume(int8_t* arg0) { } // Component Adapters +foo::foo::resources::R::R(wit::ResourceImportBase&&b) : wit::ResourceImportBase(std::move(b)) {} From 52e1b3e5d7ad378d1fc2f457583d988a43d4ec97 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 17 Mar 2024 12:05:07 +0100 Subject: [PATCH 137/672] leak fixed --- crates/cpp/tests/native_resources/the_world_native.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index e0012cf70..f79510673 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -43,7 +43,9 @@ extern "C" void fooX3AfooX2FresourcesX00consume(int32_t arg0) { foo::foo::resources::Consume(foo::foo::resources::R::Owned(*objptr)); } extern "C" void fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t idx) { - foo::foo::resources::R::remove_resource(idx); + auto ptr = foo::foo::resources::R::remove_resource(idx); + assert(ptr.has_value()); + foo::foo::resources::R::Dtor(*ptr); } extern "C" int32_t X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t* rep) { return exports::foo::foo::resources::R::store_resource(std::move(rep)); From 0f1dee6962f39fc2ed2f5ac1935a8a7af5b548ef Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 17 Mar 2024 12:16:41 +0100 Subject: [PATCH 138/672] header simplification --- crates/cpp/tests/native_resources/guest/the_world_cpp.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cpp/tests/native_resources/guest/the_world_cpp.h b/crates/cpp/tests/native_resources/guest/the_world_cpp.h index 2cd72ee9f..c1adf2ae4 100644 --- a/crates/cpp/tests/native_resources/guest/the_world_cpp.h +++ b/crates/cpp/tests/native_resources/guest/the_world_cpp.h @@ -33,9 +33,9 @@ namespace exports { namespace foo { namespace foo { namespace resources { -std::unique_ptr Create(); +R::Owned Create(); void Borrows(std::reference_wrapper o); -void Consume(std::unique_ptr o); +void Consume(R::Owned o); } // namespace resources } // namespace foo } // namespace foo From 132f25b74d4e031838feae9e17cdadae20e048e9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 18 Mar 2024 22:07:47 +0100 Subject: [PATCH 139/672] correct as of communication at https://bytecodealliance.zulipchat.com/#narrow/stream/327223-wit-bindgen/topic/Native.20plugins.20defined.20in.20WIT/near/427499641 --- crates/cpp/tests/native_resources/rust/src/the_world.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/cpp/tests/native_resources/rust/src/the_world.rs b/crates/cpp/tests/native_resources/rust/src/the_world.rs index cd96842ad..3ce45e9dc 100644 --- a/crates/cpp/tests/native_resources/rust/src/the_world.rs +++ b/crates/cpp/tests/native_resources/rust/src/the_world.rs @@ -294,14 +294,14 @@ pub mod exports { } #[doc(hidden)] #[allow(non_snake_case)] -pub unsafe fn _export_consume_cabi(arg0: *mut u8,) {#[cfg(target_arch="wasm32")] -_rt::run_ctors_once();T::consume(_rt::Box::<_RRep>::from_raw(arg0.cast()).unwrap()); +pub unsafe fn _export_consume_cabi(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: Self::R,); + fn consume(o: R,); } pub trait GuestR: 'static { @@ -366,7 +366,7 @@ macro_rules! __export_foo_foo_resources_cabi{ } #[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: *mut u8,) { + unsafe extern "C" fn fooX3AfooX2FresourcesX23consume(arg0: i32,) { $($path_to_types)*::_export_consume_cabi::<$ty>(arg0) } From 611fb7b93766533cbb4dd58050fa332c8dba3d20 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 18 Mar 2024 22:35:25 +0100 Subject: [PATCH 140/672] fix the C++ implementation to match the explanation by Alex --- .../guest/exports-foo-foo-resources-R.h | 1 + .../cpp/tests/native_resources/guest/the_world.cpp | 12 +++++++++--- crates/cpp/tests/native_resources/rust/src/lib.rs | 4 ++-- .../cpp/tests/native_resources/the_world_native.cpp | 9 ++++++--- 4 files changed, 18 insertions(+), 8 deletions(-) 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 index 6c7596402..b3b33c423 100644 --- 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 @@ -21,6 +21,7 @@ class R : public wit::ResourceExportBase { void Add(uint32_t b) { value += b; } static int32_t ResourceNew(R *self); static void ResourceDrop(int32_t id); + static R* ResourceRep(int32_t id); uint32_t GetValue() const { return value; } }; diff --git a/crates/cpp/tests/native_resources/guest/the_world.cpp b/crates/cpp/tests/native_resources/guest/the_world.cpp index 39297d50c..e474fec93 100644 --- a/crates/cpp/tests/native_resources/guest/the_world.cpp +++ b/crates/cpp/tests/native_resources/guest/the_world.cpp @@ -40,6 +40,9 @@ X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t *); extern "C" __attribute__((import_module("[export]foo:foo/resources"))) __attribute__((import_name("[resource-drop]r"))) void X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t); +extern "C" __attribute__((import_module("[export]foo:foo/resources"))) +__attribute__((import_name("[resource-rep]r"))) uint8_t* + X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(int32_t); foo::foo::resources::R::~R() { if (handle >= 0) { fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(handle); @@ -86,6 +89,9 @@ int32_t exports::foo::foo::resources::R::ResourceNew(R *self) { void exports::foo::foo::resources::R::ResourceDrop(int32_t id) { X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(id); } +exports::foo::foo::resources::R* exports::foo::foo::resources::R::ResourceRep(int32_t id) { + return (exports::foo::foo::resources::R*)X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(id); +} extern "C" __attribute__((__export_name__("foo:foo/resources#create"))) int32_t fooX3AfooX2FresourcesX23create() { auto result0 = exports::foo::foo::resources::Create(); @@ -96,9 +102,9 @@ fooX3AfooX2FresourcesX23borrows(int8_t* arg0) { exports::foo::foo::resources::Borrows(std::cref(*(exports::foo::foo::resources::R const*)arg0)); } extern "C" __attribute__((__export_name__("foo:foo/resources#consume"))) void -fooX3AfooX2FresourcesX23consume(int8_t* arg0) { - auto obj = exports::foo::foo::resources::R::Owned((exports::foo::foo::resources::R*)arg0); - obj->into_handle(); +fooX3AfooX2FresourcesX23consume(int32_t arg0) { + auto obj = exports::foo::foo::resources::R::Owned(exports::foo::foo::resources::R::ResourceRep(arg0)); + //obj->into_handle(); exports::foo::foo::resources::Consume(std::move(obj)); } diff --git a/crates/cpp/tests/native_resources/rust/src/lib.rs b/crates/cpp/tests/native_resources/rust/src/lib.rs index ea5425a4e..754aa6986 100644 --- a/crates/cpp/tests/native_resources/rust/src/lib.rs +++ b/crates/cpp/tests/native_resources/rust/src/lib.rs @@ -28,8 +28,8 @@ impl Guest for MyWorld { fn borrows(o: RBorrow<'_>) { println!("resource borrowed with {:?}", o.get::().0.lock().unwrap()); } - fn consume(o: Self::R) { - println!("resource consumed with {:?}", o.0.lock().unwrap()); + fn consume(o: resources::R) { + println!("resource consumed with {:?}", o.get::().0.lock().unwrap()); println!("exercise the other direction"); let obj = the_world::foo::foo::resources::create(); diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index f79510673..cff95031a 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -20,7 +20,7 @@ __attribute__((import_name("borrows"))) void fooX3AfooX2FresourcesX23borrows(uint8_t*); extern "C" __attribute__((import_module("foo:foo/resources"))) __attribute__((import_name("consume"))) void - fooX3AfooX2FresourcesX23consume(uint8_t*); + fooX3AfooX2FresourcesX23consume(int32_t); extern "C" int32_t fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t arg0) { auto result0 = foo::foo::resources::R::New((uint32_t(arg0))); @@ -53,6 +53,9 @@ extern "C" int32_t X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8 extern "C" void X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t idx) { exports::foo::foo::resources::R::remove_resource(idx); } +extern "C" uint8_t* X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(int32_t idx) { + return *exports::foo::foo::resources::R::lookup_resource(idx); +} exports::foo::foo::resources::R::~R() { if (this->rep) @@ -76,8 +79,8 @@ void exports::foo::foo::resources::Borrows(std::reference_wrapper o) { } void exports::foo::foo::resources::Consume(R &&o) { auto rep = o.take_rep(); - R::remove_resource(o.get_handle()); - fooX3AfooX2FresourcesX23consume(rep); + //R::remove_resource(o.get_handle()); + fooX3AfooX2FresourcesX23consume(o.get_handle()); } // Component Adapters From 68ad2ca6c0e6c2e0080628d5a518f5ed5b368544 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 22 Mar 2024 09:33:20 +0100 Subject: [PATCH 141/672] consistently use the new parser crate --- Cargo.lock | 75 +++++++++++++++++++----------------- Cargo.toml | 7 +++- crates/c/src/lib.rs | 12 +++--- crates/core/src/abi.rs | 24 ++++++------ crates/cpp/src/lib.rs | 4 +- crates/cpp/src/wamr.rs | 8 ++-- crates/csharp/src/lib.rs | 12 +++--- crates/go/src/interface.rs | 8 ++-- crates/go/src/lib.rs | 4 +- crates/markdown/src/lib.rs | 4 +- crates/rust/src/interface.rs | 4 +- crates/teavm-java/src/lib.rs | 16 ++++---- 12 files changed, 93 insertions(+), 85 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b6e8e85c..67c29070e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -466,7 +466,7 @@ dependencies = [ "itertools", "log", "smallvec", - "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser", "wasmtime-types", ] @@ -1673,8 +1673,7 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-encoder" version = "0.201.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" +source = "git+https://github.com/bytecodealliance/wasm-tools#ac798ee10168d2278c03ae928ec9f747b3335c00" dependencies = [ "leb128", ] @@ -1682,8 +1681,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.201.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2" +source = "git+https://github.com/bytecodealliance/wasm-tools#ac798ee10168d2278c03ae928ec9f747b3335c00" dependencies = [ "anyhow", "indexmap", @@ -1692,18 +1690,7 @@ dependencies = [ "serde_json", "spdx", "wasm-encoder", - "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wasmparser" -version = "0.201.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708" -dependencies = [ - "bitflags 2.5.0", - "indexmap", - "semver", + "wasmparser", ] [[package]] @@ -1719,11 +1706,10 @@ dependencies = [ [[package]] name = "wasmprinter" version = "0.201.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a67e66da702706ba08729a78e3c0079085f6bfcb1a62e4799e97bbf728c2c265" +source = "git+https://github.com/bytecodealliance/wasm-tools#ac798ee10168d2278c03ae928ec9f747b3335c00" dependencies = [ "anyhow", - "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser", ] [[package]] @@ -1756,7 +1742,7 @@ dependencies = [ "serde_json", "target-lexicon", "wasm-encoder", - "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser", "wasmtime-cache", "wasmtime-component-macro", "wasmtime-component-util", @@ -1768,7 +1754,7 @@ dependencies = [ "wasmtime-runtime", "wasmtime-slab", "wasmtime-winch", - "wat", + "wat 1.201.0 (registry+https://github.com/rust-lang/crates.io-index)", "windows-sys 0.52.0", ] @@ -1841,7 +1827,7 @@ dependencies = [ "object", "target-lexicon", "thiserror", - "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser", "wasmtime-cranelift-shared", "wasmtime-environ", "wasmtime-versioned-export-macros", @@ -1883,7 +1869,7 @@ dependencies = [ "target-lexicon", "thiserror", "wasm-encoder", - "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser", "wasmprinter", "wasmtime-component-util", "wasmtime-types", @@ -1973,7 +1959,7 @@ dependencies = [ "serde", "serde_derive", "thiserror", - "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser", ] [[package]] @@ -2029,7 +2015,7 @@ dependencies = [ "gimli", "object", "target-lexicon", - "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser", "wasmtime-cranelift-shared", "wasmtime-environ", "winch-codegen", @@ -2075,13 +2061,33 @@ dependencies = [ "wasm-encoder", ] +[[package]] +name = "wast" +version = "201.0.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#ac798ee10168d2278c03ae928ec9f747b3335c00" +dependencies = [ + "bumpalo", + "leb128", + "memchr", + "unicode-width", + "wasm-encoder", +] + [[package]] name = "wat" version = "1.201.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "453d5b37a45b98dee4f4cb68015fc73634d7883bbef1c65e6e9c78d454cf3f32" dependencies = [ - "wast 201.0.0", + "wast 201.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wat" +version = "1.201.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#ac798ee10168d2278c03ae928ec9f747b3335c00" +dependencies = [ + "wast 201.0.0 (git+https://github.com/bytecodealliance/wasm-tools)", ] [[package]] @@ -2160,7 +2166,7 @@ dependencies = [ "regalloc2", "smallvec", "target-lexicon", - "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser", "wasmtime-environ", ] @@ -2356,7 +2362,7 @@ dependencies = [ "heck 0.4.1", "test-artifacts", "wasm-encoder", - "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser", "wasmtime", "wasmtime-wasi", "wit-bindgen-c", @@ -2404,7 +2410,7 @@ dependencies = [ "test-helpers", "wasm-encoder", "wasm-metadata", - "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser", "wit-bindgen-core", "wit-component", ] @@ -2485,8 +2491,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.201.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825" +source = "git+https://github.com/bytecodealliance/wasm-tools#ac798ee10168d2278c03ae928ec9f747b3335c00" dependencies = [ "anyhow", "bitflags 2.5.0", @@ -2497,8 +2502,8 @@ dependencies = [ "serde_json", "wasm-encoder", "wasm-metadata", - "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wat", + "wasmparser", + "wat 1.201.0 (git+https://github.com/bytecodealliance/wasm-tools)", "wit-parser", ] @@ -2516,7 +2521,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.201.0 (git+https://github.com/bytecodealliance/wasm-tools)", + "wasmparser", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 92140cf4d..4f3e623b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -92,5 +92,8 @@ wasm-encoder = { workspace = true } [patch.crates-io] wit-parser = { git = "https://github.com/bytecodealliance/wasm-tools" } -#wit-parser = { git = "https://github.com/cpetig/wasm-tools" } -#wit-parser = { path = "/home/christof/src/wasm-tools/crates/wit-parser" } +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" } diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index c5a7535bb..1fcaeb49c 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -560,8 +560,8 @@ impl C { Type::S32 => dst.push_str("int32_t"), Type::U64 => dst.push_str("uint64_t"), Type::S64 => dst.push_str("int64_t"), - Type::Float32 => dst.push_str("float"), - Type::Float64 => dst.push_str("double"), + Type::F32 => dst.push_str("float"), + Type::F64 => dst.push_str("double"), Type::String => { dst.push_str(&self.world.to_snake_case()); dst.push_str("_"); @@ -731,8 +731,8 @@ pub fn push_ty_name(resolve: &Resolve, ty: &Type, src: &mut String) { Type::S32 => src.push_str("s32"), Type::U64 => src.push_str("u64"), Type::S64 => src.push_str("s64"), - Type::Float32 => src.push_str("float32"), - Type::Float64 => src.push_str("float64"), + Type::F32 => src.push_str("float32"), + Type::F64 => src.push_str("float64"), Type::String => src.push_str("string"), Type::Id(id) => { let ty = &resolve.types[*id]; @@ -1617,8 +1617,8 @@ impl InterfaceGenerator<'_> { | Type::S32 | Type::U64 | Type::S64 - | Type::Float32 - | Type::Float64 + | Type::F32 + | Type::F64 | Type::Char => {} } } diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index d20c89d10..684d10e8f 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -747,8 +747,8 @@ fn needs_post_return(resolve: &Resolve, ty: &Type) -> bool { | Type::S32 | Type::U64 | Type::S64 - | Type::Float32 - | Type::Float64 + | Type::F32 + | Type::F64 | Type::Char => false, } } @@ -1067,8 +1067,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { Type::S64 => self.emit(&I64FromS64), Type::U64 => self.emit(&I64FromU64), Type::Char => self.emit(&I32FromChar), - Type::Float32 => self.emit(&F32FromFloat32), - Type::Float64 => self.emit(&F64FromFloat64), + Type::F32 => self.emit(&F32FromFloat32), + Type::F64 => self.emit(&F64FromFloat64), Type::String => { let realloc = self.list_realloc(); self.emit(&StringLower { realloc }); @@ -1256,8 +1256,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { Type::S64 => self.emit(&S64FromI64), Type::U64 => self.emit(&U64FromI64), Type::Char => self.emit(&CharFromI32), - Type::Float32 => self.emit(&Float32FromF32), - Type::Float64 => self.emit(&Float64FromF64), + Type::F32 => self.emit(&Float32FromF32), + Type::F64 => self.emit(&Float64FromF64), Type::String => self.emit(&StringLift), Type::Id(id) => match &self.resolve.types[id].kind { TypeDefKind::Type(t) => self.lift(t), @@ -1414,8 +1414,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.lower_and_emit(ty, addr, &I32Store { offset }) } Type::U64 | Type::S64 => self.lower_and_emit(ty, addr, &I64Store { offset }), - Type::Float32 => self.lower_and_emit(ty, addr, &F32Store { offset }), - Type::Float64 => self.lower_and_emit(ty, addr, &F64Store { offset }), + Type::F32 => self.lower_and_emit(ty, addr, &F32Store { offset }), + Type::F64 => self.lower_and_emit(ty, addr, &F64Store { offset }), Type::String => self.write_list_to_memory(ty, addr, offset), Type::Id(id) => match &self.resolve.types[id].kind { @@ -1604,8 +1604,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { Type::S16 => self.emit_and_lift(ty, addr, &I32Load16S { offset }), Type::U32 | Type::S32 | Type::Char => self.emit_and_lift(ty, addr, &I32Load { offset }), Type::U64 | Type::S64 => self.emit_and_lift(ty, addr, &I64Load { offset }), - Type::Float32 => self.emit_and_lift(ty, addr, &F32Load { offset }), - Type::Float64 => self.emit_and_lift(ty, addr, &F64Load { offset }), + Type::F32 => self.emit_and_lift(ty, addr, &F32Load { offset }), + Type::F64 => self.emit_and_lift(ty, addr, &F64Load { offset }), Type::String => self.read_list_from_memory(ty, addr, offset), Type::Id(id) => match &self.resolve.types[id].kind { @@ -1805,8 +1805,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { | Type::Char | Type::U64 | Type::S64 - | Type::Float32 - | Type::Float64 => {} + | Type::F32 + | Type::F64 => {} Type::Id(id) => match &self.resolve.types[id].kind { TypeDefKind::Type(t) => self.deallocate(t, addr, offset), diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 88b645f28..8edf38e1a 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1350,8 +1350,8 @@ impl CppInterfaceGenerator<'_> { Type::S32 => "int32_t".into(), Type::U64 => "uint64_t".into(), Type::S64 => "int64_t".into(), - Type::Float32 => "float".into(), - Type::Float64 => "double".into(), + Type::F32 => "float".into(), + Type::F64 => "double".into(), Type::String => match flavor { Flavor::Argument(AbiVariant::GuestImport) => { self.gen.dependencies.needs_string_view = true; diff --git a/crates/cpp/src/wamr.rs b/crates/cpp/src/wamr.rs index db36b1e60..bed3faee4 100644 --- a/crates/cpp/src/wamr.rs +++ b/crates/cpp/src/wamr.rs @@ -27,10 +27,10 @@ fn push_wamr(ty: &Type, resolve: &Resolve, params_str: &mut String) { Type::U64 | Type::S64 => { params_str.push('I'); } - Type::Float32 => { + Type::F32 => { params_str.push('f'); } - Type::Float64 => { + Type::F64 => { params_str.push('F'); } Type::String => { @@ -80,10 +80,10 @@ fn wamr_add_result(sig: &mut WamrSig, resolve: &Resolve, ty: &Type) { Type::S64 | Type::U64 => { sig.wamr_result = "I".into(); } - Type::Float32 => { + Type::F32 => { sig.wamr_result = "f".into(); } - Type::Float64 => { + Type::F64 => { sig.wamr_result = "F".into(); } Type::String => { diff --git a/crates/csharp/src/lib.rs b/crates/csharp/src/lib.rs index b2dce297d..5a8e39b94 100644 --- a/crates/csharp/src/lib.rs +++ b/crates/csharp/src/lib.rs @@ -1151,8 +1151,8 @@ impl InterfaceGenerator<'_> { Type::S16 => "short".to_owned(), Type::S32 => "int".to_owned(), Type::S64 => "long".to_owned(), - Type::Float32 => "float".to_owned(), - Type::Float64 => "double".to_owned(), + Type::F32 => "float".to_owned(), + Type::F64 => "double".to_owned(), Type::Char => "uint".to_owned(), Type::String => "string".to_owned(), Type::Id(id) => { @@ -1241,8 +1241,8 @@ impl InterfaceGenerator<'_> { Type::S16 => "short".into(), Type::S32 => "int".into(), Type::S64 => "long".into(), - Type::Float32 => "float".into(), - Type::Float64 => "double".into(), + Type::F32 => "float".into(), + Type::F64 => "double".into(), Type::Char => "uint".into(), Type::Id(id) => { let def = &self.resolve.types[*id]; @@ -2332,8 +2332,8 @@ fn is_primitive(ty: &Type) -> bool { | Type::S32 | Type::U64 | Type::S64 - | Type::Float32 - | Type::Float64 + | Type::F32 + | Type::F64 ) } diff --git a/crates/go/src/interface.rs b/crates/go/src/interface.rs index 9a0bf038c..0ce980d9b 100644 --- a/crates/go/src/interface.rs +++ b/crates/go/src/interface.rs @@ -217,8 +217,8 @@ impl InterfaceGenerator<'_> { Type::S16 => "int16".into(), Type::S32 => "int32".into(), Type::S64 => "int64".into(), - Type::Float32 => "float32".into(), - Type::Float64 => "float64".into(), + Type::F32 => "float32".into(), + Type::F64 => "float64".into(), Type::Char => "rune".into(), Type::String => "string".into(), Type::Id(id) => { @@ -259,8 +259,8 @@ impl InterfaceGenerator<'_> { Type::S16 => "S16".into(), Type::S32 => "S32".into(), Type::S64 => "S64".into(), - Type::Float32 => "F32".into(), - Type::Float64 => "F64".into(), + Type::F32 => "F32".into(), + Type::F64 => "F64".into(), Type::Char => "Byte".into(), Type::String => "String".into(), Type::Id(id) => { diff --git a/crates/go/src/lib.rs b/crates/go/src/lib.rs index c3a6d9048..3beb4dc51 100644 --- a/crates/go/src/lib.rs +++ b/crates/go/src/lib.rs @@ -105,8 +105,8 @@ impl TinyGo { Type::S16 => "int16_t".into(), Type::S32 => "int32_t".into(), Type::S64 => "int64_t".into(), - Type::Float32 => "float".into(), - Type::Float64 => "double".into(), + Type::F32 => "float".into(), + Type::F64 => "double".into(), Type::Char => "uint32_t".into(), Type::String => { format!( diff --git a/crates/markdown/src/lib.rs b/crates/markdown/src/lib.rs index 2709b80bd..7c38afbe2 100644 --- a/crates/markdown/src/lib.rs +++ b/crates/markdown/src/lib.rs @@ -333,8 +333,8 @@ impl InterfaceGenerator<'_> { Type::S32 => self.push_str("`s32`"), Type::U64 => self.push_str("`u64`"), Type::S64 => self.push_str("`s64`"), - Type::Float32 => self.push_str("`float32`"), - Type::Float64 => self.push_str("`float64`"), + Type::F32 => self.push_str("`float32`"), + Type::F64 => self.push_str("`float64`"), Type::Char => self.push_str("`char`"), Type::String => self.push_str("`string`"), Type::Id(id) => { diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index d96ae4e98..13ca4522c 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -1132,8 +1132,8 @@ macro_rules! {macro_name} {{ Type::S16 => self.push_str("i16"), Type::S32 => self.push_str("i32"), Type::S64 => self.push_str("i64"), - Type::Float32 => self.push_str("f32"), - Type::Float64 => self.push_str("f64"), + Type::F32 => self.push_str("f32"), + Type::F64 => self.push_str("f64"), Type::Char => self.push_str("char"), Type::String => { assert_eq!(mode.lists_borrowed, mode.lifetime.is_some()); diff --git a/crates/teavm-java/src/lib.rs b/crates/teavm-java/src/lib.rs index 3a25c513c..efcee4e0b 100644 --- a/crates/teavm-java/src/lib.rs +++ b/crates/teavm-java/src/lib.rs @@ -661,8 +661,8 @@ impl InterfaceGenerator<'_> { Type::U16 | Type::S16 => "short".into(), Type::U32 | Type::S32 | Type::Char => "int".into(), Type::U64 | Type::S64 => "long".into(), - Type::Float32 => "float".into(), - Type::Float64 => "double".into(), + Type::F32 => "float".into(), + Type::F64 => "double".into(), Type::String => "String".into(), Type::Id(id) => { let ty = &self.resolve.types[*id]; @@ -735,8 +735,8 @@ impl InterfaceGenerator<'_> { Type::U16 | Type::S16 => "Short".into(), Type::U32 | Type::S32 | Type::Char => "Integer".into(), Type::U64 | Type::S64 => "Long".into(), - Type::Float32 => "Float".into(), - Type::Float64 => "Double".into(), + Type::F32 => "Float".into(), + Type::F64 => "Double".into(), Type::Id(id) => { let def = &self.resolve.types[*id]; match &def.kind { @@ -2162,8 +2162,8 @@ fn list_element_info(ty: &Type) -> (usize, &'static str) { Type::U16 | Type::S16 => (2, "short"), Type::U32 | Type::S32 => (4, "int"), Type::U64 | Type::S64 => (8, "long"), - Type::Float32 => (4, "float"), - Type::Float64 => (8, "double"), + Type::F32 => (4, "float"), + Type::F64 => (8, "double"), _ => unreachable!(), } } @@ -2207,8 +2207,8 @@ fn is_primitive(ty: &Type) -> bool { | Type::S32 | Type::U64 | Type::S64 - | Type::Float32 - | Type::Float64 + | Type::F32 + | Type::F64 ) } From d9f7c6f5339877c46206a13413f9991d0f04fdcc Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 22 Mar 2024 17:33:31 -0600 Subject: [PATCH 142/672] first draft of async lift/lower for Rust generator Signed-off-by: Joel Dice --- crates/c/src/lib.rs | 6 +- crates/core/src/abi.rs | 274 ++++++++++++++++++++++------- crates/csharp/src/lib.rs | 9 +- crates/guest-rust/macro/src/lib.rs | 65 ++++++- crates/rust/src/async_support.rs | 45 +++++ crates/rust/src/bindgen.rs | 92 +++++++++- crates/rust/src/interface.rs | 124 +++++++++---- crates/rust/src/lib.rs | 57 ++++-- crates/teavm-java/src/lib.rs | 9 +- 9 files changed, 564 insertions(+), 117 deletions(-) create mode 100644 crates/rust/src/async_support.rs diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index e61ae7468..4f402db80 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -1653,6 +1653,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut f, + false, ); let FunctionBindgen { @@ -1725,6 +1726,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut f, + false, ); let FunctionBindgen { src, .. } = f; self.src.c_adapters(&src); @@ -1755,7 +1757,7 @@ impl InterfaceGenerator<'_> { let mut f = FunctionBindgen::new(self, c_sig, &import_name); f.params = params; - abi::post_return(f.gen.resolve, func, &mut f); + abi::post_return(f.gen.resolve, func, &mut f, false); let FunctionBindgen { src, .. } = f; self.src.c_fns(&src); self.src.c_fns("}\n"); @@ -2654,7 +2656,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src.push_str(");\n"); } - Instruction::CallInterface { func } => { + Instruction::CallInterface { func, .. } => { let mut args = String::new(); for (i, (op, (byref, _))) in operands.iter().zip(&self.sig.params).enumerate() { if i > 0 { diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index ef4e5e10e..5d30256c5 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -470,7 +470,8 @@ def_instruction! { /// Note that this will be used for async functions. CallInterface { func: &'a Function, - } : [func.params.len()] => [func.results.len()], + async_: bool, + } : [func.params.len()] => [if *async_ { 1 } else { func.results.len() }], /// Returns `amt` values on the stack. This is always the last /// instruction. @@ -519,6 +520,20 @@ def_instruction! { GuestDeallocateVariant { blocks: usize, } : [1] => [0], + + AsyncMalloc { size: usize, align: usize } : [0] => [1], + + AsyncCallWasm { name: &'a str, size: usize, align: usize } : [3] => [0], + + AsyncCallStart { + name: &'a str, + params: &'a [WasmType], + results: &'a [WasmType] + } : [params.len()] => [results.len()], + + AsyncPostCallInterface { func: &'a Function } : [1] => [func.results.len()], + + AsyncCallReturn { name: &'a str, params: &'a [WasmType] } : [params.len()] => [0], } } @@ -683,8 +698,9 @@ pub fn call( lift_lower: LiftLower, func: &Function, bindgen: &mut impl Bindgen, + async_: bool, ) { - Generator::new(resolve, variant, lift_lower, bindgen).call(func); + Generator::new(resolve, variant, lift_lower, bindgen, async_).call(func); } /// Used in a similar manner as the `Interface::call` function except is @@ -693,12 +709,13 @@ pub fn call( /// This is only intended to be used in guest generators for exported /// functions and will primarily generate `GuestDeallocate*` instructions, /// plus others used as input to those instructions. -pub fn post_return(resolve: &Resolve, func: &Function, bindgen: &mut impl Bindgen) { +pub fn post_return(resolve: &Resolve, func: &Function, bindgen: &mut impl Bindgen, async_: bool) { Generator::new( resolve, AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, bindgen, + async_, ) .post_return(func); } @@ -757,6 +774,7 @@ struct Generator<'a, B: Bindgen> { variant: AbiVariant, lift_lower: LiftLower, bindgen: &'a mut B, + async_: bool, resolve: &'a Resolve, operands: Vec, results: Vec, @@ -770,12 +788,14 @@ impl<'a, B: Bindgen> Generator<'a, B> { variant: AbiVariant, lift_lower: LiftLower, bindgen: &'a mut B, + async_: bool, ) -> Generator<'a, B> { Generator { resolve, variant, lift_lower, bindgen, + async_, operands: Vec::new(), results: Vec::new(), stack: Vec::new(), @@ -784,70 +804,112 @@ impl<'a, B: Bindgen> Generator<'a, B> { } fn call(&mut self, func: &Function) { + const MAX_FLAT_PARAMS: usize = 16; + const MAX_FLAT_RESULTS: usize = 1; + let sig = self.resolve.wasm_signature(self.variant, func); match self.lift_lower { LiftLower::LowerArgsLiftResults => { - if !sig.indirect_params { - // If the parameters for this function aren't indirect - // (there aren't too many) then we simply do a normal lower - // operation for them all. + if let (AbiVariant::GuestExport, true) = (self.variant, self.async_) { + todo!("implement host-side support for async lift/lower"); + } + + let lower_to_memory = |self_: &mut Self, ptr: B::Operand| { + let mut offset = 0usize; for (nth, (_, ty)) in func.params.iter().enumerate() { - self.emit(&Instruction::GetArg { nth }); - self.lower(ty); + self_.emit(&Instruction::GetArg { nth }); + offset = align_to(offset, self_.bindgen.sizes().align(ty)); + self_.write_to_memory(ty, ptr.clone(), offset as i32); + offset += self_.bindgen.sizes().size(ty); } - } else { - // ... otherwise if parameters are indirect space is - // allocated from them and each argument is lowered - // individually into memory. + + self_.stack.push(ptr); + }; + + let params_size_align = if self.async_ { let (size, align) = self .bindgen .sizes() - .record(func.params.iter().map(|t| &t.1)); - let ptr = match self.variant { - // When a wasm module calls an import it will provide - // space that isn't explicitly deallocated. - AbiVariant::GuestImport => self.bindgen.return_pointer(size, align), - // When calling a wasm module from the outside, though, - // malloc needs to be called. - AbiVariant::GuestExport => { - self.emit(&Instruction::Malloc { - realloc: "cabi_realloc", - size, - align, - }); - self.stack.pop().unwrap() + .record(func.params.iter().map(|(_, ty)| ty)); + self.emit(&Instruction::AsyncMalloc { size, align }); + let ptr = self.stack.pop().unwrap(); + lower_to_memory(self, ptr); + Some((size, align)) + } else { + if !sig.indirect_params { + // If the parameters for this function aren't indirect + // (there aren't too many) then we simply do a normal lower + // operation for them all. + for (nth, (_, ty)) in func.params.iter().enumerate() { + self.emit(&Instruction::GetArg { nth }); + self.lower(ty); } - }; - let mut offset = 0usize; - for (nth, (_, ty)) in func.params.iter().enumerate() { - self.emit(&Instruction::GetArg { nth }); - offset = align_to(offset, self.bindgen.sizes().align(ty)); - self.write_to_memory(ty, ptr.clone(), offset as i32); - offset += self.bindgen.sizes().size(ty); + } else { + // ... otherwise if parameters are indirect space is + // allocated from them and each argument is lowered + // individually into memory. + let (size, align) = self + .bindgen + .sizes() + .record(func.params.iter().map(|t| &t.1)); + let ptr = match self.variant { + // When a wasm module calls an import it will provide + // space that isn't explicitly deallocated. + AbiVariant::GuestImport => self.bindgen.return_pointer(size, align), + // When calling a wasm module from the outside, though, + // malloc needs to be called. + AbiVariant::GuestExport => { + self.emit(&Instruction::Malloc { + realloc: "cabi_realloc", + size, + align, + }); + self.stack.pop().unwrap() + } + }; + lower_to_memory(self, ptr); } - - self.stack.push(ptr); - } + None + }; // If necessary we may need to prepare a return pointer for // this ABI. - if self.variant == AbiVariant::GuestImport && sig.retptr { - let (size, align) = self.bindgen.sizes().params(func.results.iter_types()); - let ptr = self.bindgen.return_pointer(size, align); + let dealloc_size_align = if let Some((params_size, params_align)) = + params_size_align + { + let (size, align) = self.bindgen.sizes().record(func.results.iter_types()); + self.emit(&Instruction::AsyncMalloc { size, align }); + let ptr = self.stack.pop().unwrap(); self.return_pointer = Some(ptr.clone()); self.stack.push(ptr); - } + // ... and another return pointer for the call handle + self.stack.push(self.bindgen.return_pointer(4, 4)); + + assert_eq!(self.stack.len(), 3); + self.emit(&Instruction::AsyncCallWasm { + name: &func.name, + size: params_size, + align: params_align, + }); + Some((size, align)) + } else { + if self.variant == AbiVariant::GuestImport && sig.retptr { + let (size, align) = self.bindgen.sizes().params(func.results.iter_types()); + let ptr = self.bindgen.return_pointer(size, align); + self.return_pointer = Some(ptr.clone()); + self.stack.push(ptr); + } - // Now that all the wasm args are prepared we can call the - // actual wasm function. - assert_eq!(self.stack.len(), sig.params.len()); - self.emit(&Instruction::CallWasm { - name: &func.name, - sig: &sig, - }); + assert_eq!(self.stack.len(), sig.params.len()); + self.emit(&Instruction::CallWasm { + name: &func.name, + sig: &sig, + }); + None + }; - if !sig.retptr { + 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. @@ -872,7 +934,12 @@ impl<'a, B: Bindgen> Generator<'a, B> { AbiVariant::GuestExport => self.stack.pop().unwrap(), }; - self.read_results_from_memory(&func.results, ptr, 0); + self.read_results_from_memory(&func.results, ptr.clone(), 0); + + if let Some((size, align)) = dealloc_size_align { + self.stack.push(ptr); + self.emit(&Instruction::GuestDeallocate { size, align }); + } } self.emit(&Instruction::Return { @@ -881,7 +948,53 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); } LiftLower::LiftArgsLowerResults => { - if !sig.indirect_params { + if let (AbiVariant::GuestImport, true) = (self.variant, self.async_) { + todo!("implement host-side support for async lift/lower"); + } + + let read_from_memory = |self_: &mut Self| { + let mut offset = 0usize; + let ptr = self_.stack.pop().unwrap(); + for (_, ty) in func.params.iter() { + offset = align_to(offset, self_.bindgen.sizes().align(ty)); + self_.read_from_memory(ty, ptr.clone(), offset as i32); + offset += self_.bindgen.sizes().size(ty); + } + }; + + if self.async_ { + let mut params = Vec::new(); + for (_, ty) in func.params.iter() { + self.resolve.push_flat(ty, &mut params); + } + + let name = &format!("[start]{}", func.name); + + if params.len() > MAX_FLAT_RESULTS { + let (size, align) = self + .bindgen + .sizes() + .params(func.params.iter().map(|(_, ty)| ty)); + let ptr = self.bindgen.return_pointer(size, align); + self.stack.push(ptr.clone()); + self.emit(&Instruction::AsyncCallStart { + name, + params: &[WasmType::Pointer], + results: &[], + }); + self.stack.push(ptr); + read_from_memory(self); + } else { + self.emit(&Instruction::AsyncCallStart { + name, + params: &[], + results: ¶ms, + }); + for (_, ty) in func.params.iter() { + self.lift(ty); + } + } + } else if !sig.indirect_params { // If parameters are not passed indirectly then we lift each // argument in succession from the component wasm types that // make-up the type. @@ -900,23 +1013,33 @@ impl<'a, B: Bindgen> Generator<'a, B> { // ... otherwise argument is read in succession from memory // where the pointer to the arguments is the first argument // to the function. - let mut offset = 0usize; self.emit(&Instruction::GetArg { nth: 0 }); - let ptr = self.stack.pop().unwrap(); - for (_, ty) in func.params.iter() { - offset = align_to(offset, self.bindgen.sizes().align(ty)); - self.read_from_memory(ty, ptr.clone(), offset as i32); - offset += self.bindgen.sizes().size(ty); - } + read_from_memory(self); } // ... and that allows us to call the interface types function - self.emit(&Instruction::CallInterface { func }); + self.emit(&Instruction::CallInterface { + func, + async_: self.async_, + }); + + let (lower_to_memory, async_results) = if self.async_ { + self.emit(&Instruction::AsyncPostCallInterface { func }); + + let mut results = Vec::new(); + for ty in func.results.iter_types() { + self.resolve.push_flat(ty, &mut results); + } + (results.len() > MAX_FLAT_PARAMS, Some(results)) + } else { + (sig.retptr, None) + }; - // This was dynamically allocated by the caller so after - // it's been read by the guest we need to deallocate it. + // This was dynamically allocated by the caller (or async start + // function) so after it's been read by the guest we need to + // deallocate it. if let AbiVariant::GuestExport = self.variant { - if sig.indirect_params { + if sig.indirect_params && !self.async_ { let (size, align) = self .bindgen .sizes() @@ -926,7 +1049,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - if !sig.retptr { + if !lower_to_memory { // With no return pointer in use we simply lower the // result(s) and return that directly from the function. let results = self @@ -968,10 +1091,27 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - self.emit(&Instruction::Return { - func, - amt: sig.results.len(), - }); + if let Some(results) = async_results { + let name = &format!("[return]{}", func.name); + + self.emit(&Instruction::AsyncCallReturn { + name, + params: &if results.len() > MAX_FLAT_PARAMS { + vec![WasmType::Pointer] + } else { + results + }, + }); + self.emit(&Instruction::ConstZero { + tys: &[WasmType::Pointer], + }); + self.emit(&Instruction::Return { func, amt: 1 }); + } else { + self.emit(&Instruction::Return { + func, + amt: sig.results.len(), + }); + } } } diff --git a/crates/csharp/src/lib.rs b/crates/csharp/src/lib.rs index b2dce297d..bfaf06476 100644 --- a/crates/csharp/src/lib.rs +++ b/crates/csharp/src/lib.rs @@ -968,6 +968,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut bindgen, + false, ); let src = bindgen.src; @@ -1024,6 +1025,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut bindgen, + false, ); assert!(!bindgen.needs_cleanup_list); @@ -2061,7 +2063,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { ); } - Instruction::CallInterface { func } => { + Instruction::CallInterface { func, .. } => { let module = self.gen.name.to_string(); let func_name = self.func_name.to_upper_camel_case(); let interface_name = CSharp::get_class_name_from_qualified_name(module).1; @@ -2140,6 +2142,11 @@ impl Bindgen for FunctionBindgen<'_, '_> { name: _, ty: _dir, } => todo!("HandleLeft"), + + Instruction::AsyncMalloc { .. } + | Instruction::AsyncCallStart { .. } + | Instruction::AsyncPostCallInterface { .. } + | Instruction::AsyncCallReturn { .. } => todo!(), } } diff --git a/crates/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs index e7636a751..74b2c2ad6 100644 --- a/crates/guest-rust/macro/src/lib.rs +++ b/crates/guest-rust/macro/src/lib.rs @@ -7,7 +7,7 @@ use syn::parse::{Error, Parse, ParseStream, Result}; use syn::punctuated::Punctuated; use syn::{braced, token, Token}; use wit_bindgen_core::wit_parser::{PackageId, Resolve, UnresolvedPackage, WorldId}; -use wit_bindgen_rust::{Opts, Ownership}; +use wit_bindgen_rust::{AsyncConfig, Opts, Ownership}; #[proc_macro] pub fn generate(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -46,6 +46,7 @@ impl Parse for Config { let mut opts = Opts::default(); let mut world = None; let mut source = None; + let mut async_configured = false; if input.peek(token::Brace) { let content; @@ -113,6 +114,13 @@ impl Parse for Config { Opt::PubExportMacro(enable) => { opts.pub_export_macro = enable.value(); } + Opt::Async(val, span) => { + if async_configured { + return Err(Error::new(span, "cannot specify second async config")); + } + async_configured = true; + opts.async_ = val; + } } } } else { @@ -231,6 +239,7 @@ mod kw { syn::custom_keyword!(default_bindings_module); syn::custom_keyword!(export_macro_name); syn::custom_keyword!(pub_export_macro); + syn::custom_keyword!(imports); } #[derive(Clone)] @@ -260,6 +269,11 @@ impl From for wit_bindgen_rust::ExportKey { } } +enum AsyncConfigSomeKind { + Imports, + Exports, +} + enum Opt { World(syn::LitStr), Path(syn::LitStr), @@ -280,6 +294,7 @@ enum Opt { DefaultBindingsModule(syn::LitStr), ExportMacroName(syn::LitStr), PubExportMacro(syn::LitBool), + Async(AsyncConfig, Span), } impl Parse for Opt { @@ -398,6 +413,30 @@ impl Parse for Opt { input.parse::()?; input.parse::()?; Ok(Opt::PubExportMacro(input.parse()?)) + } else if l.peek(Token![async]) { + let span = input.parse::()?.span; + input.parse::()?; + if input.peek(syn::LitBool) { + if input.parse::()?.value { + Ok(Opt::Async(AsyncConfig::All, span)) + } else { + Ok(Opt::Async(AsyncConfig::None, span)) + } + } else { + let mut imports = Vec::new(); + let mut exports = Vec::new(); + let contents; + syn::braced!(contents in input); + for (kind, values) in + contents.parse_terminated(parse_async_some_field, Token![,])? + { + match kind { + AsyncConfigSomeKind::Imports => imports = values, + AsyncConfigSomeKind::Exports => exports = values, + } + } + Ok(Opt::Async(AsyncConfig::Some { imports, exports }, span)) + } } else { Err(l.error()) } @@ -446,3 +485,27 @@ fn with_field_parse(input: ParseStream<'_>) -> Result<(String, String)> { Ok((interface, buf)) } + +fn parse_async_some_field(input: ParseStream<'_>) -> Result<(AsyncConfigSomeKind, Vec)> { + let lookahead = input.lookahead1(); + let kind = if lookahead.peek(kw::imports) { + input.parse::()?; + input.parse::()?; + AsyncConfigSomeKind::Imports + } else if lookahead.peek(kw::exports) { + input.parse::()?; + input.parse::()?; + AsyncConfigSomeKind::Exports + } else { + return Err(lookahead.error()); + }; + + let list; + syn::bracketed!(list in input); + let fields = list.parse_terminated(Parse::parse, Token![,])?; + + Ok(( + kind, + fields.iter().map(|s: &syn::LitStr| s.value()).collect(), + )) +} diff --git a/crates/rust/src/async_support.rs b/crates/rust/src/async_support.rs new file mode 100644 index 000000000..191b21663 --- /dev/null +++ b/crates/rust/src/async_support.rs @@ -0,0 +1,45 @@ +use std::{ + alloc::Layout, + future::Future, + pin::pin, + sync::Arc, + task::{Context, Poll, Wake}, +}; + +pub fn first_poll(future: impl Future + 'static) -> Result { + struct DummyWaker; + + impl Wake for DummyWaker { + fn wake(self: Arc) {} + } + + let mut future = pin!(future); + + match future + .as_mut() + .poll(&mut Context::from_waker(&Arc::new(DummyWaker).into())) + { + Poll::Ready(result) => Ok(result), + Poll::Pending => todo!(), + } +} + +const STATUS_NOT_STARTED: i32 = 0; +const STATUS_PARAMS_READ: i32 = 1; +const STATUS_RESULTS_WRITTEN: i32 = 2; +const STATUS_DONE: i32 = 3; + +pub async unsafe fn await_result( + import: unsafe extern "C" fn(*mut u8, *mut u8, *mut u8) -> i32, + params_layout: Layout, + params: *mut u8, + results: *mut u8, + call: *mut u8, +) { + match import(params, results, call) { + STATUS_DONE => { + alloc::dealloc(params, params_layout); + } + _ => todo!(), + } +} diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 9a3ae3c7b..76c0b284d 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -8,6 +8,7 @@ use wit_bindgen_core::{dealias, uwrite, uwriteln, wit_parser::*, Source}; pub(super) struct FunctionBindgen<'a, 'b> { pub gen: &'b mut InterfaceGenerator<'a>, params: Vec, + async_: bool, pub src: Source, blocks: Vec, block_storage: Vec<(Source, Vec<(String, String)>)>, @@ -23,10 +24,12 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { pub(super) fn new( gen: &'b mut InterfaceGenerator<'a>, params: Vec, + async_: bool, ) -> FunctionBindgen<'a, 'b> { FunctionBindgen { gen, params, + async_, src: Default::default(), blocks: Vec::new(), block_storage: Vec::new(), @@ -66,6 +69,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { results: &[WasmType], ) -> String { // Define the actual function we're calling inline + let tmp = self.tmp(); let mut sig = "(".to_owned(); for param in params.iter() { sig.push_str("_: "); @@ -85,14 +89,14 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { #[link(wasm_import_module = \"{module_name}\")] extern \"C\" {{ #[link_name = \"{name}\"] - fn wit_import{sig}; + fn wit_import{tmp}{sig}; }} #[cfg(not(target_arch = \"wasm32\"))] - fn wit_import{sig} {{ unreachable!() }} + extern \"C\" fn wit_import{tmp}{sig} {{ unreachable!() }} " ); - "wit_import".to_string() + format!("wit_import{tmp}") } fn let_results(&mut self, amt: usize, results: &mut Vec) { @@ -780,7 +784,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::CallWasm { name, sig, .. } => { let func = self.declare_import( - self.gen.wasm_import_module.unwrap(), + self.gen.wasm_import_module, name, &sig.params, &sig.results, @@ -797,8 +801,37 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(");\n"); } + Instruction::AsyncCallWasm { name, size, align } => { + let func = self.declare_import( + self.gen.wasm_import_module, + name, + &[WasmType::Pointer; 3], + &[WasmType::I32], + ); + + let await_result = self.gen.path_to_await_result(); + let tmp = self.tmp(); + let layout = format!("layout{tmp}"); + let alloc = self.gen.path_to_std_alloc_module(); + self.push_str(&format!( + "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align});\n", + )); + let operands = operands.join(", "); + uwriteln!( + self.src, + "{await_result}({func}, {layout}, {operands}).await;" + ); + } + Instruction::CallInterface { func, .. } => { - self.let_results(func.results.len(), results); + if self.async_ { + let tmp = self.tmp(); + let result = format!("result{tmp}"); + self.push_str(&format!("let {result} = ")); + results.push(result); + } else { + self.let_results(func.results.len(), results); + }; match &func.kind { FunctionKind::Freestanding => { self.push_str(&format!("T::{}", to_rust_ident(&func.name))); @@ -839,6 +872,55 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(";\n"); } + Instruction::AsyncMalloc { size, align } => { + let alloc = self.gen.path_to_std_alloc_module(); + let tmp = self.tmp(); + let ptr = format!("ptr{tmp}"); + let layout = format!("layout{tmp}"); + uwriteln!( + self.src, + "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align}); + let {ptr} = {alloc}::alloc({layout});" + ); + results.push(ptr); + } + + Instruction::AsyncPostCallInterface { func } => { + let result = &operands[0]; + self.let_results(func.results.len(), results); + let first_poll = self.gen.path_to_first_poll(); + uwriteln!( + self.src, + "\ + match {first_poll}({result}) {{ + Ok(results) => results, + Err(ctx) => return ctx, + }};\ + " + ); + } + + Instruction::AsyncCallStart { + name, + params, + results: call_results, + } => { + let func = + self.declare_import(self.gen.wasm_import_module, name, params, call_results); + + if !call_results.is_empty() { + self.push_str("let ret = "); + results.push("ret".to_string()); + } + uwriteln!(self.src, "{func}({});", operands.join(", ")); + } + + Instruction::AsyncCallReturn { name, params } => { + let func = self.declare_import(self.gen.wasm_import_module, name, params, &[]); + + uwriteln!(self.src, "{func}({});", operands.join(", ")); + } + Instruction::Return { amt, .. } => { self.emit_cleanup(); match amt { diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index f31d39235..6664a0643 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -1,7 +1,7 @@ use crate::bindgen::FunctionBindgen; use crate::{ - int_repr, to_rust_ident, to_upper_camel_case, wasm_type, FnSig, Identifier, InterfaceName, - Ownership, RuntimeItem, RustFlagsRepr, RustWasm, + int_repr, to_rust_ident, to_upper_camel_case, wasm_type, AsyncConfig, FnSig, Identifier, + InterfaceName, Ownership, RuntimeItem, RustFlagsRepr, RustWasm, }; use anyhow::Result; use heck::*; @@ -17,7 +17,7 @@ pub struct InterfaceGenerator<'a> { pub in_import: bool, pub sizes: SizeAlign, pub(super) gen: &'a mut RustWasm, - pub wasm_import_module: Option<&'a str>, + pub wasm_import_module: &'a str, pub resolve: &'a Resolve, pub return_pointer_area_size: usize, pub return_pointer_area_align: usize, @@ -135,7 +135,7 @@ impl InterfaceGenerator<'_> { let mut funcs_to_export = Vec::new(); let mut resources_to_drop = Vec::new(); - traits.insert(None, ("Guest".to_string(), Vec::new())); + traits.insert(None, ("Guest".to_string(), Vec::new(), false)); if let Some((id, _)) = interface { for (name, id) in self.resolve.interfaces[id].types.iter() { @@ -145,7 +145,7 @@ impl InterfaceGenerator<'_> { } resources_to_drop.push(name); let camel = name.to_upper_camel_case(); - traits.insert(Some(*id), (format!("Guest{camel}"), Vec::new())); + traits.insert(Some(*id), (format!("Guest{camel}"), Vec::new(), false)); } } @@ -154,6 +154,17 @@ impl InterfaceGenerator<'_> { continue; } + let async_ = match &self.gen.opts.async_ { + AsyncConfig::None => false, + AsyncConfig::All => true, + AsyncConfig::Some { exports, .. } => { + exports.contains(&if let Some((_, key)) = interface { + format!("{}/{}", self.resolve.name_world_key(key), func.name) + } else { + func.name.clone() + }) + } + }; let resource = match func.kind { FunctionKind::Freestanding => None, FunctionKind::Method(id) @@ -161,12 +172,14 @@ impl InterfaceGenerator<'_> { | FunctionKind::Static(id) => Some(id), }; - funcs_to_export.push((func, resource)); - let (trait_name, methods) = traits.get_mut(&resource).unwrap(); - self.generate_guest_export(func, &trait_name); + funcs_to_export.push((func, resource, async_)); + let (trait_name, methods, async_methods) = traits.get_mut(&resource).unwrap(); + *async_methods |= async_; + self.generate_guest_export(func, &trait_name, async_); let prev = mem::take(&mut self.src); let mut sig = FnSig { + async_, use_item_name: true, private: true, ..Default::default() @@ -181,18 +194,22 @@ impl InterfaceGenerator<'_> { methods.push(trait_method); } - let (name, methods) = traits.remove(&None).unwrap(); + let (name, methods, async_methods) = traits.remove(&None).unwrap(); if !methods.is_empty() || !traits.is_empty() { self.generate_interface_trait( &name, &methods, - traits.iter().map(|(resource, (trait_name, _methods))| { - (resource.unwrap(), trait_name.as_str()) - }), + traits + .iter() + .map(|(resource, (trait_name, ..))| (resource.unwrap(), trait_name.as_str())), + async_methods, ) } - for (resource, (trait_name, methods)) in traits.iter() { + for (resource, (trait_name, methods, async_methods)) in traits.iter() { + if *async_methods { + uwriteln!(self.src, "#[async_trait::async_trait(?Send)]"); + } uwriteln!(self.src, "pub trait {trait_name}: 'static {{"); let resource = resource.unwrap(); let resource_name = self.resolve.types[resource].name.as_ref().unwrap(); @@ -290,7 +307,7 @@ macro_rules! {macro_name} {{ " ); - for (func, resource) in funcs_to_export { + for (func, resource, async_) in funcs_to_export { let ty = match resource { None => "$ty".to_string(), Some(id) => { @@ -302,7 +319,7 @@ macro_rules! {macro_name} {{ format!("<$ty as $($path_to_types)*::Guest>::{name}") } }; - self.generate_raw_cabi_export(func, &ty, "$($path_to_types)*"); + self.generate_raw_cabi_export(func, &ty, "$($path_to_types)*", async_); } let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); for name in resources_to_drop { @@ -339,7 +356,11 @@ macro_rules! {macro_name} {{ trait_name: &str, methods: &[Source], resource_traits: impl Iterator, + async_methods: bool, ) { + if async_methods { + uwriteln!(self.src, "#[async_trait::async_trait(?Send)]"); + } uwriteln!(self.src, "pub trait {trait_name} {{"); for (id, trait_name) in resource_traits { let name = self.resolve.types[id] @@ -355,9 +376,13 @@ macro_rules! {macro_name} {{ uwriteln!(self.src, "}}"); } - pub fn generate_imports<'a>(&mut self, funcs: impl Iterator) { + pub fn generate_imports<'a>( + &mut self, + funcs: impl Iterator, + interface: Option<&WorldKey>, + ) { for func in funcs { - self.generate_guest_import(func); + self.generate_guest_import(func, interface); } } @@ -443,12 +468,24 @@ macro_rules! {macro_name} {{ map.push((module, module_path)) } - fn generate_guest_import(&mut self, func: &Function) { + fn generate_guest_import(&mut self, func: &Function, interface: Option<&WorldKey>) { if self.gen.skip.contains(&func.name) { return; } - let mut sig = FnSig::default(); + let async_ = match &self.gen.opts.async_ { + AsyncConfig::None => false, + AsyncConfig::All => true, + AsyncConfig::Some { imports, .. } => imports.contains(&if let Some(key) = interface { + format!("{}/{}", self.resolve.name_world_key(key), func.name) + } else { + func.name.clone() + }), + }; + let mut sig = FnSig { + async_, + ..Default::default() + }; match func.kind { FunctionKind::Freestanding => {} FunctionKind::Method(id) | FunctionKind::Static(id) | FunctionKind::Constructor(id) => { @@ -467,13 +504,14 @@ macro_rules! {macro_name} {{ self.src.push_str("{\n"); self.src.push_str("unsafe {\n"); - let mut f = FunctionBindgen::new(self, params); + let mut f = FunctionBindgen::new(self, params, async_); abi::call( f.gen.resolve, AbiVariant::GuestImport, LiftLower::LowerArgsLiftResults, func, &mut f, + async_, ); let FunctionBindgen { needs_cleanup_list, @@ -512,17 +550,23 @@ macro_rules! {macro_name} {{ } } - fn generate_guest_export(&mut self, func: &Function, trait_name: &str) { + fn generate_guest_export(&mut self, func: &Function, trait_name: &str, async_: bool) { let name_snake = func.name.to_snake_case().replace('.', "_"); + uwrite!( self.src, "\ #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn _export_{name_snake}_cabi\ -", + ", ); - let params = self.print_export_sig(func); + let params = if async_ { + self.push_str("() -> *mut u8"); + Vec::new() + } else { + self.print_export_sig(func) + }; self.push_str(" {"); if !self.gen.opts.disable_run_ctors_once_workaround { @@ -541,13 +585,14 @@ macro_rules! {macro_name} {{ ); } - let mut f = FunctionBindgen::new(self, params); + let mut f = FunctionBindgen::new(self, params, async_); abi::call( f.gen.resolve, AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, func, &mut f, + async_, ); let FunctionBindgen { needs_cleanup_list, @@ -570,13 +615,13 @@ macro_rules! {macro_name} {{ #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn __post_return_{name_snake}\ -" + " ); let params = self.print_post_return_sig(func); self.src.push_str("{\n"); - let mut f = FunctionBindgen::new(self, params); - abi::post_return(f.gen.resolve, func, &mut f); + let mut f = FunctionBindgen::new(self, params, async_); + abi::post_return(f.gen.resolve, func, &mut f, async_); let FunctionBindgen { needs_cleanup_list, src, @@ -590,7 +635,13 @@ macro_rules! {macro_name} {{ } } - fn generate_raw_cabi_export(&mut self, func: &Function, ty: &str, path_to_self: &str) { + fn generate_raw_cabi_export( + &mut self, + func: &Function, + ty: &str, + path_to_self: &str, + async_: bool, + ) { let name_snake = func.name.to_snake_case().replace('.', "_"); let wasm_module_export_name = match self.identifier { Identifier::Interface(_, key) => Some(self.resolve.name_world_key(key)), @@ -606,7 +657,12 @@ macro_rules! {macro_name} {{ ", ); - let params = self.print_export_sig(func); + let params = if async_ { + self.push_str("() -> *mut u8"); + Vec::new() + } else { + self.print_export_sig(func) + }; self.push_str(" {\n"); uwriteln!( self.src, @@ -1936,6 +1992,14 @@ macro_rules! {macro_name} {{ self.path_from_runtime_module(RuntimeItem::StdAllocModule, "alloc") } + pub fn path_to_await_result(&mut self) -> String { + self.path_from_runtime_module(RuntimeItem::AsyncSupport, "await_result") + } + + pub fn path_to_first_poll(&mut self) -> String { + self.path_from_runtime_module(RuntimeItem::AsyncSupport, "first_poll") + } + fn path_from_runtime_module( &mut self, item: RuntimeItem, @@ -1993,7 +2057,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { }} "# ); - self.wasm_import_module.unwrap().to_string() + self.wasm_import_module.to_string() } else { let module = match self.identifier { Identifier::Interface(_, key) => self.resolve.name_world_key(key), diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index cfebf82a1..86a14f21e 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -66,6 +66,7 @@ enum RuntimeItem { AsF64, ResourceType, BoxType, + AsyncSupport, } #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -82,6 +83,23 @@ fn parse_with(s: &str) -> Result<(String, String), String> { Ok((k.to_string(), v.to_string())) } +#[derive(Default, Debug, Clone)] +pub enum AsyncConfig { + #[default] + None, + Some { + imports: Vec, + exports: Vec, + }, + All, +} + +#[cfg(feature = "clap")] +fn parse_async(s: &str) -> Result { + _ = s; + Err("todo: parse `AsyncConfig`".into()) +} + #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "clap", derive(clap::Args))] pub struct Opts { @@ -182,6 +200,10 @@ pub struct Opts { /// candidate for being exported outside of the crate. #[cfg_attr(feature = "clap", arg(long))] pub pub_export_macro: bool, + + /// Determines which functions to lift or lower `async`, if any. + #[cfg_attr(feature = "clap", arg(long = "async", value_parser = parse_async))] + pub async_: AsyncConfig, } impl Opts { @@ -201,7 +223,7 @@ impl RustWasm { fn interface<'a>( &'a mut self, identifier: Identifier<'a>, - wasm_import_module: Option<&'a str>, + wasm_import_module: &'a str, resolve: &'a Resolve, in_import: bool, ) -> InterfaceGenerator<'a> { @@ -548,6 +570,10 @@ impl Drop for Resource { "#, ); } + + RuntimeItem::AsyncSupport => { + self.src.push_str(include_str!("async_support.rs")); + } } } @@ -880,7 +906,7 @@ impl WorldGenerator for RustWasm { let wasm_import_module = resolve.name_world_key(name); let mut gen = self.interface( Identifier::Interface(id, name), - Some(&wasm_import_module), + &wasm_import_module, resolve, true, ); @@ -890,7 +916,7 @@ impl WorldGenerator for RustWasm { } gen.types(id); - gen.generate_imports(resolve.interfaces[id].functions.values()); + gen.generate_imports(resolve.interfaces[id].functions.values(), Some(name)); gen.finish_append_submodule(&snake, module_path); } @@ -904,9 +930,9 @@ impl WorldGenerator for RustWasm { ) { self.import_funcs_called = true; - let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true); + let mut gen = self.interface(Identifier::World(world), "$root", resolve, true); - gen.generate_imports(funcs.iter().map(|(_, func)| *func)); + gen.generate_imports(funcs.iter().map(|(_, func)| *func), None); let src = gen.finish(); self.src.push_str(&src); @@ -920,7 +946,13 @@ impl WorldGenerator for RustWasm { _files: &mut Files, ) -> Result<()> { self.interface_last_seen_as_import.insert(id, false); - let mut gen = self.interface(Identifier::Interface(id, name), None, resolve, false); + let wasm_import_module = format!("[export]{}", resolve.name_world_key(name)); + let mut gen = self.interface( + Identifier::Interface(id, name), + &wasm_import_module, + resolve, + false, + ); let (snake, module_path) = gen.start_append_submodule(name); if gen.gen.name_interface(resolve, id, name, true) { return Ok(()); @@ -934,7 +966,12 @@ impl WorldGenerator for RustWasm { if self.opts.stubs { let world_id = self.world.unwrap(); - let mut gen = self.interface(Identifier::World(world_id), None, resolve, false); + let mut gen = self.interface( + Identifier::World(world_id), + &wasm_import_module, + resolve, + false, + ); gen.generate_stub(Some((id, name)), resolve.interfaces[id].functions.values()); let stub = gen.finish(); self.src.push_str(&stub); @@ -949,14 +986,14 @@ impl WorldGenerator for RustWasm { funcs: &[(&str, &Function)], _files: &mut Files, ) -> Result<()> { - let mut gen = self.interface(Identifier::World(world), None, resolve, false); + let mut gen = self.interface(Identifier::World(world), "[export]$root", resolve, false); let macro_name = gen.generate_exports(None, funcs.iter().map(|f| f.1))?; let src = gen.finish(); self.src.push_str(&src); self.export_macros.push((macro_name, String::new())); if self.opts.stubs { - let mut gen = self.interface(Identifier::World(world), None, resolve, false); + let mut gen = self.interface(Identifier::World(world), "[export]$root", resolve, false); gen.generate_stub(None, funcs.iter().map(|f| f.1)); let stub = gen.finish(); self.src.push_str(&stub); @@ -971,7 +1008,7 @@ impl WorldGenerator for RustWasm { types: &[(&str, TypeId)], _files: &mut Files, ) { - let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true); + let mut gen = self.interface(Identifier::World(world), "$root", resolve, true); for (name, ty) in types { gen.define_type(name, *ty); } diff --git a/crates/teavm-java/src/lib.rs b/crates/teavm-java/src/lib.rs index 3a25c513c..34d89d4fd 100644 --- a/crates/teavm-java/src/lib.rs +++ b/crates/teavm-java/src/lib.rs @@ -499,6 +499,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut bindgen, + false, ); let src = bindgen.src; @@ -568,6 +569,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut bindgen, + false, ); assert!(!bindgen.needs_cleanup_list); @@ -621,7 +623,7 @@ impl InterfaceGenerator<'_> { (0..sig.results.len()).map(|i| format!("p{i}")).collect(), ); - abi::post_return(bindgen.gen.resolve, func, &mut bindgen); + abi::post_return(bindgen.gen.resolve, func, &mut bindgen, false); let src = bindgen.src; @@ -2026,6 +2028,11 @@ impl Bindgen for FunctionBindgen<'_, '_> { "Memory.free(Address.fromInt({address}), ({length}) * {size}, {align});" ); } + + Instruction::AsyncMalloc { .. } + | Instruction::AsyncCallStart { .. } + | Instruction::AsyncPostCallInterface { .. } + | Instruction::AsyncCallReturn { .. } => todo!(), } } From d44a0232d48d65eb6f711d08020dd881bc122f17 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 25 Mar 2024 21:59:32 +0100 Subject: [PATCH 143/672] this version of wasm-tools seem to also pass cargo check :-S --- Cargo.lock | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 67c29070e..b282452bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,9 +106,9 @@ checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" [[package]] name = "async-trait" -version = "0.1.78" +version = "0.1.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", @@ -123,9 +123,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -186,9 +186,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cap-fs-ext" @@ -837,9 +837,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -1149,9 +1149,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -1314,9 +1314,9 @@ checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" @@ -1357,9 +1357,9 @@ checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "syn" -version = "2.0.53" +version = "2.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" dependencies = [ "proc-macro2", "quote", @@ -1485,9 +1485,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.8" +version = "0.22.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c12219811e0c1ba077867254e5ad62ee2c9c190b0d957110750ac0cda1ae96cd" +checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" dependencies = [ "indexmap", "serde", @@ -1673,7 +1673,7 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-encoder" version = "0.201.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#ac798ee10168d2278c03ae928ec9f747b3335c00" +source = "git+https://github.com/bytecodealliance/wasm-tools#fd5d76af28c1e1f0685f929f7d253e80d52b9eab" dependencies = [ "leb128", ] @@ -1681,7 +1681,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.201.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#ac798ee10168d2278c03ae928ec9f747b3335c00" +source = "git+https://github.com/bytecodealliance/wasm-tools#fd5d76af28c1e1f0685f929f7d253e80d52b9eab" dependencies = [ "anyhow", "indexmap", @@ -1696,7 +1696,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.201.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#031fc6141878b3be9c37f2ea1247814fb3451640" +source = "git+https://github.com/bytecodealliance/wasm-tools#fd5d76af28c1e1f0685f929f7d253e80d52b9eab" dependencies = [ "bitflags 2.5.0", "indexmap", @@ -1706,7 +1706,7 @@ dependencies = [ [[package]] name = "wasmprinter" version = "0.201.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#ac798ee10168d2278c03ae928ec9f747b3335c00" +source = "git+https://github.com/bytecodealliance/wasm-tools#fd5d76af28c1e1f0685f929f7d253e80d52b9eab" dependencies = [ "anyhow", "wasmparser", @@ -2064,7 +2064,7 @@ dependencies = [ [[package]] name = "wast" version = "201.0.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#ac798ee10168d2278c03ae928ec9f747b3335c00" +source = "git+https://github.com/bytecodealliance/wasm-tools#fd5d76af28c1e1f0685f929f7d253e80d52b9eab" dependencies = [ "bumpalo", "leb128", @@ -2085,7 +2085,7 @@ dependencies = [ [[package]] name = "wat" version = "1.201.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#ac798ee10168d2278c03ae928ec9f747b3335c00" +source = "git+https://github.com/bytecodealliance/wasm-tools#fd5d76af28c1e1f0685f929f7d253e80d52b9eab" dependencies = [ "wast 201.0.0 (git+https://github.com/bytecodealliance/wasm-tools)", ] @@ -2491,7 +2491,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.201.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#ac798ee10168d2278c03ae928ec9f747b3335c00" +source = "git+https://github.com/bytecodealliance/wasm-tools#fd5d76af28c1e1f0685f929f7d253e80d52b9eab" dependencies = [ "anyhow", "bitflags 2.5.0", @@ -2510,7 +2510,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.201.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#031fc6141878b3be9c37f2ea1247814fb3451640" +source = "git+https://github.com/bytecodealliance/wasm-tools#fd5d76af28c1e1f0685f929f7d253e80d52b9eab" dependencies = [ "anyhow", "id-arena", From f00dc117a18d7bb060a8410972ac70abed297c02 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 26 Mar 2024 07:50:03 -0700 Subject: [PATCH 144/672] Update wasm-tools crates Mainly a few internal changes of `Float32` naming to `F32` for example. Not all guest generators are updated to remove the "float" terminology entirely yet. --- Cargo.lock | 116 +++++++++++++++++++++++------------ Cargo.toml | 10 +-- crates/c/src/lib.rs | 12 ++-- crates/core/src/abi.rs | 24 ++++---- crates/csharp/src/lib.rs | 12 ++-- crates/go/src/interface.rs | 10 +-- crates/go/src/lib.rs | 4 +- crates/markdown/src/lib.rs | 4 +- crates/rust/src/interface.rs | 4 +- crates/teavm-java/src/lib.rs | 16 ++--- 10 files changed, 125 insertions(+), 87 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0ba41757f..f4c975b1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -466,7 +466,7 @@ dependencies = [ "itertools", "log", "smallvec", - "wasmparser", + "wasmparser 0.201.0", "wasmtime-types", ] @@ -1397,10 +1397,10 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder", + "wasm-encoder 0.202.0", "wit-bindgen-core", "wit-component", - "wit-parser", + "wit-parser 0.202.0", ] [[package]] @@ -1679,11 +1679,20 @@ dependencies = [ "leb128", ] +[[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.201.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2" +checksum = "094aea3cb90e09f16ee25a4c0e324b3e8c934e7fd838bfa039aef5352f44a917" dependencies = [ "anyhow", "indexmap", @@ -1691,8 +1700,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.202.0", + "wasmparser 0.202.0", ] [[package]] @@ -1706,6 +1715,17 @@ dependencies = [ "semver", ] +[[package]] +name = "wasmparser" +version = "0.202.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6998515d3cf3f8b980ef7c11b29a9b1017d4cf86b99ae93b546992df9931413" +dependencies = [ + "bitflags 2.5.0", + "indexmap", + "semver", +] + [[package]] name = "wasmprinter" version = "0.201.0" @@ -1713,7 +1733,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a67e66da702706ba08729a78e3c0079085f6bfcb1a62e4799e97bbf728c2c265" dependencies = [ "anyhow", - "wasmparser", + "wasmparser 0.201.0", ] [[package]] @@ -1745,8 +1765,8 @@ dependencies = [ "serde_derive", "serde_json", "target-lexicon", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.201.0", + "wasmparser 0.201.0", "wasmtime-cache", "wasmtime-component-macro", "wasmtime-component-util", @@ -1803,7 +1823,7 @@ dependencies = [ "syn", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser", + "wit-parser 0.201.0", ] [[package]] @@ -1831,7 +1851,7 @@ dependencies = [ "object", "target-lexicon", "thiserror", - "wasmparser", + "wasmparser 0.201.0", "wasmtime-cranelift-shared", "wasmtime-environ", "wasmtime-versioned-export-macros", @@ -1872,8 +1892,8 @@ dependencies = [ "serde_derive", "target-lexicon", "thiserror", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.201.0", + "wasmparser 0.201.0", "wasmprinter", "wasmtime-component-util", "wasmtime-types", @@ -1937,7 +1957,7 @@ dependencies = [ "psm", "rustix", "sptr", - "wasm-encoder", + "wasm-encoder 0.201.0", "wasmtime-asm-macros", "wasmtime-environ", "wasmtime-fiber", @@ -1963,7 +1983,7 @@ dependencies = [ "serde", "serde_derive", "thiserror", - "wasmparser", + "wasmparser 0.201.0", ] [[package]] @@ -2019,7 +2039,7 @@ dependencies = [ "gimli", "object", "target-lexicon", - "wasmparser", + "wasmparser 0.201.0", "wasmtime-cranelift-shared", "wasmtime-environ", "winch-codegen", @@ -2034,7 +2054,7 @@ dependencies = [ "anyhow", "heck 0.4.1", "indexmap", - "wit-parser", + "wit-parser 0.201.0", ] [[package]] @@ -2054,24 +2074,24 @@ dependencies = [ [[package]] name = "wast" -version = "201.0.0" +version = "202.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ef6e1ef34d7da3e2b374fd2b1a9c0227aff6cad596e1b24df9b58d0f6222faa" +checksum = "1fbcb11204515c953c9b42ede0a46a1c5e17f82af05c4fae201a8efff1b0f4fe" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder", + "wasm-encoder 0.202.0", ] [[package]] name = "wat" -version = "1.201.0" +version = "1.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453d5b37a45b98dee4f4cb68015fc73634d7883bbef1c65e6e9c78d454cf3f32" +checksum = "4de4b15a47135c56a3573406e9977b9518787a6154459b4842a9b9d3d1684848" dependencies = [ - "wast 201.0.0", + "wast 202.0.0", ] [[package]] @@ -2150,7 +2170,7 @@ dependencies = [ "regalloc2", "smallvec", "target-lexicon", - "wasmparser", + "wasmparser 0.201.0", "wasmtime-environ", ] @@ -2330,11 +2350,11 @@ dependencies = [ "clap", "heck 0.4.1", "test-helpers", - "wasm-encoder", + "wasm-encoder 0.202.0", "wasm-metadata", "wit-bindgen-core", "wit-component", - "wit-parser", + "wit-parser 0.202.0", ] [[package]] @@ -2345,8 +2365,8 @@ dependencies = [ "clap", "heck 0.4.1", "test-artifacts", - "wasm-encoder", - "wasmparser", + "wasm-encoder 0.202.0", + "wasmparser 0.202.0", "wasmtime", "wasmtime-wasi", "wit-bindgen-c", @@ -2357,7 +2377,7 @@ dependencies = [ "wit-bindgen-rust", "wit-bindgen-teavm-java", "wit-component", - "wit-parser", + "wit-parser 0.202.0", ] [[package]] @@ -2365,7 +2385,7 @@ name = "wit-bindgen-core" version = "0.22.0" dependencies = [ "anyhow", - "wit-parser", + "wit-parser 0.202.0", ] [[package]] @@ -2376,9 +2396,9 @@ dependencies = [ "clap", "heck 0.4.1", "test-helpers", - "wasm-encoder", + "wasm-encoder 0.202.0", "wasm-metadata", - "wasmparser", + "wasmparser 0.202.0", "wit-bindgen-core", "wit-component", ] @@ -2458,9 +2478,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.201.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825" +checksum = "0c836b1fd9932de0431c1758d8be08212071b6bba0151f7bac826dbc4312a2a9" dependencies = [ "anyhow", "bitflags 2.5.0", @@ -2469,11 +2489,11 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder", + "wasm-encoder 0.202.0", "wasm-metadata", - "wasmparser", + "wasmparser 0.202.0", "wat", - "wit-parser", + "wit-parser 0.202.0", ] [[package]] @@ -2491,7 +2511,25 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser", + "wasmparser 0.201.0", +] + +[[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 0.202.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d3055cfe3..8e0d741d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,11 +28,11 @@ pulldown-cmark = { version = "0.9", default-features = false } clap = { version = "4.3.19", features = ["derive"] } indexmap = "2.0.0" -wasmparser = "0.201.0" -wasm-encoder = "0.201.0" -wasm-metadata = "0.201.0" -wit-parser = "0.201.0" -wit-component = "0.201.0" +wasmparser = "0.202.0" +wasm-encoder = "0.202.0" +wasm-metadata = "0.202.0" +wit-parser = "0.202.0" +wit-component = "0.202.0" wit-bindgen-core = { path = 'crates/core', version = '0.22.0' } wit-bindgen-c = { path = 'crates/c', version = '0.22.0' } diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 18c22cbfa..5b56b0c52 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -560,8 +560,8 @@ impl C { Type::S32 => dst.push_str("int32_t"), Type::U64 => dst.push_str("uint64_t"), Type::S64 => dst.push_str("int64_t"), - Type::Float32 => dst.push_str("float"), - Type::Float64 => dst.push_str("double"), + Type::F32 => dst.push_str("float"), + Type::F64 => dst.push_str("double"), Type::String => { dst.push_str(&self.world.to_snake_case()); dst.push_str("_"); @@ -731,8 +731,8 @@ pub fn push_ty_name(resolve: &Resolve, ty: &Type, src: &mut String) { Type::S32 => src.push_str("s32"), Type::U64 => src.push_str("u64"), Type::S64 => src.push_str("s64"), - Type::Float32 => src.push_str("float32"), - Type::Float64 => src.push_str("float64"), + Type::F32 => src.push_str("float32"), + Type::F64 => src.push_str("float64"), Type::String => src.push_str("string"), Type::Id(id) => { let ty = &resolve.types[*id]; @@ -1617,8 +1617,8 @@ impl InterfaceGenerator<'_> { | Type::S32 | Type::U64 | Type::S64 - | Type::Float32 - | Type::Float64 + | Type::F32 + | Type::F64 | Type::Char => {} } } diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index ef4e5e10e..4cc631d95 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -747,8 +747,8 @@ fn needs_post_return(resolve: &Resolve, ty: &Type) -> bool { | Type::S32 | Type::U64 | Type::S64 - | Type::Float32 - | Type::Float64 + | Type::F32 + | Type::F64 | Type::Char => false, } } @@ -1067,8 +1067,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { Type::S64 => self.emit(&I64FromS64), Type::U64 => self.emit(&I64FromU64), Type::Char => self.emit(&I32FromChar), - Type::Float32 => self.emit(&F32FromFloat32), - Type::Float64 => self.emit(&F64FromFloat64), + Type::F32 => self.emit(&F32FromFloat32), + Type::F64 => self.emit(&F64FromFloat64), Type::String => { let realloc = self.list_realloc(); self.emit(&StringLower { realloc }); @@ -1256,8 +1256,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { Type::S64 => self.emit(&S64FromI64), Type::U64 => self.emit(&U64FromI64), Type::Char => self.emit(&CharFromI32), - Type::Float32 => self.emit(&Float32FromF32), - Type::Float64 => self.emit(&Float64FromF64), + Type::F32 => self.emit(&Float32FromF32), + Type::F64 => self.emit(&Float64FromF64), Type::String => self.emit(&StringLift), Type::Id(id) => match &self.resolve.types[id].kind { TypeDefKind::Type(t) => self.lift(t), @@ -1414,8 +1414,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.lower_and_emit(ty, addr, &I32Store { offset }) } Type::U64 | Type::S64 => self.lower_and_emit(ty, addr, &I64Store { offset }), - Type::Float32 => self.lower_and_emit(ty, addr, &F32Store { offset }), - Type::Float64 => self.lower_and_emit(ty, addr, &F64Store { offset }), + Type::F32 => self.lower_and_emit(ty, addr, &F32Store { offset }), + Type::F64 => self.lower_and_emit(ty, addr, &F64Store { offset }), Type::String => self.write_list_to_memory(ty, addr, offset), Type::Id(id) => match &self.resolve.types[id].kind { @@ -1602,8 +1602,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { Type::S16 => self.emit_and_lift(ty, addr, &I32Load16S { offset }), Type::U32 | Type::S32 | Type::Char => self.emit_and_lift(ty, addr, &I32Load { offset }), Type::U64 | Type::S64 => self.emit_and_lift(ty, addr, &I64Load { offset }), - Type::Float32 => self.emit_and_lift(ty, addr, &F32Load { offset }), - Type::Float64 => self.emit_and_lift(ty, addr, &F64Load { offset }), + Type::F32 => self.emit_and_lift(ty, addr, &F32Load { offset }), + Type::F64 => self.emit_and_lift(ty, addr, &F64Load { offset }), Type::String => self.read_list_from_memory(ty, addr, offset), Type::Id(id) => match &self.resolve.types[id].kind { @@ -1799,8 +1799,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { | Type::Char | Type::U64 | Type::S64 - | Type::Float32 - | Type::Float64 => {} + | Type::F32 + | Type::F64 => {} Type::Id(id) => match &self.resolve.types[id].kind { TypeDefKind::Type(t) => self.deallocate(t, addr, offset), diff --git a/crates/csharp/src/lib.rs b/crates/csharp/src/lib.rs index b2dce297d..5a8e39b94 100644 --- a/crates/csharp/src/lib.rs +++ b/crates/csharp/src/lib.rs @@ -1151,8 +1151,8 @@ impl InterfaceGenerator<'_> { Type::S16 => "short".to_owned(), Type::S32 => "int".to_owned(), Type::S64 => "long".to_owned(), - Type::Float32 => "float".to_owned(), - Type::Float64 => "double".to_owned(), + Type::F32 => "float".to_owned(), + Type::F64 => "double".to_owned(), Type::Char => "uint".to_owned(), Type::String => "string".to_owned(), Type::Id(id) => { @@ -1241,8 +1241,8 @@ impl InterfaceGenerator<'_> { Type::S16 => "short".into(), Type::S32 => "int".into(), Type::S64 => "long".into(), - Type::Float32 => "float".into(), - Type::Float64 => "double".into(), + Type::F32 => "float".into(), + Type::F64 => "double".into(), Type::Char => "uint".into(), Type::Id(id) => { let def = &self.resolve.types[*id]; @@ -2332,8 +2332,8 @@ fn is_primitive(ty: &Type) -> bool { | Type::S32 | Type::U64 | Type::S64 - | Type::Float32 - | Type::Float64 + | Type::F32 + | Type::F64 ) } diff --git a/crates/go/src/interface.rs b/crates/go/src/interface.rs index 9a0bf038c..d29d9dbb7 100644 --- a/crates/go/src/interface.rs +++ b/crates/go/src/interface.rs @@ -217,8 +217,8 @@ impl InterfaceGenerator<'_> { Type::S16 => "int16".into(), Type::S32 => "int32".into(), Type::S64 => "int64".into(), - Type::Float32 => "float32".into(), - Type::Float64 => "float64".into(), + Type::F32 => "float32".into(), + Type::F64 => "float64".into(), Type::Char => "rune".into(), Type::String => "string".into(), Type::Id(id) => { @@ -259,8 +259,8 @@ impl InterfaceGenerator<'_> { Type::S16 => "S16".into(), Type::S32 => "S32".into(), Type::S64 => "S64".into(), - Type::Float32 => "F32".into(), - Type::Float64 => "F64".into(), + Type::F32 => "F32".into(), + Type::F64 => "F64".into(), Type::Char => "Byte".into(), Type::String => "String".into(), Type::Id(id) => { @@ -1049,7 +1049,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { self.src, "//go:wasmimport {import_module} [resource-drop]{name} func _{type_name}_drop(self {type_name}) - + func (self {type_name}) Drop() {{ _{type_name}_drop(self) }} diff --git a/crates/go/src/lib.rs b/crates/go/src/lib.rs index c3a6d9048..3beb4dc51 100644 --- a/crates/go/src/lib.rs +++ b/crates/go/src/lib.rs @@ -105,8 +105,8 @@ impl TinyGo { Type::S16 => "int16_t".into(), Type::S32 => "int32_t".into(), Type::S64 => "int64_t".into(), - Type::Float32 => "float".into(), - Type::Float64 => "double".into(), + Type::F32 => "float".into(), + Type::F64 => "double".into(), Type::Char => "uint32_t".into(), Type::String => { format!( diff --git a/crates/markdown/src/lib.rs b/crates/markdown/src/lib.rs index 2709b80bd..b01eb02c8 100644 --- a/crates/markdown/src/lib.rs +++ b/crates/markdown/src/lib.rs @@ -333,8 +333,8 @@ impl InterfaceGenerator<'_> { Type::S32 => self.push_str("`s32`"), Type::U64 => self.push_str("`u64`"), Type::S64 => self.push_str("`s64`"), - Type::Float32 => self.push_str("`float32`"), - Type::Float64 => self.push_str("`float64`"), + Type::F32 => self.push_str("`f32`"), + Type::F64 => self.push_str("`f64`"), Type::Char => self.push_str("`char`"), Type::String => self.push_str("`string`"), Type::Id(id) => { diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index f31d39235..cc67e26c2 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -1107,8 +1107,8 @@ macro_rules! {macro_name} {{ Type::S16 => self.push_str("i16"), Type::S32 => self.push_str("i32"), Type::S64 => self.push_str("i64"), - Type::Float32 => self.push_str("f32"), - Type::Float64 => self.push_str("f64"), + Type::F32 => self.push_str("f32"), + Type::F64 => self.push_str("f64"), Type::Char => self.push_str("char"), Type::String => { assert_eq!(mode.lists_borrowed, mode.lifetime.is_some()); diff --git a/crates/teavm-java/src/lib.rs b/crates/teavm-java/src/lib.rs index 3a25c513c..efcee4e0b 100644 --- a/crates/teavm-java/src/lib.rs +++ b/crates/teavm-java/src/lib.rs @@ -661,8 +661,8 @@ impl InterfaceGenerator<'_> { Type::U16 | Type::S16 => "short".into(), Type::U32 | Type::S32 | Type::Char => "int".into(), Type::U64 | Type::S64 => "long".into(), - Type::Float32 => "float".into(), - Type::Float64 => "double".into(), + Type::F32 => "float".into(), + Type::F64 => "double".into(), Type::String => "String".into(), Type::Id(id) => { let ty = &self.resolve.types[*id]; @@ -735,8 +735,8 @@ impl InterfaceGenerator<'_> { Type::U16 | Type::S16 => "Short".into(), Type::U32 | Type::S32 | Type::Char => "Integer".into(), Type::U64 | Type::S64 => "Long".into(), - Type::Float32 => "Float".into(), - Type::Float64 => "Double".into(), + Type::F32 => "Float".into(), + Type::F64 => "Double".into(), Type::Id(id) => { let def = &self.resolve.types[*id]; match &def.kind { @@ -2162,8 +2162,8 @@ fn list_element_info(ty: &Type) -> (usize, &'static str) { Type::U16 | Type::S16 => (2, "short"), Type::U32 | Type::S32 => (4, "int"), Type::U64 | Type::S64 => (8, "long"), - Type::Float32 => (4, "float"), - Type::Float64 => (8, "double"), + Type::F32 => (4, "float"), + Type::F64 => (8, "double"), _ => unreachable!(), } } @@ -2207,8 +2207,8 @@ fn is_primitive(ty: &Type) -> bool { | Type::S32 | Type::U64 | Type::S64 - | Type::Float32 - | Type::Float64 + | Type::F32 + | Type::F64 ) } From d0a98c7d0463c4453c6b06c4cfa3912bf9a5e984 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 27 Mar 2024 01:06:27 +0100 Subject: [PATCH 145/672] remove patching --- Cargo.lock | 70 ++++++++++++++++-------------------------------------- Cargo.toml | 14 +++++------ 2 files changed, 28 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e345a43e0..d1e9ac756 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1397,7 +1397,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.202.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder 0.202.0", "wit-bindgen-core", "wit-component", "wit-parser 0.202.0", @@ -1673,7 +1673,8 @@ checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-encoder" version = "0.201.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#fd5d76af28c1e1f0685f929f7d253e80d52b9eab" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" dependencies = [ "leb128", ] @@ -1687,18 +1688,11 @@ dependencies = [ "leb128", ] -[[package]] -name = "wasm-encoder" -version = "0.202.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#7906a20c315d3d6f3fcc7f35812dc08fa1a926b0" -dependencies = [ - "leb128", -] - [[package]] name = "wasm-metadata" version = "0.202.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#7906a20c315d3d6f3fcc7f35812dc08fa1a926b0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "094aea3cb90e09f16ee25a4c0e324b3e8c934e7fd838bfa039aef5352f44a917" dependencies = [ "anyhow", "indexmap", @@ -1706,7 +1700,7 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.202.0 (git+https://github.com/bytecodealliance/wasm-tools)", + "wasm-encoder 0.202.0", "wasmparser 0.202.0", ] @@ -1724,7 +1718,8 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.202.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#7906a20c315d3d6f3fcc7f35812dc08fa1a926b0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6998515d3cf3f8b980ef7c11b29a9b1017d4cf86b99ae93b546992df9931413" dependencies = [ "bitflags 2.5.0", "indexmap", @@ -1783,7 +1778,7 @@ dependencies = [ "wasmtime-runtime", "wasmtime-slab", "wasmtime-winch", - "wat 1.202.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wat", "windows-sys 0.52.0", ] @@ -2087,19 +2082,7 @@ dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.202.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wast" -version = "202.0.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#7906a20c315d3d6f3fcc7f35812dc08fa1a926b0" -dependencies = [ - "bumpalo", - "leb128", - "memchr", - "unicode-width", - "wasm-encoder 0.202.0 (git+https://github.com/bytecodealliance/wasm-tools)", + "wasm-encoder 0.202.0", ] [[package]] @@ -2108,15 +2091,7 @@ version = "1.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4de4b15a47135c56a3573406e9977b9518787a6154459b4842a9b9d3d1684848" dependencies = [ - "wast 202.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "wat" -version = "1.202.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#7906a20c315d3d6f3fcc7f35812dc08fa1a926b0" -dependencies = [ - "wast 202.0.0 (git+https://github.com/bytecodealliance/wasm-tools)", + "wast 202.0.0", ] [[package]] @@ -2375,7 +2350,7 @@ dependencies = [ "clap", "heck 0.4.1", "test-helpers", - "wasm-encoder 0.202.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder 0.202.0", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -2390,7 +2365,7 @@ dependencies = [ "clap", "heck 0.4.1", "test-artifacts", - "wasm-encoder 0.202.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder 0.202.0", "wasmparser 0.202.0", "wasmtime", "wasmtime-wasi", @@ -2422,7 +2397,7 @@ dependencies = [ "clap", "heck 0.4.1", "test-helpers", - "wasm-encoder 0.202.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder 0.202.0", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", @@ -2437,7 +2412,7 @@ dependencies = [ "clap", "heck 0.4.1", "test-helpers", - "wasm-encoder 0.202.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder 0.202.0", "wasm-metadata", "wasmparser 0.202.0", "wit-bindgen-core", @@ -2520,7 +2495,8 @@ dependencies = [ [[package]] name = "wit-component" version = "0.202.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#7906a20c315d3d6f3fcc7f35812dc08fa1a926b0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c836b1fd9932de0431c1758d8be08212071b6bba0151f7bac826dbc4312a2a9" dependencies = [ "anyhow", "bitflags 2.5.0", @@ -2529,10 +2505,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.202.0 (git+https://github.com/bytecodealliance/wasm-tools)", + "wasm-encoder 0.202.0", "wasm-metadata", "wasmparser 0.202.0", - "wat 1.202.0 (git+https://github.com/bytecodealliance/wasm-tools)", + "wat", "wit-parser 0.202.0", ] @@ -2557,7 +2533,8 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.202.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#7906a20c315d3d6f3fcc7f35812dc08fa1a926b0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744237b488352f4f27bca05a10acb79474415951c450e52ebd0da784c1df2bcc" dependencies = [ "anyhow", "id-arena", @@ -2630,8 +2607,3 @@ dependencies = [ "cc", "pkg-config", ] - -[[patch.unused]] -name = "wasmprinter" -version = "0.202.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#7906a20c315d3d6f3fcc7f35812dc08fa1a926b0" diff --git a/Cargo.toml b/Cargo.toml index b3586f09d..dad141cec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,10 +90,10 @@ 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.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" } From 8de70477a02f0a7b8d1bb2118e3d241e8f657efa Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Wed, 27 Mar 2024 11:57:54 -0600 Subject: [PATCH 146/672] more work on async lift/lower Signed-off-by: Joel Dice --- crates/core/src/abi.rs | 6 ++-- crates/rust/src/interface.rs | 63 ++++++++++++++++++++++-------------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 5d30256c5..d3c232a28 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -888,7 +888,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { assert_eq!(self.stack.len(), 3); self.emit(&Instruction::AsyncCallWasm { - name: &func.name, + name: &format!("[async]{}", func.name), size: params_size, align: params_align, }); @@ -968,7 +968,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.resolve.push_flat(ty, &mut params); } - let name = &format!("[start]{}", func.name); + let name = &format!("[async-start]{}", func.name); if params.len() > MAX_FLAT_RESULTS { let (size, align) = self @@ -1092,7 +1092,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { } if let Some(results) = async_results { - let name = &format!("[return]{}", func.name); + let name = &format!("[async-return]{}", func.name); self.emit(&Instruction::AsyncCallReturn { name, diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 6664a0643..f65b4f27a 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -135,7 +135,7 @@ impl InterfaceGenerator<'_> { let mut funcs_to_export = Vec::new(); let mut resources_to_drop = Vec::new(); - traits.insert(None, ("Guest".to_string(), Vec::new(), false)); + traits.insert(None, ("Guest".to_string(), Vec::new())); if let Some((id, _)) = interface { for (name, id) in self.resolve.interfaces[id].types.iter() { @@ -145,7 +145,7 @@ impl InterfaceGenerator<'_> { } resources_to_drop.push(name); let camel = name.to_upper_camel_case(); - traits.insert(Some(*id), (format!("Guest{camel}"), Vec::new(), false)); + traits.insert(Some(*id), (format!("Guest{camel}"), Vec::new())); } } @@ -173,8 +173,7 @@ impl InterfaceGenerator<'_> { }; funcs_to_export.push((func, resource, async_)); - let (trait_name, methods, async_methods) = traits.get_mut(&resource).unwrap(); - *async_methods |= async_; + let (trait_name, methods) = traits.get_mut(&resource).unwrap(); self.generate_guest_export(func, &trait_name, async_); let prev = mem::take(&mut self.src); @@ -188,13 +187,13 @@ impl InterfaceGenerator<'_> { sig.self_arg = Some("&self".into()); sig.self_is_first_param = true; } - self.print_signature(func, true, &sig); + self.print_signature(func, true, &sig, false); self.src.push_str(";\n"); let trait_method = mem::replace(&mut self.src, prev); methods.push(trait_method); } - let (name, methods, async_methods) = traits.remove(&None).unwrap(); + let (name, methods) = traits.remove(&None).unwrap(); if !methods.is_empty() || !traits.is_empty() { self.generate_interface_trait( &name, @@ -202,14 +201,10 @@ impl InterfaceGenerator<'_> { traits .iter() .map(|(resource, (trait_name, ..))| (resource.unwrap(), trait_name.as_str())), - async_methods, ) } - for (resource, (trait_name, methods, async_methods)) in traits.iter() { - if *async_methods { - uwriteln!(self.src, "#[async_trait::async_trait(?Send)]"); - } + 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(); @@ -356,11 +351,7 @@ macro_rules! {macro_name} {{ trait_name: &str, methods: &[Source], resource_traits: impl Iterator, - async_methods: bool, ) { - if async_methods { - uwriteln!(self.src, "#[async_trait::async_trait(?Send)]"); - } uwriteln!(self.src, "pub trait {trait_name} {{"); for (id, trait_name) in resource_traits { let name = self.resolve.types[id] @@ -500,7 +491,7 @@ macro_rules! {macro_name} {{ } } self.src.push_str("#[allow(unused_unsafe, clippy::all)]\n"); - let params = self.print_signature(func, false, &sig); + let params = self.print_signature(func, false, &sig, true); self.src.push_str("{\n"); self.src.push_str("unsafe {\n"); @@ -649,6 +640,11 @@ macro_rules! {macro_name} {{ }; let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); let export_name = func.core_export_name(wasm_module_export_name.as_deref()); + let export_name = if async_ { + format!("[async]{export_name}") + } else { + export_name.to_string() + }; uwrite!( self.src, "\ @@ -785,7 +781,7 @@ macro_rules! {macro_name} {{ sig.self_arg = Some("&self".into()); sig.self_is_first_param = true; } - self.print_signature(func, true, &sig); + self.print_signature(func, true, &sig, true); self.src.push_str("{ unreachable!() }\n"); } @@ -843,12 +839,18 @@ macro_rules! {macro_name} {{ // } } - fn print_signature(&mut self, func: &Function, params_owned: bool, sig: &FnSig) -> Vec { - let params = self.print_docs_and_params(func, params_owned, sig); + fn print_signature( + &mut self, + func: &Function, + params_owned: bool, + sig: &FnSig, + use_async_sugar: bool, + ) -> Vec { + let params = self.print_docs_and_params(func, params_owned, sig, use_async_sugar); if let FunctionKind::Constructor(_) = &func.kind { self.push_str(" -> Self") } else { - self.print_results(&func.results); + self.print_results(&func.results, sig.async_ && !use_async_sugar); } params } @@ -858,6 +860,7 @@ macro_rules! {macro_name} {{ func: &Function, params_owned: bool, sig: &FnSig, + use_async_sugar: bool, ) -> Vec { self.rustdoc(&func.docs); self.rustdoc_params(&func.params, "Parameters"); @@ -870,7 +873,7 @@ macro_rules! {macro_name} {{ if sig.unsafe_ { self.push_str("unsafe "); } - if sig.async_ { + if sig.async_ && use_async_sugar { self.push_str("async "); } self.push_str("fn "); @@ -960,18 +963,24 @@ macro_rules! {macro_name} {{ params } - fn print_results(&mut self, results: &Results) { + fn print_results(&mut self, results: &Results, async_: bool) { + self.push_str(" -> "); + if async_ { + self.push_str("impl ::core::future::Future {} + 0 => { + self.push_str("()"); + } 1 => { - self.push_str(" -> "); let ty = results.iter_types().next().unwrap(); let mode = self.type_mode_for(ty, TypeOwnershipStyle::Owned, "'INVALID"); assert!(mode.lifetime.is_none()); self.print_ty(ty, mode); } _ => { - self.push_str(" -> ("); + self.push_str("("); for ty in results.iter_types() { let mode = self.type_mode_for(ty, TypeOwnershipStyle::Owned, "'INVALID"); assert!(mode.lifetime.is_none()); @@ -981,6 +990,10 @@ macro_rules! {macro_name} {{ self.push_str(")") } } + + if async_ { + self.push_str("> + 'static"); + } } /// Calculates the `TypeMode` to be used for the `ty` specified. From e907a96fa9e7e2c0048bf41bdc54e37df430985b Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Wed, 27 Mar 2024 13:06:21 -0600 Subject: [PATCH 147/672] emit callback (and no post-return) for async exports Signed-off-by: Joel Dice --- crates/rust/src/async_support.rs | 4 ++++ crates/rust/src/interface.rs | 32 +++++++++++++++++++++++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/crates/rust/src/async_support.rs b/crates/rust/src/async_support.rs index 191b21663..364daa62f 100644 --- a/crates/rust/src/async_support.rs +++ b/crates/rust/src/async_support.rs @@ -43,3 +43,7 @@ pub async unsafe fn await_result( _ => todo!(), } } + +pub fn callback(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + todo!() +} diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index f65b4f27a..90a8c1369 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -599,7 +599,19 @@ macro_rules! {macro_name} {{ self.src.push_str(&String::from(src)); self.src.push_str("}\n"); - if abi::guest_export_needs_post_return(self.resolve, func) { + if async_ { + let callback = self.path_to_callback(); + uwrite!( + self.src, + "\ + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __callback_{name_snake}(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 {{ + {callback}(ctx, event0, event1, event2) + }} + " + ); + } else if abi::guest_export_needs_post_return(self.resolve, func) { uwrite!( self.src, "\ @@ -667,8 +679,18 @@ macro_rules! {macro_name} {{ ); self.push_str("}\n"); - if abi::guest_export_needs_post_return(self.resolve, func) { - let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); + let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); + if async_ { + uwrite!( + self.src, + "\ + #[export_name = \"{export_prefix}[callback]{export_name}\"] + unsafe extern \"C\" fn _callback_{name_snake}(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 {{ + {path_to_self}::__callback_{name_snake}(ctx, event0, event1, event2) + }} + " + ); + } else if abi::guest_export_needs_post_return(self.resolve, func) { uwrite!( self.src, "\ @@ -2013,6 +2035,10 @@ macro_rules! {macro_name} {{ self.path_from_runtime_module(RuntimeItem::AsyncSupport, "first_poll") } + pub fn path_to_callback(&mut self) -> String { + self.path_from_runtime_module(RuntimeItem::AsyncSupport, "callback") + } + fn path_from_runtime_module( &mut self, item: RuntimeItem, From 5e2fbbedd49c35ed81dc399b41614525a99d8b3f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 29 Mar 2024 09:59:51 +0100 Subject: [PATCH 148/672] focus on getting guest support first --- crates/cpp/tests/codegen.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/crates/cpp/tests/codegen.rs b/crates/cpp/tests/codegen.rs index 99e6c6b7b..4474bc5af 100644 --- a/crates/cpp/tests/codegen.rs +++ b/crates/cpp/tests/codegen.rs @@ -18,16 +18,19 @@ macro_rules! codegen_test { }, verify, ); - 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, - ); + 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, + ); + } } }; } From 6fe8412769098b66001ac6b4ef0394193e68f59e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 29 Mar 2024 10:18:21 +0100 Subject: [PATCH 149/672] support more bitcasting ops --- crates/cpp/src/lib.rs | 80 ++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 8edf38e1a..c9be13aa8 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -223,6 +223,41 @@ impl Cpp { 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) + } + } + } } impl WorldGenerator for Cpp { @@ -1988,49 +2023,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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; - match cast { - Bitcast::I32ToF32 | Bitcast::I64ToF32 => { - results - .push(format!("((union {{ int32_t a; float b; }}){{ {} }}).b", op)); - } - Bitcast::F32ToI32 | Bitcast::F32ToI64 => { - results - .push(format!("((union {{ float a; int32_t b; }}){{ {} }}).b", op)); - } - Bitcast::I64ToF64 => { - results.push(format!( - "((union {{ int64_t a; double b; }}){{ {} }}).b", - op - )); - } - Bitcast::F64ToI64 => { - results.push(format!( - "((union {{ double a; int64_t b; }}){{ {} }}).b", - op - )); - } - Bitcast::I32ToI64 => { - results.push(format!("(int64_t) {}", op)); - } - Bitcast::I64ToI32 => { - results.push(format!("(int32_t) {}", op)); - } - Bitcast::None => results.push(op.to_string()), - Bitcast::P64ToI64 => todo!(), - Bitcast::I64ToP64 => todo!(), - Bitcast::P64ToP => todo!(), - Bitcast::PToP64 => todo!(), - Bitcast::I32ToP => todo!(), - Bitcast::PToI32 => todo!(), - Bitcast::PToL => todo!(), - Bitcast::LToP => todo!(), - Bitcast::I32ToL => todo!(), - Bitcast::LToI32 => todo!(), - Bitcast::I64ToL => todo!(), - Bitcast::LToI64 => todo!(), - Bitcast::Sequence(_) => todo!(), - } + // let op = op; + results.push(self.gen.gen.perform_cast(op, cast)); } } abi::Instruction::ConstZero { tys } => { From 831a557771077fae151a7771856810d0502a67cf Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 29 Mar 2024 10:43:04 +0100 Subject: [PATCH 150/672] this namespace correction fixed a lot of test cases --- crates/cpp/src/lib.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index c9be13aa8..7c5325151 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1,4 +1,4 @@ -use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase, *}; +use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase, ToPascalCase}; use std::{ collections::{HashMap, HashSet}, fmt::Write as FmtWrite, @@ -22,6 +22,8 @@ mod wamr; pub const RESOURCE_IMPORT_BASE_CLASS_NAME: &str = "ResourceImportBase"; pub const RESOURCE_EXPORT_BASE_CLASS_NAME: &str = "ResourceExportBase"; pub const OWNED_CLASS_NAME: &str = "Owned"; +// these types are always defined in the non-exports namespace +const NOT_IN_EXPORTED_NAMESPACE: bool = false; type CppType = String; @@ -1407,7 +1409,7 @@ impl CppInterfaceGenerator<'_> { }, Type::Id(id) => match &self.resolve.types[*id].kind { TypeDefKind::Record(_r) => { - self.scoped_type_name(*id, from_namespace, flavor.is_guest_export()) + self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) } TypeDefKind::Resource => { self.scoped_type_name(*id, from_namespace, flavor.is_guest_export()) @@ -1421,7 +1423,7 @@ impl CppInterfaceGenerator<'_> { + ">" } TypeDefKind::Flags(_f) => { - self.scoped_type_name(*id, from_namespace, flavor.is_guest_export()) + 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| { @@ -1434,10 +1436,10 @@ impl CppInterfaceGenerator<'_> { String::from("std::tuple<") + &types + ">" } TypeDefKind::Variant(_v) => { - self.scoped_type_name(*id, from_namespace, flavor.is_guest_export()) + self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) } TypeDefKind::Enum(_e) => { - self.scoped_type_name(*id, from_namespace, flavor.is_guest_export()) + self.scoped_type_name(*id, from_namespace, NOT_IN_EXPORTED_NAMESPACE) } TypeDefKind::Option(o) => { self.gen.dependencies.needs_optional = true; @@ -1546,7 +1548,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner, false); + let namespc = namespace(self.resolve, &ty.owner, NOT_IN_EXPORTED_NAMESPACE); if self.gen.is_first_definition(&namespc, name) { self.gen.h_src.change_namespace(&namespc); Self::docs(&mut self.gen.h_src.src, docs); @@ -1708,7 +1710,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner, false); + let namespc = namespace(self.resolve, &ty.owner, NOT_IN_EXPORTED_NAMESPACE); if self.gen.is_first_definition(&namespc, name) { self.gen.h_src.change_namespace(&namespc); Self::docs(&mut self.gen.h_src.src, docs); @@ -1748,7 +1750,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner, false); + let namespc = namespace(self.resolve, &ty.owner, NOT_IN_EXPORTED_NAMESPACE); self.gen.h_src.change_namespace(&namespc); Self::docs(&mut self.gen.h_src.src, docs); let pascal = name.to_pascal_case(); @@ -1801,7 +1803,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner, false); + let namespc = namespace(self.resolve, &ty.owner, NOT_IN_EXPORTED_NAMESPACE); if self.gen.is_first_definition(&namespc, name) { self.gen.h_src.change_namespace(&namespc); let pascal = name.to_pascal_case(); @@ -1828,7 +1830,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner, false); + let namespc = namespace(self.resolve, &ty.owner, NOT_IN_EXPORTED_NAMESPACE); self.gen.h_src.change_namespace(&namespc); let pascal = name.to_pascal_case(); Self::docs(&mut self.gen.h_src.src, docs); From b5c776b98b9922a1368de7b2bd4ac8630924a83c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 29 Mar 2024 12:03:59 +0100 Subject: [PATCH 151/672] aim for a lower bar on lists --- crates/csharp/tests/codegen.rs | 1 + tests/codegen/trivial-lists.wit | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/codegen/trivial-lists.wit diff --git a/crates/csharp/tests/codegen.rs b/crates/csharp/tests/codegen.rs index eee3b23c2..d66c5daf2 100644 --- a/crates/csharp/tests/codegen.rs +++ b/crates/csharp/tests/codegen.rs @@ -53,6 +53,7 @@ macro_rules! codegen_test { "simple-http", "simple-lists", "small-anonymous", + "trivial-lists", "unused-import", "use-across-interfaces", "variants", 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); + trivial-list2: func() -> list; + trivial-list3: func(a: list, b: list) -> list; +} + +world my-world { + import trivial-lists; + export trivial-lists; +} From 82ccbc08888e79101e322631fefc2d0874edada7 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 29 Mar 2024 12:27:47 +0100 Subject: [PATCH 152/672] trivial lists compile --- crates/cpp/src/lib.rs | 10 ++++---- crates/cpp/tests/codegen.rs | 47 +++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7c5325151..6bbd36634 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1,4 +1,4 @@ -use heck::{ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase, ToPascalCase}; +use heck::{ToPascalCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase}; use std::{ collections::{HashMap, HashSet}, fmt::Write as FmtWrite, @@ -2892,12 +2892,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let tmp = self.tmp(); let ptr = self.tempname("ptr", tmp); let len = self.tempname("len", tmp); - uwriteln!(self.src, "int32_t {ptr} = {};", operands[0]); - uwriteln!(self.src, "int32_t {len} = {};", operands[1]); + 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 (int32_t {i} = 0; {i} < {len}; {i}++) {{"); + uwriteln!(self.src, "for (size_t {i} = 0; {i} < {len}; {i}++) {{"); let size = self.gen.sizes.size(element); - uwriteln!(self.src, "int32_t base = {ptr} + {i} * {size};"); + uwriteln!(self.src, "uint8_t* base = {ptr} + {i} * {size};"); uwriteln!(self.src, "(void) base;"); uwrite!(self.src, "{body}"); uwriteln!(self.src, "}}"); diff --git a/crates/cpp/tests/codegen.rs b/crates/cpp/tests/codegen.rs index 4474bc5af..e759f10ee 100644 --- a/crates/cpp/tests/codegen.rs +++ b/crates/cpp/tests/codegen.rs @@ -7,6 +7,53 @@ macro_rules! codegen_test { ($id:ident $name:tt $test:tt) => { #[test] fn $id() { + if [ + "go_params", + "guest-name", + "import-and-export-resource", + "import-and-export-resource-alias", + "issue544", + "issue551", + "issue573", + "issue607", + "issue668", + "just-export", + "keywords", + "lift-lower-foreign", + "lists", + "many-arguments", + "multi-return", + "multiversion", + "option-result", + "records", + "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", + "small-anonymous", + "unused-import", + "use-across-interfaces", + "variants", + "variants-unioning-types", + "worlds-with-types", + "zero-size-tuple", + ] + .contains(&$name) + { + return; + } test_helpers::run_world_codegen_test( "cpp", $test.as_ref(), From 8bca8a465428087ff591c13e7768ce82a4e415c5 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 31 Mar 2024 11:22:50 +0200 Subject: [PATCH 153/672] more tests pass now --- crates/cpp/tests/codegen.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/crates/cpp/tests/codegen.rs b/crates/cpp/tests/codegen.rs index e759f10ee..d56610078 100644 --- a/crates/cpp/tests/codegen.rs +++ b/crates/cpp/tests/codegen.rs @@ -12,20 +12,16 @@ macro_rules! codegen_test { "guest-name", "import-and-export-resource", "import-and-export-resource-alias", - "issue544", "issue551", "issue573", "issue607", "issue668", - "just-export", "keywords", "lift-lower-foreign", "lists", - "many-arguments", "multi-return", "multiversion", "option-result", - "records", "resource-alias", "resource-borrow-in-record", "resource-borrow-in-record-export", @@ -42,8 +38,6 @@ macro_rules! codegen_test { "same-names5", "simple-http", "simple-lists", - "small-anonymous", - "unused-import", "use-across-interfaces", "variants", "variants-unioning-types", @@ -52,7 +46,10 @@ macro_rules! codegen_test { ] .contains(&$name) { - return; + let test_all_code = env::var_os("CPP_ALL_TESTS").is_some(); + if !test_all_code { + return; + } } test_helpers::run_world_codegen_test( "cpp", From 110efa4e892b8c42e25c095d4d47b1e14e58ca3f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 31 Mar 2024 11:37:28 +0200 Subject: [PATCH 154/672] code generation still works fine for strings example --- crates/cpp/tests/native_resources/.gitignore | 1 + crates/cpp/tests/native_resources/Makefile | 9 ------ .../tests/native_resources/guest/.gitignore | 1 + crates/cpp/tests/native_strings/Makefile | 1 + .../native_strings/rust/src/the_world.rs | 29 ++++++++++++++----- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/crates/cpp/tests/native_resources/.gitignore b/crates/cpp/tests/native_resources/.gitignore index 0830c813c..4fc762d50 100644 --- a/crates/cpp/tests/native_resources/.gitignore +++ b/crates/cpp/tests/native_resources/.gitignore @@ -1,3 +1,4 @@ /*.o /*.so /app-resources +/*.template diff --git a/crates/cpp/tests/native_resources/Makefile b/crates/cpp/tests/native_resources/Makefile index d50e678da..db79f4eb3 100644 --- a/crates/cpp/tests/native_resources/Makefile +++ b/crates/cpp/tests/native_resources/Makefile @@ -3,12 +3,6 @@ WIT_BINDGEN=../../../../target/debug/wit-bindgen all: libresources.so app-resources -#libresources.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-resources: the_world_native.o main.o $(CXX) $(CXXFLAGS) -o $@ $^ -L. -lresources @@ -16,9 +10,6 @@ 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 - -# guest.wasm: the_world.cpp guest.cpp -# /opt/wasi-sdk/bin/clang++ -o $@ $^ $(CXXFLAGS) clean: -rm *.o app-resources diff --git a/crates/cpp/tests/native_resources/guest/.gitignore b/crates/cpp/tests/native_resources/guest/.gitignore index 925b4b450..d6ec319d2 100644 --- a/crates/cpp/tests/native_resources/guest/.gitignore +++ b/crates/cpp/tests/native_resources/guest/.gitignore @@ -1,2 +1,3 @@ /*.o /*.so +/*.template diff --git a/crates/cpp/tests/native_strings/Makefile b/crates/cpp/tests/native_strings/Makefile index 74a0a894e..832fe9925 100644 --- a/crates/cpp/tests/native_strings/Makefile +++ b/crates/cpp/tests/native_strings/Makefile @@ -15,6 +15,7 @@ app-strings: the_world_native.o main.o 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) diff --git a/crates/cpp/tests/native_strings/rust/src/the_world.rs b/crates/cpp/tests/native_strings/rust/src/the_world.rs index 84964deaa..db636d098 100644 --- a/crates/cpp/tests/native_strings/rust/src/the_world.rs +++ b/crates/cpp/tests/native_strings/rust/src/the_world.rs @@ -1,8 +1,10 @@ -// Generated by `wit-bindgen` 0.22.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.23.0. DO NOT EDIT! // Options used: +#[allow(dead_code)] pub mod foo { + #[allow(dead_code)] pub mod foo { - #[allow(clippy::all)] + #[allow(dead_code, clippy::all)] pub mod strings { #[used] #[doc(hidden)] @@ -75,10 +77,13 @@ pub mod foo { } } +#[allow(dead_code)] pub mod exports { + #[allow(dead_code)] pub mod foo { + #[allow(dead_code)] pub mod foo { - #[allow(clippy::all)] + #[allow(dead_code, clippy::all)] pub mod strings { #[used] #[doc(hidden)] @@ -87,13 +92,15 @@ pub mod exports { use super::super::super::super::_rt; #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_a_cabi(arg0: *mut u8,arg1: usize,) {let len0 = arg1; + pub unsafe fn _export_a_cabi(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() -> *mut u8 {let result0 = T::b(); + pub unsafe fn _export_b_cabi() -> *mut u8 {#[cfg(target_arch="wasm32")] + _rt::run_ctors_once();let result0 = T::b(); let ptr1 = _RET_AREA.0.as_mut_ptr().cast::(); let vec2 = (result0.into_bytes()).into_boxed_slice(); let ptr2 = vec2.as_ptr().cast::(); @@ -112,7 +119,8 @@ pub mod exports { } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_c_cabi(arg0: *mut u8,arg1: usize,arg2: *mut u8,arg3: usize,) -> *mut u8 {let len0 = arg1; + pub unsafe fn _export_c_cabi(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); @@ -191,6 +199,11 @@ mod _rt { 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; @@ -231,7 +244,7 @@ macro_rules! __export_the_world_impl { pub(crate) use __export_the_world_impl as export; #[cfg(target_arch = "wasm32")] -#[link_section = "component-type:wit-bindgen:0.22.0:the-world:encoded world"] +#[link_section = "component-type:wit-bindgen:0.23.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\ @@ -240,7 +253,7 @@ A\x04\x01B\x06\x01@\x01\x01xs\x01\0\x04\0\x01a\x01\0\x01@\0\0s\x04\0\x01b\x01\x0 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.201.0\x10wit-bindgen-rust\x060.22.0"; +processed-by\x02\x0dwit-component\x070.202.0\x10wit-bindgen-rust\x060.23.0"; #[inline(never)] #[doc(hidden)] From 6f220d96e0450d3b5765f4543bdb49fa6c885be7 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 31 Mar 2024 11:42:34 +0200 Subject: [PATCH 155/672] document remaining incompatibilities in Rust for native --- .../native_resources/rust/src/the_world.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/crates/cpp/tests/native_resources/rust/src/the_world.rs b/crates/cpp/tests/native_resources/rust/src/the_world.rs index 3ce45e9dc..05ed53064 100644 --- a/crates/cpp/tests/native_resources/rust/src/the_world.rs +++ b/crates/cpp/tests/native_resources/rust/src/the_world.rs @@ -1,8 +1,10 @@ -// Generated by `wit-bindgen` 0.22.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.23.0. DO NOT EDIT! // Options used: +#[allow(dead_code)] pub mod foo { + #[allow(dead_code)] pub mod foo { - #[allow(clippy::all)] + #[allow(dead_code, clippy::all)] pub mod resources { #[used] #[doc(hidden)] @@ -122,10 +124,13 @@ pub mod foo { } } +#[allow(dead_code)] pub mod exports { + #[allow(dead_code)] pub mod foo { + #[allow(dead_code)] pub mod foo { - #[allow(clippy::all)] + #[allow(dead_code, clippy::all)] pub mod resources { #[used] #[doc(hidden)] @@ -217,7 +222,7 @@ pub mod exports { fn as_ptr(&self) -> *mut _RRep { R::type_guard::(); - unsafe { T::_resource_rep(self.handle()).cast() } + T::_resource_rep(self.handle()).cast() } } @@ -596,7 +601,7 @@ macro_rules! __export_the_world_impl { pub(crate) use __export_the_world_impl as export; #[cfg(target_arch = "wasm32")] -#[link_section = "component-type:wit-bindgen:0.22.0:the-world:encoded world"] +#[link_section = "component-type:wit-bindgen:0.23.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\ @@ -610,7 +615,7 @@ 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\ -1.0\x10wit-bindgen-rust\x060.22.0"; +2.0\x10wit-bindgen-rust\x060.23.0"; #[inline(never)] #[doc(hidden)] From 4f6082d6392f5922c8a1126a9b0685966a07bf58 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 31 Mar 2024 23:22:38 +0200 Subject: [PATCH 156/672] fix constructor (guest side) --- crates/cpp/src/lib.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 6bbd36634..e579c59b5 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2287,16 +2287,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { .. } => { let op = &operands[0]; - // let namespace = namespace(self.gen.resolve, &self.gen.resolve.types[*ty].owner); - // let mut code = String::default(); - // for n in namespace { - // code.push_str(&n); - // code.push_str("::"); - // } if self.gen.gen.opts.host_side() { results.push(format!("{op}.store_resource(std::move({op}))")); } else { - results.push(format!("{op}->handle")); + results.push(format!("{op}.into_handle()")); } } abi::Instruction::HandleLower { @@ -2803,16 +2797,24 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } self.push_str(&operands.join(", ")); - self.push_str(");\n"); + if matches!(func.kind, FunctionKind::Constructor(_)) + && !self.gen.gen.opts.host_side() + { + // acquire object from unique_ptr + self.push_str(").release();"); + results[0] = format!("(*({}))", results[0]); + } else { + self.push_str(");\n"); + } } abi::Instruction::Return { amt, func } => { - let import = matches!(self.variant, AbiVariant::GuestImport); + let guest_import = matches!(self.variant, AbiVariant::GuestImport); match amt { 0 => {} _ => { assert!(*amt == operands.len()); match &func.kind { - FunctionKind::Constructor(_) if import => { + FunctionKind::Constructor(_) if guest_import => { // strange but works self.src.push_str("this->handle = "); } From 15b5ec2e224ca7cf67016fec4de849cfc106a7ca Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 1 Apr 2024 00:12:57 +0200 Subject: [PATCH 157/672] more correct im/export in direct code --- crates/cpp/src/lib.rs | 29 ++++++++++++------- .../exports-foo-foo-resources-R.h | 20 ------------- 2 files changed, 19 insertions(+), 30 deletions(-) delete mode 100644 crates/cpp/tests/native_resources/exports-foo-foo-resources-R.h diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index e579c59b5..dde0a5a8e 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -177,7 +177,7 @@ impl Cpp { &'a mut self, resolve: &'a Resolve, name: Option<&'a WorldKey>, - in_import: bool, + in_guest_import: bool, wasm_import_module: Option, ) -> CppInterfaceGenerator<'a> { let mut sizes = SizeAlign::new(if self.opts.wasm64 { @@ -195,7 +195,7 @@ impl Cpp { _name: name, sizes, // public_anonymous_types: BTreeSet::new(), - in_import, + in_guest_import, // export_funcs: Vec::new(), // return_pointer_area_size: 0, // return_pointer_area_align: 0, @@ -670,7 +670,7 @@ struct CppInterfaceGenerator<'a> { interface: Option, _name: Option<&'a WorldKey>, sizes: SizeAlign, - in_import: bool, + in_guest_import: bool, // return_pointer_area_size: usize, // return_pointer_area_align: usize, pub wasm_import_module: Option, @@ -1573,7 +1573,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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); + let definition = !(guest_import ^ self.gen.opts.host_side()); let mut world_name = self.gen.world.to_snake_case(); world_name.push_str("::"); let mut headerfile = SourceWithState::default(); @@ -1601,10 +1601,15 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } self.gen.dependencies.needs_wit = true; - let base_type = if definition { - format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}<{pascal}>") - } else { - String::from_str("wit::").unwrap() + RESOURCE_IMPORT_BASE_CLASS_NAME + 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"); @@ -2013,7 +2018,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { match inst { abi::Instruction::GetArg { nth } => { if *nth == 0 && self.params[0].as_str() == "self" { - if self.gen.in_import ^ self.gen.gen.opts.host { + if self.gen.in_guest_import ^ self.gen.gen.opts.host { results.push("(*this)".to_string()); } else { results.push("(*lookup_resource(self))".to_string()); @@ -2947,7 +2952,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 8 => "uint64_t", _ => todo!(), }; - let static_var = if self.gen.in_import { "" } else { "static " }; + let static_var = if self.gen.in_guest_import { + "" + } else { + "static " + }; uwriteln!(self.src, "{static_var}{tp} ret_area[{elems}];"); uwriteln!( self.src, diff --git a/crates/cpp/tests/native_resources/exports-foo-foo-resources-R.h b/crates/cpp/tests/native_resources/exports-foo-foo-resources-R.h deleted file mode 100644 index fbc8f229e..000000000 --- a/crates/cpp/tests/native_resources/exports-foo-foo-resources-R.h +++ /dev/null @@ -1,20 +0,0 @@ -/* User class definition file, autogenerated once, then user modified - * Updated versions of this file are generated into - * exports-foo-foo-resources-R.h.template. - */ -namespace exports { -namespace foo { -namespace foo { -namespace resources { -class R : public wit::ResourceExportBase { - -public: - static void Dtor(R *self) { delete self; }; - R(uint32_t a); - void Add(uint32_t b) const; -}; - -} // namespace resources -} // namespace foo -} // namespace foo -} // namespace exports From c585ae2f29dd4a16ae55ed3e16e9cf1095475468 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 1 Apr 2024 00:25:22 +0200 Subject: [PATCH 158/672] minor fix: correct ctor arg --- crates/cpp/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index dde0a5a8e..d25048047 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1667,7 +1667,8 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> // consuming constructor from handle (bindings) uwriteln!( self.gen.h_src.src, - "{pascal}(wit::{RESOURCE_IMPORT_BASE_CLASS_NAME}&&);\n" + "{pascal}({base_type});\n", + // if self.gen.opts.host_side() {RESOURCE_EXPORT_BASE_CLASS_NAME} else {RESOURCE_IMPORT_BASE_CLASS_NAME} ); uwriteln!(self.gen.h_src.src, "{pascal}({pascal}&&) = default;\n"); } else { From db1393f1c8af0a47dad41446fc93efe486a64aac Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 2 Apr 2024 22:43:10 +0200 Subject: [PATCH 159/672] fix compilation with latest changes --- crates/cpp/src/lib.rs | 8 ++++---- crates/rust/src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index d25048047..8cb86449c 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2089,8 +2089,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { | abi::Instruction::I32FromU32 | abi::Instruction::I32FromS32 => top_as("int32_t"), abi::Instruction::I64FromU64 | abi::Instruction::I64FromS64 => top_as("int64_t"), - abi::Instruction::F32FromFloat32 => top_as("float"), - abi::Instruction::F64FromFloat64 => top_as("double"), + 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"), @@ -2100,8 +2100,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::S64FromI64 => top_as("int64_t"), abi::Instruction::U64FromI64 => top_as("uint64_t"), abi::Instruction::CharFromI32 => top_as("uint32_t"), - abi::Instruction::Float32FromF32 => top_as("float"), - abi::Instruction::Float64FromF64 => top_as("double"), + 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(); diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 635cd51dd..1bbe40d34 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -186,7 +186,7 @@ pub struct Opts { /// 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, From 8ea51c128d6442d298b720523d00aee3d4163069 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 2 Apr 2024 22:48:32 +0200 Subject: [PATCH 160/672] fix test --- crates/cpp/tests/codegen.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/cpp/tests/codegen.rs b/crates/cpp/tests/codegen.rs index d56610078..d10cd291d 100644 --- a/crates/cpp/tests/codegen.rs +++ b/crates/cpp/tests/codegen.rs @@ -12,6 +12,7 @@ macro_rules! codegen_test { "guest-name", "import-and-export-resource", "import-and-export-resource-alias", + "interface-has-go-keyword", "issue551", "issue573", "issue607", @@ -22,6 +23,7 @@ macro_rules! codegen_test { "multi-return", "multiversion", "option-result", + "record-has-go-keyword-and-used-in-fn", "resource-alias", "resource-borrow-in-record", "resource-borrow-in-record-export", From 62b8de1783c31410b9532efa2017064ad41437a3 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 2 Apr 2024 23:22:07 +0200 Subject: [PATCH 161/672] generic fix --- crates/cpp/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 8cb86449c..67eeae8b8 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1667,10 +1667,11 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> // consuming constructor from handle (bindings) uwriteln!( self.gen.h_src.src, - "{pascal}({base_type});\n", + "{pascal}({base_type} &&);", // if self.gen.opts.host_side() {RESOURCE_EXPORT_BASE_CLASS_NAME} else {RESOURCE_IMPORT_BASE_CLASS_NAME} ); - uwriteln!(self.gen.h_src.src, "{pascal}({pascal}&&) = default;\n"); + uwriteln!(self.gen.h_src.src, "{pascal}({pascal}&&) = default;"); + uwriteln!(self.gen.h_src.src, "{pascal}& operator=({pascal}&&) = default;"); } else { if !self.gen.opts.host_side() { let func = Function { From 32c586478d9019ded131e5cfe145dc77e545380c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 2 Apr 2024 23:37:20 +0200 Subject: [PATCH 162/672] define move ctor --- crates/cpp/src/lib.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 67eeae8b8..7dfcf5138 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1665,13 +1665,17 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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}({base_type} &&);", - // if self.gen.opts.host_side() {RESOURCE_EXPORT_BASE_CLASS_NAME} else {RESOURCE_IMPORT_BASE_CLASS_NAME} + "{pascal}& operator=({pascal}&&) = default;" + ); + self.gen.c_src.qualify(&namespc); + uwriteln!( + self.gen.c_src.src, + "::{pascal}({base_type}&&b) : {base_type}(std::move(b)) {{}}" ); - uwriteln!(self.gen.h_src.src, "{pascal}({pascal}&&) = default;"); - uwriteln!(self.gen.h_src.src, "{pascal}& operator=({pascal}&&) = default;"); } else { if !self.gen.opts.host_side() { let func = Function { From c228085c462ef0dbf718e08f7e58c029bac64cdd Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 2 Apr 2024 23:41:47 +0200 Subject: [PATCH 163/672] proper copy ctor --- crates/cpp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7dfcf5138..5f1d9d69d 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1674,7 +1674,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> self.gen.c_src.qualify(&namespc); uwriteln!( self.gen.c_src.src, - "::{pascal}({base_type}&&b) : {base_type}(std::move(b)) {{}}" + "{pascal}::{pascal}({base_type}&&b) : {base_type}(std::move(b)) {{}}" ); } else { if !self.gen.opts.host_side() { From d627e568740a5c2734b0b5fc3fefb376de278adf Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 2 Apr 2024 23:54:08 +0200 Subject: [PATCH 164/672] proper dtor handling --- crates/cpp/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 5f1d9d69d..bd6a5f46c 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1174,6 +1174,7 @@ impl CppInterfaceGenerator<'_> { }, SpecialMethod::Dtor => { let classname = class_namespace(self, func, variant).join("::"); + uwriteln!(self.gen.c_src.src, "(({classname}*)arg0)->handle=-1;"); uwriteln!(self.gen.c_src.src, "{0}::Dtor(({0}*)arg0);", classname); } SpecialMethod::ResourceNew => { @@ -3011,7 +3012,6 @@ enum SpecialMethod { ResourceNew, // [export][resource-new] Dtor, // [dtor] (guest export only) Allocate, // internal: allocate new object (called from generated code) - // Deallocate, // internal: de-allocate object - now Dtor } fn is_special_method(func: &Function) -> SpecialMethod { @@ -3024,8 +3024,6 @@ fn is_special_method(func: &Function) -> SpecialMethod { SpecialMethod::Dtor } else if func.name == "$alloc" { SpecialMethod::Allocate - // } else if func.name == "$dealloc" { - // SpecialMethod::Deallocate } else { SpecialMethod::None } From c1fc7e27c82d529b2bd14350718432a9449bd721 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 5 Apr 2024 22:54:06 +0200 Subject: [PATCH 165/672] fix native Rust code generation --- .../tests/native_resources/rust/Cargo.lock | 56 +++++++++---------- .../native_resources/rust/src/the_world.rs | 38 ++++++------- crates/rust/src/interface.rs | 40 ++++--------- 3 files changed, 55 insertions(+), 79 deletions(-) diff --git a/crates/cpp/tests/native_resources/rust/Cargo.lock b/crates/cpp/tests/native_resources/rust/Cargo.lock index fcfcb721c..5e8e19daf 100644 --- a/crates/cpp/tests/native_resources/rust/Cargo.lock +++ b/crates/cpp/tests/native_resources/rust/Cargo.lock @@ -184,18 +184,18 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "wasm-encoder" -version = "0.201.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" +checksum = "bfd106365a7f5f7aa3c1916a98cbb3ad477f5ff96ddb130285a91c6e7429e67a" dependencies = [ "leb128", ] [[package]] name = "wasm-metadata" -version = "0.201.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fd83062c17b9f4985d438603cde0a5e8c5c8198201a6937f778b607924c7da2" +checksum = "094aea3cb90e09f16ee25a4c0e324b3e8c934e7fd838bfa039aef5352f44a917" dependencies = [ "anyhow", "indexmap", @@ -204,24 +204,14 @@ dependencies = [ "serde_json", "spdx", "wasm-encoder", - "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser", ] [[package]] name = "wasmparser" -version = "0.201.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84e5df6dba6c0d7fafc63a450f1738451ed7a0b52295d83e868218fa286bf708" -dependencies = [ - "bitflags", - "indexmap", - "semver", -] - -[[package]] -name = "wasmparser" -version = "0.201.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#9c01485c2713d926e8f5b67b3e6f62f792da8ba7" +checksum = "d6998515d3cf3f8b980ef7c11b29a9b1017d4cf86b99ae93b546992df9931413" dependencies = [ "bitflags", "indexmap", @@ -230,16 +220,15 @@ dependencies = [ [[package]] name = "wit-bindgen" -version = "0.22.0" +version = "0.24.0" dependencies = [ - "bitflags", "wit-bindgen-rt", "wit-bindgen-rust-macro", ] [[package]] name = "wit-bindgen-core" -version = "0.22.0" +version = "0.24.0" dependencies = [ "anyhow", "wit-parser", @@ -247,11 +236,14 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" -version = "0.22.0" +version = "0.24.0" +dependencies = [ + "bitflags", +] [[package]] name = "wit-bindgen-rust" -version = "0.22.0" +version = "0.24.0" dependencies = [ "anyhow", "heck", @@ -263,7 +255,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" -version = "0.22.0" +version = "0.24.0" dependencies = [ "anyhow", "proc-macro2", @@ -275,9 +267,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.201.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "421c0c848a0660a8c22e2fd217929a0191f14476b68962afd2af89fd22e39825" +checksum = "0c836b1fd9932de0431c1758d8be08212071b6bba0151f7bac826dbc4312a2a9" dependencies = [ "anyhow", "bitflags", @@ -288,14 +280,15 @@ dependencies = [ "serde_json", "wasm-encoder", "wasm-metadata", - "wasmparser 0.201.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.201.0" -source = "git+https://github.com/bytecodealliance/wasm-tools#9c01485c2713d926e8f5b67b3e6f62f792da8ba7" +version = "0.202.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "744237b488352f4f27bca05a10acb79474415951c450e52ebd0da784c1df2bcc" dependencies = [ "anyhow", "id-arena", @@ -306,5 +299,10 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.201.0 (git+https://github.com/bytecodealliance/wasm-tools)", + "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/src/the_world.rs b/crates/cpp/tests/native_resources/rust/src/the_world.rs index 05ed53064..c80b4a3a4 100644 --- a/crates/cpp/tests/native_resources/rust/src/the_world.rs +++ b/crates/cpp/tests/native_resources/rust/src/the_world.rs @@ -1,4 +1,4 @@ -// Generated by `wit-bindgen` 0.23.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.24.0. DO NOT EDIT! // Options used: #[allow(dead_code)] pub mod foo { @@ -294,7 +294,7 @@ pub mod exports { } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_borrows_cabi(arg0: *mut u8,) {#[cfg(target_arch="wasm32")] + pub unsafe fn _export_borrows_cabi(arg0: *const u8,) {#[cfg(target_arch="wasm32")] _rt::run_ctors_once();T::borrows(RBorrow::lift(arg0 as usize)); } #[doc(hidden)] @@ -314,29 +314,25 @@ pub trait GuestR: 'static { 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) + #[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) - } + #[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) } } @@ -366,7 +362,7 @@ macro_rules! __export_foo_foo_resources_cabi{ } #[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: *mut u8,) { + 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")] @@ -601,7 +597,7 @@ macro_rules! __export_the_world_impl { pub(crate) use __export_the_world_impl as export; #[cfg(target_arch = "wasm32")] -#[link_section = "component-type:wit-bindgen:0.23.0:the-world:encoded world"] +#[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\ @@ -615,7 +611,7 @@ 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.23.0"; +2.0\x10wit-bindgen-rust\x060.24.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 39b60dc96..0c31b77f7 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -218,43 +218,25 @@ impl InterfaceGenerator<'_> { unsafe fn _resource_new(val: *mut u8) -> u32 where Self: Sized {{ - #[cfg(not(target_arch = "wasm32"))] - {{ - let _ = val; - unreachable!(); - }} - - #[cfg(target_arch = "wasm32")] - {{ - #[link(wasm_import_module = "[export]{module}")] - extern "C" {{ - #[cfg_attr(target_arch = "wasm32", link_name = "[resource-new]{resource_name}")] - fn {external_new}(_: *mut u8) -> u32; - }} - {external_new}(val) + #[link(wasm_import_module = "[export]{module}")] + extern "C" {{ + #[cfg_attr(target_arch = "wasm32", link_name = "[resource-new]{resource_name}")] + fn {external_new}(_: *mut u8) -> u32; }} + {external_new}(val) }} #[doc(hidden)] fn _resource_rep(handle: u32) -> *mut u8 where Self: Sized {{ - #[cfg(not(target_arch = "wasm32"))] - {{ - let _ = handle; - unreachable!(); + #[link(wasm_import_module = "[export]{module}")] + extern "C" {{ + #[cfg_attr(target_arch = "wasm32", link_name = "[resource-rep]{resource_name}")] + fn {external_rep}(_: u32) -> *mut u8; }} - - #[cfg(target_arch = "wasm32")] - {{ - #[link(wasm_import_module = "[export]{module}")] - extern "C" {{ - #[cfg_attr(target_arch = "wasm32", link_name = "[resource-rep]{resource_name}")] - fn {external_rep}(_: u32) -> *mut u8; - }} - unsafe {{ - {external_rep}(handle) - }} + unsafe {{ + {external_rep}(handle) }} }} From b9766d56933f3965a5b5409ce69b7e28e8085454 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Tue, 9 Apr 2024 17:06:41 -0600 Subject: [PATCH 166/672] flesh out async support Signed-off-by: Joel Dice --- crates/core/src/abi.rs | 5 +- crates/rust/src/async_support.rs | 85 +++++++++++++++++++++++++------- crates/rust/src/bindgen.rs | 45 +++++++++++------ 3 files changed, 97 insertions(+), 38 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index d3c232a28..4533b57e7 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -531,7 +531,7 @@ def_instruction! { results: &'a [WasmType] } : [params.len()] => [results.len()], - AsyncPostCallInterface { func: &'a Function } : [1] => [func.results.len()], + AsyncPostCallInterface { func: &'a Function } : [1] => [func.results.len() + 1], AsyncCallReturn { name: &'a str, params: &'a [WasmType] } : [params.len()] => [0], } @@ -1102,9 +1102,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { results }, }); - self.emit(&Instruction::ConstZero { - tys: &[WasmType::Pointer], - }); self.emit(&Instruction::Return { func, amt: 1 }); } else { self.emit(&Instruction::Return { diff --git a/crates/rust/src/async_support.rs b/crates/rust/src/async_support.rs index 364daa62f..990979b7f 100644 --- a/crates/rust/src/async_support.rs +++ b/crates/rust/src/async_support.rs @@ -1,34 +1,50 @@ -use std::{ - alloc::Layout, - future::Future, - pin::pin, - sync::Arc, - task::{Context, Poll, Wake}, +use { + futures::{channel::oneshot, future::FutureExt}, + once_cell::sync::Lazy, + std::{ + alloc::Layout, + collections::HashMap, + future::Future, + pin::{pin, Pin}, + ptr, + sync::Arc, + task::{Context, Poll, Wake, Waker}, + }, }; -pub fn first_poll(future: impl Future + 'static) -> Result { +type BoxFuture = Pin + 'static>>; + +struct FutureState(BoxFuture); + +static mut CALLS: Lazy>> = Lazy::new(HashMap::new); + +fn dummy_waker() -> Waker { struct DummyWaker; impl Wake for DummyWaker { fn wake(self: Arc) {} } - let mut future = pin!(future); + static WAKER: Lazy> = Lazy::new(|| Arc::new(DummyWaker)); + + WAKER.clone().into() +} + +pub fn first_poll( + future: impl Future + 'static, + fun: impl FnOnce(T) + 'static, +) -> *mut u8 { + let mut future = Box::pin(future.map(fun)) as BoxFuture; match future .as_mut() - .poll(&mut Context::from_waker(&Arc::new(DummyWaker).into())) + .poll(&mut Context::from_waker(&dummy_waker())) { - Poll::Ready(result) => Ok(result), - Poll::Pending => todo!(), + Poll::Ready(()) => ptr::null_mut(), + Poll::Pending => Box::into_raw(Box::new(FutureState(future))) as _, } } -const STATUS_NOT_STARTED: i32 = 0; -const STATUS_PARAMS_READ: i32 = 1; -const STATUS_RESULTS_WRITTEN: i32 = 2; -const STATUS_DONE: i32 = 3; - pub async unsafe fn await_result( import: unsafe extern "C" fn(*mut u8, *mut u8, *mut u8) -> i32, params_layout: Layout, @@ -36,7 +52,18 @@ pub async unsafe fn await_result( results: *mut u8, call: *mut u8, ) { + const STATUS_NOT_STARTED: i32 = 0; + const STATUS_PARAMS_READ: i32 = 1; + const STATUS_RESULTS_WRITTEN: i32 = 2; + const STATUS_DONE: i32 = 3; + match import(params, results, call) { + STATUS_PARAMS_READ => { + alloc::dealloc(params, params_layout); + let (tx, rx) = oneshot::channel(); + CALLS.insert(*call.cast::(), tx); + rx.await.unwrap() + } STATUS_DONE => { alloc::dealloc(params, params_layout); } @@ -44,6 +71,28 @@ pub async unsafe fn await_result( } } -pub fn callback(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { - todo!() +pub unsafe fn callback(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + const EVENT_CALL_STARTED: i32 = 0; + const EVENT_CALL_RETURNED: i32 = 1; + const EVENT_CALL_DONE: i32 = 2; + + match event0 { + EVENT_CALL_DONE => { + CALLS.remove(&event1).unwrap().send(()); + + match (*(ctx as *mut FutureState)) + .0 + .as_mut() + .poll(&mut Context::from_waker(&dummy_waker())) + { + Poll::Ready(()) => { + // TODO: consider spawned task before returning "done" here + drop(Box::from_raw(ctx as *mut FutureState)); + 1 + } + Poll::Pending => 0, + } + } + _ => todo!(), + } } diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 76c0b284d..f4d901a56 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -885,21 +885,6 @@ impl Bindgen for FunctionBindgen<'_, '_> { results.push(ptr); } - Instruction::AsyncPostCallInterface { func } => { - let result = &operands[0]; - self.let_results(func.results.len(), results); - let first_poll = self.gen.path_to_first_poll(); - uwriteln!( - self.src, - "\ - match {first_poll}({result}) {{ - Ok(results) => results, - Err(ctx) => return ctx, - }};\ - " - ); - } - Instruction::AsyncCallStart { name, params, @@ -915,10 +900,38 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!(self.src, "{func}({});", operands.join(", ")); } + Instruction::AsyncPostCallInterface { func } => { + let result = &operands[0]; + results.push("result".into()); + let params = (0..func.results.len()) + .map(|_| { + let tmp = self.tmp(); + let param = format!("result{}", tmp); + results.push(param.clone()); + param + }) + .collect::>() + .join(", "); + let first_poll = self.gen.path_to_first_poll(); + uwriteln!( + self.src, + "\ + let result = {first_poll}({result}, |{params}| {{ + " + ); + } + Instruction::AsyncCallReturn { name, params } => { let func = self.declare_import(self.gen.wasm_import_module, name, params, &[]); - uwriteln!(self.src, "{func}({});", operands.join(", ")); + uwriteln!( + self.src, + "\ + {func}({}); + }}); + ", + operands.join(", ") + ); } Instruction::Return { amt, .. } => { From ab6e34d57b80b48d8deac09924c87e69b42a9e64 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 11 Apr 2024 00:21:57 +0200 Subject: [PATCH 167/672] fix generated resource data types in guest --- crates/cpp/src/lib.rs | 10 +++++++++- .../cpp/tests/native_resources/guest/the_world_cpp.h | 3 +-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index bd6a5f46c..5cffed9e7 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1416,7 +1416,15 @@ impl CppInterfaceGenerator<'_> { self.scoped_type_name(*id, from_namespace, flavor.is_guest_export()) } TypeDefKind::Handle(Handle::Own(id)) => { - self.type_name(&Type::Id(*id), from_namespace, flavor) + let mut typename = self.type_name(&Type::Id(*id), from_namespace, flavor); + match flavor { + Flavor::Argument(AbiVariant::GuestImport) => typename.push_str("&&"), + Flavor::Argument(AbiVariant::GuestExport) + | Flavor::Result(AbiVariant::GuestExport) => typename.push_str("::Owned"), + Flavor::Result(AbiVariant::GuestImport) => (), + Flavor::InStruct => (), + } + typename } TypeDefKind::Handle(Handle::Borrow(id)) => { "std::reference_wrapper #include #include -#include #include namespace foo { namespace foo { @@ -17,8 +16,8 @@ class R : public wit::ResourceImportBase { R(uint32_t a); void Add(uint32_t b) const; R(wit::ResourceImportBase &&); - R(R &&) = default; + R &operator=(R &&) = default; }; R Create(); From 76577e790ca283e8c90853550a73d96afdedf2d0 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 11 Apr 2024 00:40:00 +0200 Subject: [PATCH 168/672] correct types on host side (header) --- crates/cpp/src/lib.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 5cffed9e7..8882dec96 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1417,12 +1417,20 @@ impl CppInterfaceGenerator<'_> { } TypeDefKind::Handle(Handle::Own(id)) => { let mut typename = self.type_name(&Type::Id(*id), from_namespace, flavor); - match flavor { - Flavor::Argument(AbiVariant::GuestImport) => typename.push_str("&&"), - Flavor::Argument(AbiVariant::GuestExport) - | Flavor::Result(AbiVariant::GuestExport) => typename.push_str("::Owned"), - Flavor::Result(AbiVariant::GuestImport) => (), - Flavor::InStruct => (), + 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("::Owned") + } + (false, Flavor::Result(AbiVariant::GuestImport)) + | (true, Flavor::Result(AbiVariant::GuestExport)) => (), + (_, Flavor::InStruct) => (), } typename } From 78696dfc0f812da3a0f2a7bf761beff73198fe23 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Mon, 15 Apr 2024 17:22:49 -0600 Subject: [PATCH 169/672] flesh out async_support.rs Signed-off-by: Joel Dice --- crates/rust/src/async_support.rs | 44 +++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/crates/rust/src/async_support.rs b/crates/rust/src/async_support.rs index 990979b7f..53ac54275 100644 --- a/crates/rust/src/async_support.rs +++ b/crates/rust/src/async_support.rs @@ -58,16 +58,22 @@ pub async unsafe fn await_result( const STATUS_DONE: i32 = 3; match import(params, results, call) { + STATUS_NOT_STARTED => { + let (tx, rx) = oneshot::channel(); + CALLS.insert(*call.cast::(), tx); + rx.await.unwrap(); + alloc::dealloc(params, params_layout); + } STATUS_PARAMS_READ => { alloc::dealloc(params, params_layout); let (tx, rx) = oneshot::channel(); CALLS.insert(*call.cast::(), tx); rx.await.unwrap() } - STATUS_DONE => { + STATUS_RESULTS_WRITTEN | STATUS_DONE => { alloc::dealloc(params, params_layout); } - _ => todo!(), + status => unreachable!(), } } @@ -77,22 +83,30 @@ pub unsafe fn callback(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i const EVENT_CALL_DONE: i32 = 2; match event0 { - EVENT_CALL_DONE => { - CALLS.remove(&event1).unwrap().send(()); + EVENT_CALL_STARTED => { + // TODO: could dealloc params here if we attached the pointer to the call + 1 + } + EVENT_CALL_RETURNED | EVENT_CALL_DONE => { + if let Some(call) = CALLS.remove(&event1) { + call.send(()); - match (*(ctx as *mut FutureState)) - .0 - .as_mut() - .poll(&mut Context::from_waker(&dummy_waker())) - { - Poll::Ready(()) => { - // TODO: consider spawned task before returning "done" here - drop(Box::from_raw(ctx as *mut FutureState)); - 1 + match (*(ctx as *mut FutureState)) + .0 + .as_mut() + .poll(&mut Context::from_waker(&dummy_waker())) + { + Poll::Ready(()) => { + // TODO: consider spawned task before returning "done" here + drop(Box::from_raw(ctx as *mut FutureState)); + 1 + } + Poll::Pending => 0, } - Poll::Pending => 0, + } else { + 1 } } - _ => todo!(), + _ => unreachable!(), } } From 063fac34e22cb8689aac9a1a5da30aa8ea9f79f8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 19 Apr 2024 22:39:16 +0200 Subject: [PATCH 170/672] fix some guest problems --- crates/cpp/src/lib.rs | 12 +++++++++++- .../cpp/tests/native_resources/guest/the_world.cpp | 13 ++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 8882dec96..08463ef72 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2329,8 +2329,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { results.push(format!("{op}.get_handle()")); } abi::Instruction::HandleLift { .. } => { + // does it make a difference whether we own or borrow? + // if so we could look at handle let op = &operands[0]; - results.push(op.clone()); + if self.gen.gen.opts.host_side() { + results.push(format!("wit::ResourceExportBase{{{op}}}")); + } else { + results.push(format!("wit::ResourceImportBase{{{op}}}")); + } } abi::Instruction::TupleLower { tuple, .. } => { let op = &operands[0]; @@ -2899,6 +2905,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src.push_str(&format!(", ret, {})", cabi_post_name)); } } + if matches!(func.kind, FunctionKind::Constructor(_)) { + // we wrapped the handle in an object, so unpack it + self.src.push_str(".into_handle()"); + } self.src.push_str(";\n"); } } diff --git a/crates/cpp/tests/native_resources/guest/the_world.cpp b/crates/cpp/tests/native_resources/guest/the_world.cpp index e474fec93..599721279 100644 --- a/crates/cpp/tests/native_resources/guest/the_world.cpp +++ b/crates/cpp/tests/native_resources/guest/the_world.cpp @@ -50,12 +50,14 @@ foo::foo::resources::R::~R() { } foo::foo::resources::R::R(uint32_t a) { auto ret = fooX3AfooX2FresourcesX00X5BconstructorX5Dr((int32_t(a))); - this->handle = ret; + 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}; @@ -63,19 +65,21 @@ foo::foo::resources::R foo::foo::resources::Create() { void foo::foo::resources::Borrows(std::reference_wrapper o) { fooX3AfooX2FresourcesX00borrows(o.get().get_handle()); } -void foo::foo::resources::Consume(R&& o) { +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()->into_handle(); + auto result0 = + exports::foo::foo::resources::R::New((uint32_t(arg0))).release(); + return (*(result0)).into_handle(); } extern "C" __attribute__((__export_name__("foo:foo/resources#[method]r.add"))) void @@ -109,4 +113,3 @@ fooX3AfooX2FresourcesX23consume(int32_t arg0) { } // Component Adapters -foo::foo::resources::R::R(wit::ResourceImportBase&&b) : wit::ResourceImportBase(std::move(b)) {} From 0d8bb1f68a2202ff2b1f8e027e21124bd35f821d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 21 Apr 2024 23:22:42 +0200 Subject: [PATCH 171/672] C++ run smoketest works --- crates/c/src/lib.rs | 2 +- crates/cpp/src/lib.rs | 52 +++++++++++++++++---- tests/runtime/main.rs | 89 ++++++++++++++++++++++++++++++++++++ tests/runtime/smoke/wasm.cpp | 6 +++ 4 files changed, 139 insertions(+), 10 deletions(-) create mode 100644 tests/runtime/smoke/wasm.cpp diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 49b29437b..b4b13338c 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::*; diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 08463ef72..bd26418ad 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -9,7 +9,7 @@ use std::{ use wit_bindgen_c::to_c_ident; use wit_bindgen_core::{ abi::{self, AbiVariant, Bindgen, Bitcast, LiftLower, WasmSignature, WasmType}, - make_external_symbol, uwrite, uwriteln, + make_external_component, make_external_symbol, uwrite, uwriteln, wit_parser::{ AddressSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, Resolve, Results, SizeAlign, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, @@ -348,7 +348,7 @@ impl WorldGenerator for Cpp { funcs: &[(&str, &Function)], _files: &mut Files, ) { - let name = WorldKey::Name(resolve.worlds[world].name.clone()); + 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)); @@ -370,9 +370,9 @@ impl WorldGenerator for Cpp { _files: &mut Files, ) -> anyhow::Result<()> { let name = WorldKey::Name(resolve.worlds[world].name.clone()); - let wasm_import_module = resolve.name_world_key(&name); + // let wasm_import_module = resolve.name_world_key(&name); let binding = Some(name); - let mut gen = self.interface(resolve, binding.as_ref(), false, Some(wasm_import_module)); + let mut gen = self.interface(resolve, binding.as_ref(), false, None); let namespace = namespace(resolve, &TypeOwner::World(world), true); for (_name, func) in funcs.iter() { @@ -404,6 +404,7 @@ impl WorldGenerator for Cpp { ) -> 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(); @@ -492,6 +493,19 @@ impl WorldGenerator for Cpp { uwriteln!(c_str.src, "#include \"{snake}_cpp_native.h\""); } else if !self.opts.host { // uwriteln!(c_str.src, "#include \"{snake}_cpp.h\""); + uwriteln!( + c_str.src, + "\n// Ensure that the *_component_type.o object is linked in" + ); + uwrite!( + c_str.src, + " + extern void {linking_symbol}(void); + void {linking_symbol}_public_use_in_this_compilation_unit(void) {{ + {linking_symbol}(); + }} + ", + ); } else { uwriteln!(c_str.src, "#include \"{snake}_cpp_host.h\""); uwriteln!( @@ -589,6 +603,18 @@ impl WorldGenerator for Cpp { 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(()) } } @@ -796,12 +822,12 @@ impl CppInterfaceGenerator<'_> { retptr: false, }, }; - let mut module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + let mut module_name = self.wasm_import_module.as_ref().map(|e| e.clone()); if matches!( is_drop, SpecialMethod::ResourceNew | SpecialMethod::ResourceDrop ) { - module_name = String::from("[export]") + &module_name; + module_name = Some(String::from("[export]") + &module_name.unwrap()); } if self.gen.opts.short_cut { uwrite!(self.gen.c_src.src, "extern \"C\" "); @@ -809,9 +835,14 @@ impl CppInterfaceGenerator<'_> { self.gen.c_src.src.push_str("static "); } else { let func_name = &func.name; + 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, - r#"extern "C" __attribute__((__export_name__("{module_name}#{func_name}")))"# + r#"extern "C" __attribute__((__export_name__("{module_prefix}{func_name}")))"# ); } let return_via_pointer = signature.retptr && self.gen.opts.host_side(); @@ -824,7 +855,10 @@ impl CppInterfaceGenerator<'_> { self.gen.opts.wasm_type(signature.results[0]) }); self.gen.c_src.src.push_str(" "); - let export_name = make_external_symbol(&module_name, &func.name, variant); + let export_name = match module_name { + Some(ref module_name) => make_external_symbol(&module_name, &func.name, variant), + None => make_external_component(&func.name), + }; self.gen.c_src.src.push_str(&export_name); self.gen.c_src.src.push_str("("); let mut first_arg = true; @@ -866,7 +900,7 @@ impl CppInterfaceGenerator<'_> { }; self.gen .host_functions - .entry(module_name) + .entry(module_name.unwrap_or(self.gen.world.clone())) .and_modify(|v| v.push(remember.clone())) .or_insert(vec![remember]); } diff --git a/tests/runtime/main.rs b/tests/runtime/main.rs index a7a05bab5..385110634 100644 --- a/tests/runtime/main.rs +++ b/tests/runtime/main.rs @@ -122,6 +122,7 @@ fn tests(name: &str, dir_name: &str) -> Result> { let mut java = Vec::new(); let mut go = Vec::new(); let mut c_sharp: Vec = 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()) { @@ -134,6 +135,7 @@ fn tests(name: &str, dir_name: &str) -> Result> { c_sharp.push(path); } } + Some("cpp") => cpp.push(path), _ => {} } } @@ -268,6 +270,93 @@ fn tests(name: &str, dir_name: &str) -> Result> { } } + #[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-{}", 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("utf16") { + // opts.string_encoding = wit_component::StringEncoding::UTF16; + // } + // } + 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("-Wall") + .arg("-Wextra") + .arg("-Werror") + .arg("-Wno-unused-parameter") + .arg("-mexec-model=reactor") + .arg("-g") + .arg("-o") + .arg(&out_wasm); + // Disable the warning about compiling a `.c` file in C++ mode. + // if compiler.ends_with("++") { + // cmd.arg("-Wno-deprecated"); + // } + 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); + } + } + // FIXME: need to fix flaky Go test #[cfg(feature = "go")] if !go.is_empty() { 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 +//#include + +void exports::smoke::Thunk() { + test::smoke::imports::Thunk(); +} From f1a0e9f2c5f0ecd54b5afae30911ce804b365d20 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 22 Apr 2024 22:42:57 +0200 Subject: [PATCH 172/672] only require binary wit on wasm --- crates/cpp/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index bd26418ad..6cffc3e73 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -492,18 +492,18 @@ impl WorldGenerator for Cpp { if self.opts.short_cut { uwriteln!(c_str.src, "#include \"{snake}_cpp_native.h\""); } else if !self.opts.host { - // uwriteln!(c_str.src, "#include \"{snake}_cpp.h\""); 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 { From 685d5cc13ff7f3dae60a2971bcee7085f8237983 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 22 Apr 2024 23:41:15 +0200 Subject: [PATCH 173/672] support methods again --- crates/cpp/src/lib.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 6cffc3e73..8b9a41485 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2362,14 +2362,16 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let op = &operands[0]; results.push(format!("{op}.get_handle()")); } - abi::Instruction::HandleLift { .. } => { - // does it make a difference whether we own or borrow? - // if so we could look at handle + abi::Instruction::HandleLift { handle, .. } => { let op = &operands[0]; - if self.gen.gen.opts.host_side() { - results.push(format!("wit::ResourceExportBase{{{op}}}")); - } else { - results.push(format!("wit::ResourceImportBase{{{op}}}")); + match (handle, self.gen.gen.opts.host_side()) { + (Handle::Own(_), true) => { + results.push(format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}{{{op}}}")) + } + (Handle::Own(_), false) => { + results.push(format!("wit::{RESOURCE_IMPORT_BASE_CLASS_NAME}{{{op}}}")) + } + (Handle::Borrow(_), _) => results.push(op.clone()), } } abi::Instruction::TupleLower { tuple, .. } => { From c408927ee3d1ccc9d533a3700d7088f2b23f36eb Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 22 Apr 2024 23:52:45 +0200 Subject: [PATCH 174/672] remove the duplicate into_handle --- crates/cpp/src/lib.rs | 2 +- crates/cpp/tests/native_resources/guest/the_world.cpp | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 8b9a41485..2841f2290 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2941,7 +2941,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src.push_str(&format!(", ret, {})", cabi_post_name)); } } - if matches!(func.kind, FunctionKind::Constructor(_)) { + if matches!(func.kind, FunctionKind::Constructor(_)) && guest_import { // we wrapped the handle in an object, so unpack it self.src.push_str(".into_handle()"); } diff --git a/crates/cpp/tests/native_resources/guest/the_world.cpp b/crates/cpp/tests/native_resources/guest/the_world.cpp index 599721279..87f6f5a0d 100644 --- a/crates/cpp/tests/native_resources/guest/the_world.cpp +++ b/crates/cpp/tests/native_resources/guest/the_world.cpp @@ -1,4 +1,13 @@ // 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 // realloc From d9298109ac866cac0d945b83feeaf872914560c6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 24 Apr 2024 23:45:08 +0200 Subject: [PATCH 175/672] strings runtime test works --- Cargo.lock | 2 +- crates/cpp/src/lib.rs | 20 ++++++++++++++++---- tests/runtime/main.rs | 3 +++ tests/runtime/strings/wasm.cpp | 28 ++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 tests/runtime/strings/wasm.cpp diff --git a/Cargo.lock b/Cargo.lock index eb2409d3b..2092ceef6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2376,7 +2376,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.202.0", + "wasm-encoder 0.205.0", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 2841f2290..abd6b0a93 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1303,10 +1303,22 @@ impl CppInterfaceGenerator<'_> { && abi::guest_export_needs_post_return(self.resolve, func) { let sig = self.resolve.wasm_signature(variant, func); - let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); - let export_name = func.core_export_name(Some(&module_name)); - let import_name = - make_external_symbol(&module_name, &func.name, AbiVariant::GuestExport); + let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()); + let export_name = match module_name { + Some(ref module_name) => make_external_symbol(module_name, &func.name, variant), + 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, 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}\")))" diff --git a/tests/runtime/main.rs b/tests/runtime/main.rs index e2236aa63..ea6e356e9 100644 --- a/tests/runtime/main.rs +++ b/tests/runtime/main.rs @@ -310,11 +310,14 @@ fn tests(name: &str, dir_name: &str) -> Result> { .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("-Wall") .arg("-Wextra") .arg("-Werror") .arg("-Wno-unused-parameter") .arg("-mexec-model=reactor") + .arg("-std=c++17") .arg("-g") .arg("-o") .arg(&out_wasm); 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 +#include +#include +#include +#include + +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); +} From 8723690bda70ed4013b91df5565d49f38d9d9a36 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 24 Apr 2024 23:56:43 +0200 Subject: [PATCH 176/672] numbers test passes --- tests/runtime/numbers/wasm.cpp | 112 +++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 tests/runtime/numbers/wasm.cpp 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 +#include +#include +#include + +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); +} From 76db7c6019d8c4a7f68377523236b4b6d1c64f3a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 2 May 2024 00:10:41 +0200 Subject: [PATCH 177/672] fmt + update --- Cargo.lock | 2 +- crates/cpp/src/lib.rs | 8 +++++--- tests/runtime/main.rs | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d6141212..ba1691539 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2378,7 +2378,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.205.0", + "wasm-encoder 0.206.0", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index abd6b0a93..62570d38d 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1310,15 +1310,17 @@ impl CppInterfaceGenerator<'_> { }; //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, AbiVariant::GuestExport), + Some(ref module_name) => { + make_external_symbol(module_name, &func.name, AbiVariant::GuestExport) + } None => make_external_component(&func.name), }; - // make_external_symbol(&module_name, &func.name, AbiVariant::GuestExport); + // 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}\")))" diff --git a/tests/runtime/main.rs b/tests/runtime/main.rs index 0246c156e..e42bd5572 100644 --- a/tests/runtime/main.rs +++ b/tests/runtime/main.rs @@ -307,7 +307,7 @@ fn tests(name: &str, dir_name: &str) -> Result> { .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/helper-types")) .arg("-Wall") .arg("-Wextra") .arg("-Werror") From 9de3070ff0c5d0fc76f2ec86ed1d6b38e057012d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 May 2024 16:00:30 +0200 Subject: [PATCH 178/672] optional toplevel namespace --- crates/cpp/src/lib.rs | 74 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 62570d38d..c468414fc 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -122,6 +122,23 @@ pub struct Opts { /// 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, + + /// 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, } impl Opts { @@ -304,7 +321,7 @@ impl WorldGenerator for Cpp { 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); + 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) { @@ -330,7 +347,7 @@ impl WorldGenerator for Cpp { 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); + 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) { @@ -352,7 +369,7 @@ impl WorldGenerator for Cpp { 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); + let namespace = namespace(resolve, &TypeOwner::World(world), false, &gen.gen.opts); for (_name, func) in funcs.iter() { if matches!(func.kind, FunctionKind::Freestanding) { @@ -373,7 +390,7 @@ impl WorldGenerator for Cpp { // 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); + let namespace = namespace(resolve, &TypeOwner::World(world), true, &gen.gen.opts); for (_name, func) in funcs.iter() { if matches!(func.kind, FunctionKind::Freestanding) { @@ -620,8 +637,11 @@ impl WorldGenerator for Cpp { } // determine namespace (for the lifted C++ function) -fn namespace(resolve: &Resolve, owner: &TypeOwner, guest_export: bool) -> Vec { +fn namespace(resolve: &Resolve, owner: &TypeOwner, guest_export: bool, opts: &Opts) -> Vec { let mut result = Vec::default(); + if let Some(prefix) = &opts.internal_prefix { + result.push(prefix.clone()); + } if guest_export { result.push(String::from("exports")); } @@ -757,7 +777,7 @@ impl CppInterfaceGenerator<'_> { .map(|id| TypeOwner::Interface(id)) .unwrap_or(TypeOwner::World(self.gen.world_id.unwrap())), )); - let mut namespace = namespace(self.resolve, &owner, guest_export); + 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()); @@ -1152,6 +1172,7 @@ impl CppInterfaceGenerator<'_> { cifg.resolve, &owner.owner, matches!(variant, AbiVariant::GuestExport), + &cifg.gen.opts, ); namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); namespace @@ -1249,6 +1270,7 @@ impl CppInterfaceGenerator<'_> { self.resolve, owner, matches!(variant, AbiVariant::GuestExport), + &self.gen.opts, ) } else { let owner = &self.resolve.types[match &func.kind { @@ -1262,6 +1284,7 @@ impl CppInterfaceGenerator<'_> { self.resolve, &owner.owner, matches!(variant, AbiVariant::GuestExport), + &self.gen.opts, ); namespace.push(owner.name.as_ref().unwrap().to_upper_camel_case()); namespace @@ -1413,7 +1436,7 @@ impl CppInterfaceGenerator<'_> { guest_export: bool, ) -> String { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner, guest_export); + 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); @@ -1613,7 +1636,12 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner, NOT_IN_EXPORTED_NAMESPACE); + 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); @@ -1642,7 +1670,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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); + let namespc = namespace(self.resolve, &type_.owner, !guest_import, &self.gen.opts); let pascal = name.to_upper_camel_case(); let user_filename = namespc.join("-") + "-" + &pascal + ".h"; if definition { @@ -1786,7 +1814,12 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner, NOT_IN_EXPORTED_NAMESPACE); + 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); @@ -1826,7 +1859,12 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner, NOT_IN_EXPORTED_NAMESPACE); + 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(); @@ -1879,7 +1917,12 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner, NOT_IN_EXPORTED_NAMESPACE); + 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(); @@ -1906,7 +1949,12 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> docs: &wit_bindgen_core::wit_parser::Docs, ) { let ty = &self.resolve.types[id]; - let namespc = namespace(self.resolve, &ty.owner, NOT_IN_EXPORTED_NAMESPACE); + 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); From 2d0b75d9ebd755d650d030d78a842d295e48db6c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 May 2024 16:01:13 +0200 Subject: [PATCH 179/672] musings on borrowing --- crates/cpp/DESIGN.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md index c28119eb8..f114ab4e4 100644 --- a/crates/cpp/DESIGN.md +++ b/crates/cpp/DESIGN.md @@ -25,6 +25,8 @@ | | | record{string, list} | &T | T const& | a,l,a,l | | | | large-struct (>16 args) | &T | T const& | &t | | | | result | Result<&str, &[]> | std::expected | d,a,l | +| | | option\ | Option\<&str> | optional const& | d,a,l| +| | | list\ | &[\&Resrc]? | vector const& | a,l| | GIR | t | string | String | wit::string[^2] | &(addr, len) | | | | | list | Vec | wit::vector | &(a,l) | | | | result | Result | std::expected | &(d,a,l) | From f352b696eeb56cc670bed94182ce6545067cd8fa Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 May 2024 21:36:28 +0200 Subject: [PATCH 180/672] add ownership option (no implementation yet) --- crates/cpp/src/lib.rs | 69 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index c468414fc..5821aa894 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -107,6 +107,59 @@ struct Cpp { defined_types: HashSet<(Vec, 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 { + 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 { @@ -139,6 +192,22 @@ pub struct Opts { /// This avoids identical names across components, useful for native #[cfg_attr(feature = "clap", arg(long))] pub internal_prefix: Option, + + /// 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, } impl Opts { From 7ec5d10a9c841bf64ef90583a55fe0e21aad00bd Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 May 2024 22:08:44 +0200 Subject: [PATCH 181/672] generate resource rep (guest focus) --- crates/cpp/src/lib.rs | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 5821aa894..f7a96b5f4 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -867,6 +867,7 @@ impl CppInterfaceGenerator<'_> { } SpecialMethod::Dtor => "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(), @@ -888,6 +889,12 @@ impl CppInterfaceGenerator<'_> { indirect_params: false, retptr: false, }, + SpecialMethod::ResourceRep => WasmSignature { + params: vec![WasmType::I32], + results: vec![WasmType::Pointer], + indirect_params: false, + retptr: false, + }, SpecialMethod::Dtor => WasmSignature { params: vec![WasmType::Pointer], results: Vec::new(), @@ -1042,7 +1049,7 @@ impl CppInterfaceGenerator<'_> { } wit_bindgen_core::wit_parser::Results::Anon(ty) => { res.result = self.type_name(ty, from_namespace, Flavor::Result(abi_variant)); - if matches!(is_drop, SpecialMethod::Allocate) { + if matches!(is_drop, SpecialMethod::Allocate | SpecialMethod::ResourceRep) { res.result.push('*'); } } @@ -1172,7 +1179,7 @@ impl CppInterfaceGenerator<'_> { if !import && !matches!( is_special, - SpecialMethod::ResourceDrop | SpecialMethod::ResourceNew + SpecialMethod::ResourceDrop | SpecialMethod::ResourceNew | SpecialMethod::ResourceRep ) { self.print_export_signature(func, variant) @@ -1330,6 +1337,23 @@ impl CppInterfaceGenerator<'_> { // self.gen.c_src.src.push_str("// new logic: call r-new\n"); //let f = Function { name: String::new(), kind: FunctionKind::Static(Id), params: Vec::new(), results: Vec::new(), docs: Docs::default() }; } + SpecialMethod::ResourceRep => { + 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 + ); + } SpecialMethod::Allocate => unreachable!(), // SpecialMethod::Deallocate => self.gen.c_src.src.push_str("// deallocate\n"), SpecialMethod::None => { @@ -1849,6 +1873,15 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> }; self.generate_function(&func, &TypeOwner::Interface(intf), variant); + let func = Function { + name: "[resource-rep]".to_string() + &name, + kind: FunctionKind::Static(id), + params: vec![("id".into(), Type::S32)], + results: Results::Anon(Type::Id(id)), + docs: Docs::default(), + }; + self.generate_function(&func, &TypeOwner::Interface(intf), variant); + let func2 = Function { name: "[resource-drop]".to_string() + &name, kind: FunctionKind::Static(id), @@ -3203,6 +3236,7 @@ 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) } @@ -3213,6 +3247,8 @@ fn is_special_method(func: &Function) -> SpecialMethod { 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" { From 88dbae6f0c0820a8eff96d7f3d8eac079103ee2a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 May 2024 22:14:30 +0200 Subject: [PATCH 182/672] new styled reference code --- .../native_resources/guest/the_world.cpp | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/crates/cpp/tests/native_resources/guest/the_world.cpp b/crates/cpp/tests/native_resources/guest/the_world.cpp index 87f6f5a0d..c6c842334 100644 --- a/crates/cpp/tests/native_resources/guest/the_world.cpp +++ b/crates/cpp/tests/native_resources/guest/the_world.cpp @@ -47,11 +47,11 @@ 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__((import_module("[export]foo:foo/resources"))) -__attribute__((import_name("[resource-rep]r"))) uint8_t* - X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(int32_t); foo::foo::resources::R::~R() { if (handle >= 0) { fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(handle); @@ -83,9 +83,9 @@ fooX3AfooX2FresourcesX23X5BdtorX5Dr(uint8_t *arg0) { 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) { +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))).release(); return (*(result0)).into_handle(); @@ -99,12 +99,14 @@ 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); } -exports::foo::foo::resources::R* exports::foo::foo::resources::R::ResourceRep(int32_t id) { - return (exports::foo::foo::resources::R*)X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(id); -} extern "C" __attribute__((__export_name__("foo:foo/resources#create"))) int32_t fooX3AfooX2FresourcesX23create() { auto result0 = exports::foo::foo::resources::Create(); @@ -117,7 +119,7 @@ fooX3AfooX2FresourcesX23borrows(int8_t* arg0) { extern "C" __attribute__((__export_name__("foo:foo/resources#consume"))) void fooX3AfooX2FresourcesX23consume(int32_t arg0) { auto obj = exports::foo::foo::resources::R::Owned(exports::foo::foo::resources::R::ResourceRep(arg0)); - //obj->into_handle(); + obj->into_handle(); exports::foo::foo::resources::Consume(std::move(obj)); } From a949b1021d29747cabc67748d83986338cfb2161 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 May 2024 22:45:50 +0200 Subject: [PATCH 183/672] centralize Deleter --- crates/cpp/helper-types/wit-guest.h | 6 ++++++ crates/cpp/src/lib.rs | 14 ++++++++++++-- .../guest/exports-foo-foo-resources-R.h | 4 ---- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 7f1f92781..745b268df 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -2,6 +2,7 @@ #include #include #include +#include // unique_ptr #include "wit-common.h" namespace wit { @@ -82,6 +83,11 @@ class vector { template class ResourceExportBase { public: + struct Deleter { + void operator()(R* ptr) const { R::Dtor(ptr); } + }; + typedef std::unique_ptr Owned; + static const int32_t invalid = -1; int32_t handle; diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index f7a96b5f4..e4e58dbc4 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -75,6 +75,7 @@ struct Includes { needs_tuple: bool, // needs wit types needs_wit: bool, + needs_memory: bool, } #[derive(Clone)] @@ -563,6 +564,9 @@ impl WorldGenerator for Cpp { self.include(""); } } + if self.dependencies.needs_memory { + self.include(""); + } if self.opts.short_cut { uwriteln!(h_str.src, "#define WIT_HOST_DIRECT"); @@ -1145,9 +1149,9 @@ impl CppInterfaceGenerator<'_> { uwrite!( self.gen.h_src.src, "{{\ - return new {}({});\ + return Owned(new {}({}));\ }}", - cpp_sig.namespace.join("::"), + cpp_sig.namespace.last().unwrap(), //join("::"), cpp_sig .arguments .iter() @@ -1786,6 +1790,8 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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}>"), @@ -1822,6 +1828,10 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> }; 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 { 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 index b3b33c423..438782772 100644 --- 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 @@ -12,10 +12,6 @@ class R : public wit::ResourceExportBase { public: static void Dtor(R *self) { delete self; }; - struct Deleter { - void operator()(R* ptr) const { R::Dtor(ptr); } - }; - typedef std::unique_ptr Owned; R(uint32_t a) : value(a) {} static Owned New(uint32_t a) { return Owned(new R(a)); } void Add(uint32_t b) { value += b; } From 1358302f8049d7fc5160d4b5891965d5003e0f74 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 May 2024 22:58:48 +0200 Subject: [PATCH 184/672] generate correct new method --- crates/cpp/src/lib.rs | 24 +++++++++++++------ .../guest/exports-foo-foo-resources-R.h | 2 +- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index e4e58dbc4..1d4f1b41a 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -917,7 +917,7 @@ impl CppInterfaceGenerator<'_> { } SpecialMethod::Allocate => WasmSignature { params: vec![], - results: vec![WasmType::Pointer], + results: vec![], indirect_params: false, retptr: false, }, @@ -1052,9 +1052,17 @@ impl CppInterfaceGenerator<'_> { } } wit_bindgen_core::wit_parser::Results::Anon(ty) => { - res.result = self.type_name(ty, from_namespace, Flavor::Result(abi_variant)); - if matches!(is_drop, SpecialMethod::Allocate | SpecialMethod::ResourceRep) { - res.result.push('*'); + if matches!(is_drop, SpecialMethod::Allocate) { + res.result = OWNED_CLASS_NAME.into(); + } else { + res.result = + self.type_name(ty, from_namespace, Flavor::Result(abi_variant)); + if matches!( + is_drop, + SpecialMethod::Allocate | SpecialMethod::ResourceRep + ) { + res.result.push('*'); + } } } } @@ -1149,7 +1157,7 @@ impl CppInterfaceGenerator<'_> { uwrite!( self.gen.h_src.src, "{{\ - return Owned(new {}({}));\ + return {OWNED_CLASS_NAME}(new {}({}));\ }}", cpp_sig.namespace.last().unwrap(), //join("::"), cpp_sig @@ -1183,7 +1191,9 @@ impl CppInterfaceGenerator<'_> { if !import && !matches!( is_special, - SpecialMethod::ResourceDrop | SpecialMethod::ResourceNew | SpecialMethod::ResourceRep + SpecialMethod::ResourceDrop + | SpecialMethod::ResourceNew + | SpecialMethod::ResourceRep ) { self.print_export_signature(func, variant) @@ -1594,7 +1604,7 @@ impl CppInterfaceGenerator<'_> { | (false, Flavor::Result(AbiVariant::GuestExport)) | (true, Flavor::Argument(AbiVariant::GuestImport)) | (true, Flavor::Result(AbiVariant::GuestImport)) => { - typename.push_str("::Owned") + typename.push_str(&format!("::{OWNED_CLASS_NAME}")) } (false, Flavor::Result(AbiVariant::GuestImport)) | (true, Flavor::Result(AbiVariant::GuestExport)) => (), 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 index 438782772..ce8d45fd0 100644 --- 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 @@ -16,8 +16,8 @@ class R : public wit::ResourceExportBase { 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 void ResourceDrop(int32_t id); static R* ResourceRep(int32_t id); + static void ResourceDrop(int32_t id); uint32_t GetValue() const { return value; } }; From d566b99ed294f126b5737fe8473259c36f325674 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 00:24:12 +0200 Subject: [PATCH 185/672] towards a correct dtor --- crates/cpp/helper-types/wit-host.h | 5 +++ crates/cpp/src/lib.rs | 37 +++++++++++++++---- .../native_resources/foo-foo-resources-R.h | 7 +--- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index cfae84f31..c04f9d3e2 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -233,6 +233,11 @@ template class ResourceImportBase : public ResourceTable { int32_t index; public: + struct Deleter { + void operator()(R* ptr) const { R::Dtor(ptr); } + }; + typedef std::unique_ptr Owned; + static const int32_t invalid=-1; ResourceImportBase() : index(this->store_resource((R*)this)) {} ~ResourceImportBase() {} diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 1d4f1b41a..ce5d04f7e 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -21,6 +21,7 @@ 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"; // these types are always defined in the non-exports namespace const NOT_IN_EXPORTED_NAMESPACE: bool = false; @@ -616,7 +617,10 @@ impl WorldGenerator for Cpp { } } if self.opts.host_side() && self.dependencies.needs_exported_resources { - uwriteln!(c_str.src, "template std::map wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}::resources;"); + uwriteln!( + c_str.src, + "template std::map wit::{RESOURCE_TABLE_NAME}::resources;" + ); } h_str.change_namespace(&Vec::default()); @@ -863,7 +867,9 @@ impl CppInterfaceGenerator<'_> { } else { match is_drop { SpecialMethod::ResourceDrop => { - if guest_export { + if self.gen.opts.host_side() && !guest_export { + "Dtor".to_string() + } else if guest_export { "ResourceDrop".to_string() } else { "~".to_string() + &object @@ -1028,9 +1034,11 @@ impl CppInterfaceGenerator<'_> { let is_drop = is_special_method(func); // we might want to separate c_sig and h_sig // let mut sig = String::new(); + // not for ctor nor imported dtor on guest if !matches!(&func.kind, FunctionKind::Constructor(_)) && !(matches!(is_drop, SpecialMethod::ResourceDrop) - && matches!(abi_variant, AbiVariant::GuestImport)) + && matches!(abi_variant, AbiVariant::GuestImport) + && !self.gen.opts.host_side()) { match &func.results { wit_bindgen_core::wit_parser::Results::Named(n) => { @@ -1073,8 +1081,9 @@ impl CppInterfaceGenerator<'_> { } } if matches!(func.kind, FunctionKind::Static(_)) - && !(matches!(is_drop, SpecialMethod::ResourceDrop) - && matches!(abi_variant, AbiVariant::GuestImport)) + && !(matches!(&is_drop, SpecialMethod::ResourceDrop) + && matches!(abi_variant, AbiVariant::GuestImport) + && !self.gen.opts.host_side()) { res.static_member = true; } @@ -1082,13 +1091,19 @@ impl CppInterfaceGenerator<'_> { if i == 0 && name == "self" && (matches!(&func.kind, FunctionKind::Method(_)) - || (matches!(is_drop, SpecialMethod::ResourceDrop) - && matches!(abi_variant, AbiVariant::GuestImport))) + || (matches!(&is_drop, SpecialMethod::ResourceDrop) + && matches!(abi_variant, AbiVariant::GuestImport) + && !self.gen.opts.host_side())) { res.implicit_self = true; continue; } - if matches!(is_drop, SpecialMethod::Dtor | SpecialMethod::ResourceNew) { + 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)) + "*", @@ -1827,6 +1842,12 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> AbiVariant::GuestImport => "[resource-drop]", AbiVariant::GuestExport => "[dtor]", } + // 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 { diff --git a/crates/cpp/tests/native_resources/foo-foo-resources-R.h b/crates/cpp/tests/native_resources/foo-foo-resources-R.h index abd9e3b64..e3591eafe 100644 --- a/crates/cpp/tests/native_resources/foo-foo-resources-R.h +++ b/crates/cpp/tests/native_resources/foo-foo-resources-R.h @@ -1,8 +1,7 @@ /* User class definition file, autogenerated once, then user modified * Updated versions of this file are generated into - * exports-foo-foo-resources-R.h.template. + * foo-foo-resources-R.h.template. */ -#include namespace foo { namespace foo { namespace resources { @@ -11,10 +10,6 @@ class R : public wit::ResourceImportBase { public: static void Dtor(R *self) { delete self; }; - struct Deleter { - void operator()(R* ptr) const { R::Dtor(ptr); } - }; - typedef std::unique_ptr Owned; R(uint32_t a) : value(a) {} static Owned New(uint32_t a) { return Owned(new R(a)); } void Add(uint32_t b) { value += b; } From a45f23e397f57ddb01d81aeba24a5ebeada680da Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 00:41:01 +0200 Subject: [PATCH 186/672] dtor has right signature (in one case) --- crates/cpp/src/lib.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index ce5d04f7e..7d724a80b 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -930,8 +930,8 @@ impl CppInterfaceGenerator<'_> { }; let mut module_name = self.wasm_import_module.as_ref().map(|e| e.clone()); if matches!( - is_drop, - SpecialMethod::ResourceNew | SpecialMethod::ResourceDrop + (&is_drop, self.gen.opts.host_side()), + (SpecialMethod::ResourceNew, false) | (SpecialMethod::ResourceDrop, false) | (SpecialMethod::ResourceRep, false) ) { module_name = Some(String::from("[export]") + &module_name.unwrap()); } @@ -1167,8 +1167,8 @@ impl CppInterfaceGenerator<'_> { self.gen.h_src.src.push_str(" const"); } let is_special = is_special_method(func); - match is_special { - SpecialMethod::Allocate => { + match (&is_special, self.gen.opts.host_side()) { + (SpecialMethod::Allocate, _) => { uwrite!( self.gen.h_src.src, "{{\ @@ -1185,7 +1185,7 @@ impl CppInterfaceGenerator<'_> { // body is inside the header return Vec::default(); } - SpecialMethod::Dtor => { + (SpecialMethod::Dtor, _) | (SpecialMethod::ResourceDrop, true) => { uwrite!( self.gen.h_src.src, "{{\ @@ -1197,19 +1197,18 @@ impl CppInterfaceGenerator<'_> { // SpecialMethod::None => todo!(), // SpecialMethod::ResourceDrop => todo!(), // SpecialMethod::ResourceNew => todo!(), - _ => (), + _ => self.gen.h_src.src.push_str(";\n"), } - 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 - && !matches!( - is_special, + && matches!( + &is_special, SpecialMethod::ResourceDrop | SpecialMethod::ResourceNew | SpecialMethod::ResourceRep - ) + ) == self.gen.opts.host_side() { self.print_export_signature(func, variant) } else { From 206f44f815216beff5625f77cd9fea6a0ef9b645 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 10:17:45 +0200 Subject: [PATCH 187/672] shrink difference to currently autogenerated code --- crates/cpp/helper-types/wit-host.h | 1 + crates/cpp/tests/native_resources/Makefile | 3 +++ .../tests/native_resources/the_world_cpp_native.h | 14 ++++---------- .../tests/native_resources/the_world_native.cpp | 10 +++++----- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index c04f9d3e2..11dcfb56e 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -5,6 +5,7 @@ #include #include #include +#include // unique_ptr #include "wit-common.h" #ifndef WIT_HOST_DIRECT diff --git a/crates/cpp/tests/native_resources/Makefile b/crates/cpp/tests/native_resources/Makefile index db79f4eb3..a53c0bc1d 100644 --- a/crates/cpp/tests/native_resources/Makefile +++ b/crates/cpp/tests/native_resources/Makefile @@ -13,3 +13,6 @@ bindgen: wit/resources_simple.wit clean: -rm *.o app-resources + +run: app-resources + LD_LIBRARY_PATH=. ./app-resources diff --git a/crates/cpp/tests/native_resources/the_world_cpp_native.h b/crates/cpp/tests/native_resources/the_world_cpp_native.h index c638e38e5..dbe1400c8 100644 --- a/crates/cpp/tests/native_resources/the_world_cpp_native.h +++ b/crates/cpp/tests/native_resources/the_world_cpp_native.h @@ -16,37 +16,31 @@ namespace resources { R::Owned Create(); void Borrows(std::reference_wrapper o); void Consume(R::Owned o); +// export_interface Interface(Id { idx: 0 }) } // namespace resources } // namespace foo } // namespace foo - -// guest exports namespace exports { namespace foo { namespace foo { namespace resources { -// only a handle, no data, imported from guest class R : public wit::ResourceExportBase { public: ~R(); R(uint32_t a); void Add(uint32_t b) const; - R(ResourceExportBase &&); - + R(wit::ResourceExportBase &&); R(R &&) = default; - R(R const&) = delete; - R& operator=(R const&)=delete; - R& operator=(R &&)=default; + R &operator=(R &&) = default; }; R Create(); void Borrows(std::reference_wrapper o); void Consume(R &&o); -// export_interface Interface(Id { idx: 0 }) } // 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 index cff95031a..e393a3585 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -22,6 +22,11 @@ extern "C" __attribute__((import_module("foo:foo/resources"))) __attribute__((import_name("consume"))) void fooX3AfooX2FresourcesX23consume(int32_t); +extern "C" void fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t idx) { + auto ptr = foo::foo::resources::R::remove_resource(idx); + 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(); @@ -42,11 +47,6 @@ extern "C" void fooX3AfooX2FresourcesX00consume(int32_t arg0) { assert(objptr.has_value()); foo::foo::resources::Consume(foo::foo::resources::R::Owned(*objptr)); } -extern "C" void fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t idx) { - auto ptr = foo::foo::resources::R::remove_resource(idx); - assert(ptr.has_value()); - foo::foo::resources::R::Dtor(*ptr); -} extern "C" int32_t X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t* rep) { return exports::foo::foo::resources::R::store_resource(std::move(rep)); } From 80bd45e3ba2a22498936bf3a39240dfd152b51fe Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 10:18:47 +0200 Subject: [PATCH 188/672] rustfmt --- crates/cpp/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7d724a80b..2c99d5502 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -931,7 +931,9 @@ impl CppInterfaceGenerator<'_> { let mut module_name = self.wasm_import_module.as_ref().map(|e| e.clone()); if matches!( (&is_drop, self.gen.opts.host_side()), - (SpecialMethod::ResourceNew, false) | (SpecialMethod::ResourceDrop, false) | (SpecialMethod::ResourceRep, false) + (SpecialMethod::ResourceNew, false) + | (SpecialMethod::ResourceDrop, false) + | (SpecialMethod::ResourceRep, false) ) { module_name = Some(String::from("[export]") + &module_name.unwrap()); } From f5f2714682f3bda01e741cf52f504b1192b08795 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 10:33:56 +0200 Subject: [PATCH 189/672] correct host dtor --- crates/cpp/src/lib.rs | 15 ++++++++++++++- .../tests/native_resources/the_world_native.cpp | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 2c99d5502..053563194 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -74,6 +74,7 @@ struct Includes { needs_exported_resources: bool, needs_variant: bool, needs_tuple: bool, + needs_assert: bool, // needs wit types needs_wit: bool, needs_memory: bool, @@ -622,6 +623,9 @@ impl WorldGenerator for Cpp { "template std::map wit::{RESOURCE_TABLE_NAME}::resources;" ); } + if self.dependencies.needs_assert { + uwriteln!(c_str.src, "#include "); + } h_str.change_namespace(&Vec::default()); @@ -1302,8 +1306,17 @@ impl CppInterfaceGenerator<'_> { 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, " remove_resource({});", params[0]); + 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(); diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index e393a3585..8384824ee 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -22,8 +22,8 @@ extern "C" __attribute__((import_module("foo:foo/resources"))) __attribute__((import_name("consume"))) void fooX3AfooX2FresourcesX23consume(int32_t); -extern "C" void fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t idx) { - auto ptr = foo::foo::resources::R::remove_resource(idx); +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); } From 7bfafea7050a08378ac9758d713e497b3f6b6f05 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 10:52:57 +0200 Subject: [PATCH 190/672] host works but guest export is broken --- crates/cpp/src/lib.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 053563194..847b2bfdd 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1209,12 +1209,14 @@ impl CppInterfaceGenerator<'_> { // we want to separate the lowered signature (wasm) and the high level signature if !import - && matches!( - &is_special, - SpecialMethod::ResourceDrop - | SpecialMethod::ResourceNew - | SpecialMethod::ResourceRep - ) == self.gen.opts.host_side() + && (self.gen.opts.host_side() + || matches!( + &is_special, + SpecialMethod::ResourceDrop + | SpecialMethod::ResourceNew + | SpecialMethod::ResourceRep + )) + // == self.gen.opts.host_side() { self.print_export_signature(func, variant) } else { From 44f42e4a40a5241027184281e8b8d839226536f3 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 10:59:54 +0200 Subject: [PATCH 191/672] guest logic repaired --- crates/cpp/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 847b2bfdd..2adf3fd67 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1210,13 +1210,12 @@ impl CppInterfaceGenerator<'_> { // we want to separate the lowered signature (wasm) and the high level signature if !import && (self.gen.opts.host_side() - || matches!( + || !matches!( &is_special, SpecialMethod::ResourceDrop | SpecialMethod::ResourceNew | SpecialMethod::ResourceRep )) - // == self.gen.opts.host_side() { self.print_export_signature(func, variant) } else { From 55b6fc62255b09d22cfc2ab71fe10bd2c3b392e1 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 11:42:11 +0200 Subject: [PATCH 192/672] emit resource-X methods on host side --- crates/cpp/src/lib.rs | 54 +++++++++---------- crates/cpp/src/wamr.rs | 6 ++- .../native_resources/the_world_native.cpp | 18 +++---- 3 files changed, 40 insertions(+), 38 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 2adf3fd67..6b03eaf1f 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1919,34 +1919,32 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> "{pascal}::{pascal}({base_type}&&b) : {base_type}(std::move(b)) {{}}" ); } else { - if !self.gen.opts.host_side() { - let func = Function { - name: "[resource-new]".to_string() + &name, - kind: FunctionKind::Static(id), - params: vec![("self".into(), Type::Id(id))], - results: Results::Anon(Type::S32), - docs: Docs::default(), - }; - self.generate_function(&func, &TypeOwner::Interface(intf), variant); - - let func = Function { - name: "[resource-rep]".to_string() + &name, - kind: FunctionKind::Static(id), - params: vec![("id".into(), Type::S32)], - results: Results::Anon(Type::Id(id)), - docs: Docs::default(), - }; - self.generate_function(&func, &TypeOwner::Interface(intf), variant); - - let func2 = Function { - name: "[resource-drop]".to_string() + &name, - kind: FunctionKind::Static(id), - params: vec![("id".into(), Type::S32)], - results: Results::Named(vec![]), - docs: Docs::default(), - }; - self.generate_function(&func2, &TypeOwner::Interface(intf), variant); - } + let func = Function { + name: "[resource-new]".to_string() + &name, + kind: FunctionKind::Static(id), + params: vec![("self".into(), Type::Id(id))], + results: Results::Anon(Type::S32), + docs: Docs::default(), + }; + self.generate_function(&func, &TypeOwner::Interface(intf), variant); + + let func = Function { + name: "[resource-rep]".to_string() + &name, + kind: FunctionKind::Static(id), + params: vec![("id".into(), Type::S32)], + results: Results::Anon(Type::Id(id)), + docs: Docs::default(), + }; + self.generate_function(&func, &TypeOwner::Interface(intf), variant); + + let func2 = Function { + name: "[resource-drop]".to_string() + &name, + kind: FunctionKind::Static(id), + params: vec![("id".into(), Type::S32)], + results: Results::Named(vec![]), + docs: Docs::default(), + }; + self.generate_function(&func2, &TypeOwner::Interface(intf), variant); } uwriteln!(self.gen.h_src.src, "}};\n"); if definition { diff --git a/crates/cpp/src/wamr.rs b/crates/cpp/src/wamr.rs index bed3faee4..3d13843a6 100644 --- a/crates/cpp/src/wamr.rs +++ b/crates/cpp/src/wamr.rs @@ -115,7 +115,11 @@ fn wamr_add_result(sig: &mut WamrSig, resolve: &Resolve, ty: &Type) { TypeDefKind::Stream(_) => todo!(), TypeDefKind::Type(ty) => wamr_add_result(sig, resolve, &ty), TypeDefKind::Unknown => todo!(), - TypeDefKind::Resource => todo!(), + TypeDefKind::Resource => { + // resource-rep is returning a pointer + // perhaps i??? + sig.wamr_result = "*".into(); + } TypeDefKind::Handle(_h) => { sig.wamr_result = "i".into(); } diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index 8384824ee..8272e36e8 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -35,6 +35,15 @@ 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 X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t* rep) { + return exports::foo::foo::resources::R::store_resource(std::move(rep)); +} +extern "C" void X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t idx) { + exports::foo::foo::resources::R::remove_resource(idx); +} +extern "C" uint8_t* X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(int32_t idx) { + return *exports::foo::foo::resources::R::lookup_resource(idx); +} extern "C" int32_t fooX3AfooX2FresourcesX00create() { auto result0 = foo::foo::resources::Create(); return result0.release()->get_handle(); @@ -47,15 +56,6 @@ extern "C" void fooX3AfooX2FresourcesX00consume(int32_t arg0) { assert(objptr.has_value()); foo::foo::resources::Consume(foo::foo::resources::R::Owned(*objptr)); } -extern "C" int32_t X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t* rep) { - return exports::foo::foo::resources::R::store_resource(std::move(rep)); -} -extern "C" void X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t idx) { - exports::foo::foo::resources::R::remove_resource(idx); -} -extern "C" uint8_t* X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(int32_t idx) { - return *exports::foo::foo::resources::R::lookup_resource(idx); -} exports::foo::foo::resources::R::~R() { if (this->rep) From 43c5d3a4cb0507cc35057315e1a0d97eaf1cc1ff Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 11:49:59 +0200 Subject: [PATCH 193/672] reduce differences --- .../tests/native_resources/the_world_native.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index 8272e36e8..29e727861 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -35,14 +35,14 @@ 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 X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t* rep) { - return exports::foo::foo::resources::R::store_resource(std::move(rep)); +extern "C" int32_t X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t *arg0) { + return exports::foo::foo::resources::R::store_resource(std::move(arg0)); } -extern "C" void X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t idx) { - exports::foo::foo::resources::R::remove_resource(idx); +extern "C" uint8_t* X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(int32_t arg0) { + return *exports::foo::foo::resources::R::lookup_resource(arg0); } -extern "C" uint8_t* X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(int32_t idx) { - return *exports::foo::foo::resources::R::lookup_resource(idx); +extern "C" void X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t arg0) { + exports::foo::foo::resources::R::remove_resource(arg0); } extern "C" int32_t fooX3AfooX2FresourcesX00create() { auto result0 = foo::foo::resources::Create(); @@ -70,6 +70,8 @@ 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)) {} exports::foo::foo::resources::R exports::foo::foo::resources::Create() { auto ret = fooX3AfooX2FresourcesX23create(); return wit::ResourceExportBase{ret}; @@ -84,4 +86,3 @@ void exports::foo::foo::resources::Consume(R &&o) { } // Component Adapters -exports::foo::foo::resources::R::R(wit::ResourceExportBase&& b) : wit::ResourceExportBase(std::move(b)) {} From 2e1b2484f81eeb7514616746a123cc8c1fbe0fe3 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 12:50:15 +0200 Subject: [PATCH 194/672] reduce confusion --- crates/cpp/src/lib.rs | 156 ++++++++++-------- .../native_resources/the_world_native.cpp | 18 +- 2 files changed, 93 insertions(+), 81 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 6b03eaf1f..10c628834 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -933,12 +933,14 @@ impl CppInterfaceGenerator<'_> { }, }; let mut module_name = self.wasm_import_module.as_ref().map(|e| e.clone()); - if matches!( - (&is_drop, self.gen.opts.host_side()), - (SpecialMethod::ResourceNew, false) - | (SpecialMethod::ResourceDrop, false) - | (SpecialMethod::ResourceRep, false) - ) { + 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.short_cut { @@ -1135,77 +1137,87 @@ impl CppInterfaceGenerator<'_> { variant: AbiVariant, import: bool, ) -> Vec { - 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(", "); + let is_special = is_special_method(func); + if !(import == false + && 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 "); } - } - for (num, (arg, typ)) in cpp_sig.arguments.iter().enumerate() { - if num > 0 { - self.gen.h_src.src.push_str(", "); + 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(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"); - } - let is_special = is_special_method(func); - match (&is_special, self.gen.opts.host_side()) { - (SpecialMethod::Allocate, _) => { - uwrite!( - self.gen.h_src.src, - "{{\ + 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()) { + (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::>() - .join(", ") - ); - // body is inside the header - return Vec::default(); - } - (SpecialMethod::Dtor, _) | (SpecialMethod::ResourceDrop, true) => { - uwrite!( - self.gen.h_src.src, - "{{\ + cpp_sig.namespace.last().unwrap(), //join("::"), + cpp_sig + .arguments + .iter() + .map(|(arg, _)| arg.clone()) + .collect::>() + .join(", ") + ); + // body is inside the header + return Vec::default(); + } + (SpecialMethod::Dtor, _) | (SpecialMethod::ResourceDrop, true) => { + uwrite!( + self.gen.h_src.src, + "{{\ delete {};\ }}", - cpp_sig.arguments.get(0).unwrap().0 - ); + cpp_sig.arguments.get(0).unwrap().0 + ); + } + // SpecialMethod::None => todo!(), + // SpecialMethod::ResourceDrop => todo!(), + // SpecialMethod::ResourceNew => todo!(), + _ => self.gen.h_src.src.push_str(";\n"), } - // SpecialMethod::None => todo!(), - // SpecialMethod::ResourceDrop => todo!(), - // SpecialMethod::ResourceNew => todo!(), - _ => self.gen.h_src.src.push_str(";\n"), } - drop(cpp_sig); + // drop(cpp_sig); // we want to separate the lowered signature (wasm) and the high level signature if !import @@ -1905,7 +1917,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } } - if !definition { + if definition==self.gen.opts.host_side() { // consuming constructor from handle (bindings) uwriteln!(self.gen.h_src.src, "{pascal}({base_type} &&);",); uwriteln!(self.gen.h_src.src, "{pascal}({pascal}&&) = default;"); @@ -1928,14 +1940,14 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> }; self.generate_function(&func, &TypeOwner::Interface(intf), variant); - let func = Function { + let func1 = Function { name: "[resource-rep]".to_string() + &name, kind: FunctionKind::Static(id), params: vec![("id".into(), Type::S32)], results: Results::Anon(Type::Id(id)), docs: Docs::default(), }; - self.generate_function(&func, &TypeOwner::Interface(intf), variant); + self.generate_function(&func1, &TypeOwner::Interface(intf), variant); let func2 = Function { name: "[resource-drop]".to_string() + &name, diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index 29e727861..9d76daebc 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -35,15 +35,6 @@ 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 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) { - exports::foo::foo::resources::R::remove_resource(arg0); -} extern "C" int32_t fooX3AfooX2FresourcesX00create() { auto result0 = foo::foo::resources::Create(); return result0.release()->get_handle(); @@ -70,6 +61,15 @@ void exports::foo::foo::resources::R::Add(uint32_t b) const { fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd((*this).get_rep(), (int32_t(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) { + exports::foo::foo::resources::R::remove_resource(arg0); +} exports::foo::foo::resources::R::R(wit::ResourceExportBase&& b) : wit::ResourceExportBase(std::move(b)) {} exports::foo::foo::resources::R exports::foo::foo::resources::Create() { From dce8733a6e8e671b81777539b22b546c99d15797 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 12:50:53 +0200 Subject: [PATCH 195/672] fmt --- crates/cpp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 10c628834..3e8743b1a 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1917,7 +1917,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } } - if definition==self.gen.opts.host_side() { + if definition == self.gen.opts.host_side() { // consuming constructor from handle (bindings) uwriteln!(self.gen.h_src.src, "{pascal}({base_type} &&);",); uwriteln!(self.gen.h_src.src, "{pascal}({pascal}&&) = default;"); From 5d2e6fd2ea58eeaf5ebd45ee73bb46873bdfed60 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 13:13:34 +0200 Subject: [PATCH 196/672] now rep/new/drop is on the right object (host) --- crates/cpp/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 3e8743b1a..bb8114311 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1917,7 +1917,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } } - if definition == self.gen.opts.host_side() { + 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;"); @@ -1930,7 +1930,8 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> self.gen.c_src.src, "{pascal}::{pascal}({base_type}&&b) : {base_type}(std::move(b)) {{}}" ); - } else { + } + if matches!(variant, AbiVariant::GuestExport) { let func = Function { name: "[resource-new]".to_string() + &name, kind: FunctionKind::Static(id), From 59a49eff7f5347490986c3c5e6e05019c39a31af Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 14:56:35 +0200 Subject: [PATCH 197/672] correct dtor handling --- crates/cpp/src/lib.rs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index bb8114311..60ada4448 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -836,6 +836,7 @@ impl CppInterfaceGenerator<'_> { } } + /// This describes the C++ side name fn func_namespace_name( &self, func: &Function, @@ -879,7 +880,13 @@ impl CppInterfaceGenerator<'_> { "~".to_string() + &object } } - SpecialMethod::Dtor => "Dtor".to_string(), + 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(), @@ -1047,6 +1054,9 @@ impl CppInterfaceGenerator<'_> { && !(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()) { match &func.results { wit_bindgen_core::wit_parser::Results::Named(n) => { @@ -1092,6 +1102,9 @@ impl CppInterfaceGenerator<'_> { && !(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; } @@ -1101,7 +1114,10 @@ impl CppInterfaceGenerator<'_> { && (matches!(&func.kind, FunctionKind::Method(_)) || (matches!(&is_drop, SpecialMethod::ResourceDrop) && matches!(abi_variant, AbiVariant::GuestImport) - && !self.gen.opts.host_side())) + && !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; @@ -1184,8 +1200,8 @@ impl CppInterfaceGenerator<'_> { if cpp_sig.const_member { self.gen.h_src.src.push_str(" const"); } - match (&is_special, self.gen.opts.host_side()) { - (SpecialMethod::Allocate, _) => { + match (&is_special, self.gen.opts.host_side(), &variant) { + (SpecialMethod::Allocate, _, _) => { uwrite!( self.gen.h_src.src, "{{\ @@ -1202,7 +1218,8 @@ impl CppInterfaceGenerator<'_> { // body is inside the header return Vec::default(); } - (SpecialMethod::Dtor, _) | (SpecialMethod::ResourceDrop, true) => { + (SpecialMethod::Dtor, _, AbiVariant::GuestImport) + | (SpecialMethod::ResourceDrop, true, _) => { uwrite!( self.gen.h_src.src, "{{\ @@ -3299,6 +3316,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } +/// 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] From b5cbde93eb8e9187a3c5efd9b6e19c6f314abe35 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 15:22:49 +0200 Subject: [PATCH 198/672] proper import symbol --- crates/cpp/src/lib.rs | 18 +++++++++++++++--- .../native_resources/the_world_native.cpp | 4 ++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 60ada4448..483593909 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -940,6 +940,7 @@ impl CppInterfaceGenerator<'_> { }, }; let mut module_name = self.wasm_import_module.as_ref().map(|e| e.clone()); + let mut symbol_variant = variant; if matches!(variant, AbiVariant::GuestExport) && matches!( is_drop, @@ -949,6 +950,9 @@ impl CppInterfaceGenerator<'_> { ) { module_name = Some(String::from("[export]") + &module_name.unwrap()); + if self.gen.opts.host_side() { + symbol_variant = AbiVariant::GuestImport; + } } if self.gen.opts.short_cut { uwrite!(self.gen.c_src.src, "extern \"C\" "); @@ -977,7 +981,7 @@ impl CppInterfaceGenerator<'_> { }); self.gen.c_src.src.push_str(" "); let export_name = match module_name { - Some(ref module_name) => make_external_symbol(&module_name, &func.name, variant), + Some(ref module_name) => make_external_symbol(&module_name, &func.name, symbol_variant), None => make_external_component(&func.name), }; self.gen.c_src.src.push_str(&export_name); @@ -1154,7 +1158,7 @@ impl CppInterfaceGenerator<'_> { import: bool, ) -> Vec { let is_special = is_special_method(func); - if !(import == false + if !(import == true && self.gen.opts.host_side() && matches!( &is_special, @@ -1237,13 +1241,21 @@ impl CppInterfaceGenerator<'_> { // drop(cpp_sig); // we want to separate the lowered signature (wasm) and the high level signature - if !import + 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) diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index 9d76daebc..05fa44cd1 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -61,6 +61,8 @@ 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)); } @@ -70,8 +72,6 @@ extern "C" uint8_t* X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(int3 extern "C" void X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t arg0) { exports::foo::foo::resources::R::remove_resource(arg0); } -exports::foo::foo::resources::R::R(wit::ResourceExportBase&& b) - : wit::ResourceExportBase(std::move(b)) {} exports::foo::foo::resources::R exports::foo::foo::resources::Create() { auto ret = fooX3AfooX2FresourcesX23create(); return wit::ResourceExportBase{ret}; From 5d2dd2f2bd71e2ac885fd3e27b491036e096dd4b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 15:37:59 +0200 Subject: [PATCH 199/672] generate New on host guest import implementation --- crates/cpp/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 483593909..caeaa2bc7 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1929,8 +1929,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } { self.generate_function(func, &TypeOwner::Interface(intf), variant); if matches!(func.kind, FunctionKind::Constructor(_)) - && matches!(variant, AbiVariant::GuestExport) - && !self.gen.opts.host_side() + && 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 { From 4d08771fdd6b436f1c074e047c454f9b1624ac24 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 16:38:34 +0200 Subject: [PATCH 200/672] add method is now correct --- crates/cpp/src/lib.rs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index caeaa2bc7..1f8fd6e8b 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -900,6 +900,27 @@ impl CppInterfaceGenerator<'_> { (namespace, func_name_h) } + // local patching of borrows function needs more complex solution + fn patched_wasm_signature(&self, variant: AbiVariant, func: &Function) -> WasmSignature { + let res = self.resolve.wasm_signature(variant, func); + // 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)); + // } + // } + // } + // } + // } + // } + res + } + // print the signature of the guest export (lowered (wasm) function calling into highlevel) fn print_export_signature(&mut self, func: &Function, variant: AbiVariant) -> Vec { let is_drop = is_special_method(func); @@ -930,7 +951,7 @@ impl CppInterfaceGenerator<'_> { }, SpecialMethod::None => { // TODO perhaps remember better names for the arguments - self.resolve.wasm_signature(variant, func) + self.patched_wasm_signature(variant, func) } SpecialMethod::Allocate => WasmSignature { params: vec![], @@ -1503,7 +1524,7 @@ impl CppInterfaceGenerator<'_> { && matches!(variant, AbiVariant::GuestExport) && abi::guest_export_needs_post_return(self.resolve, func) { - let sig = self.resolve.wasm_signature(variant, 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) => make_external_symbol(module_name, &func.name, variant), @@ -3096,7 +3117,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { relative.qualify(&namespace); uwrite!( self.src, - "{}lookup_resource({this})->", + "(*{}lookup_resource({this}))->", relative.src.to_string() ); } else { From dcabc0c70065387be2c23454a9a2e7f6fce7364c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 19:00:58 +0200 Subject: [PATCH 201/672] host: import ctor should work --- crates/cpp/src/lib.rs | 26 ++++++++++++++++--- .../native_resources/the_world_native.cpp | 14 +++++----- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 1f8fd6e8b..76dab1c53 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -224,6 +224,10 @@ impl Opts { 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*" @@ -2630,7 +2634,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } => { let op = &operands[0]; if self.gen.gen.opts.host_side() { - results.push(format!("{op}.store_resource(std::move({op}))")); + if matches!(self.variant, AbiVariant::GuestImport) { + results.push(format!("{op}.get_handle()")); + } else { + results.push(format!("{op}.store_resource(std::move({op}))")); + } } else { results.push(format!("{op}.into_handle()")); } @@ -3137,6 +3145,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // 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(_)) { @@ -3148,7 +3161,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } self.push_str(&operands.join(", ")); if matches!(func.kind, FunctionKind::Constructor(_)) - && !self.gen.gen.opts.host_side() + && !self.gen.gen.opts.is_only_handle(self.variant) { // acquire object from unique_ptr self.push_str(").release();"); @@ -3164,7 +3177,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { _ => { assert!(*amt == operands.len()); match &func.kind { - FunctionKind::Constructor(_) if guest_import => { + FunctionKind::Constructor(_) + if self.gen.gen.opts.is_only_handle(self.variant) => + { // strange but works self.src.push_str("this->handle = "); } @@ -3221,7 +3236,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src.push_str(&format!(", ret, {})", cabi_post_name)); } } - if matches!(func.kind, FunctionKind::Constructor(_)) && guest_import { + if matches!(func.kind, FunctionKind::Constructor(_)) + && guest_import + && !self.gen.gen.opts.host_side() + { // we wrapped the handle in an object, so unpack it self.src.push_str(".into_handle()"); } diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index 05fa44cd1..bbc4525ee 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -28,8 +28,8 @@ extern "C" void fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t arg0) { 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(); + auto result0 = (foo::foo::resources::R::New((uint32_t(arg0)))).release(); + return (*(result0)).get_handle(); } extern "C" void fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(int32_t arg0, int32_t arg1) { @@ -63,13 +63,16 @@ void exports::foo::foo::resources::R::Add(uint32_t b) const { } exports::foo::foo::resources::R::R(wit::ResourceExportBase&& b) : wit::ResourceExportBase(std::move(b)) {} -extern "C" int32_t X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_newX5Dr(uint8_t *arg0) { +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) { +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) { +extern "C" void +X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t arg0) { exports::foo::foo::resources::R::remove_resource(arg0); } exports::foo::foo::resources::R exports::foo::foo::resources::Create() { @@ -81,7 +84,6 @@ void exports::foo::foo::resources::Borrows(std::reference_wrapper o) { } void exports::foo::foo::resources::Consume(R &&o) { auto rep = o.take_rep(); - //R::remove_resource(o.get_handle()); fooX3AfooX2FresourcesX23consume(o.get_handle()); } From 8406fd3a6e2fbc69d8957bfb15462e1240ad485f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 19:34:12 +0200 Subject: [PATCH 202/672] host side export dtor works --- crates/cpp/src/lib.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 76dab1c53..d95a5b663 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1414,9 +1414,24 @@ impl CppInterfaceGenerator<'_> { } }, SpecialMethod::Dtor => { - let classname = class_namespace(self, func, variant).join("::"); - uwriteln!(self.gen.c_src.src, "(({classname}*)arg0)->handle=-1;"); - uwriteln!(self.gen.c_src.src, "{0}::Dtor(({0}*)arg0);", classname); + 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("::"); + uwriteln!(self.gen.c_src.src, "(({classname}*)arg0)->handle=-1;"); + uwriteln!(self.gen.c_src.src, "{0}::Dtor(({0}*)arg0);", classname); + } } SpecialMethod::ResourceNew => { let module_name = String::from("[export]") From f9e8baacb004ca89ace963655fb677d6c8b0acf9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 19:50:00 +0200 Subject: [PATCH 203/672] host: export ctor --- crates/cpp/src/lib.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index d95a5b663..91b547765 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -3196,7 +3196,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if self.gen.gen.opts.is_only_handle(self.variant) => { // strange but works - self.src.push_str("this->handle = "); + if matches!(self.variant, AbiVariant::GuestExport) { + self.src.push_str("this->index = "); + } else { + self.src.push_str("this->handle = "); + } } _ => self.src.push_str("return "), } @@ -3252,11 +3256,17 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } if matches!(func.kind, FunctionKind::Constructor(_)) - && guest_import - && !self.gen.gen.opts.host_side() + && self.gen.gen.opts.is_only_handle(self.variant) { // we wrapped the handle in an object, so unpack it - self.src.push_str(".into_handle()"); + 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"); } From fc80eb238c321547014cbd39a9822b5ecb5f4dd6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 May 2024 20:10:39 +0200 Subject: [PATCH 204/672] host: export rep/drop/new --- crates/cpp/src/lib.rs | 112 ++++++++++-------- .../native_resources/the_world_native.cpp | 11 +- 2 files changed, 67 insertions(+), 56 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 91b547765..4c72b4bc2 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1401,16 +1401,26 @@ impl CppInterfaceGenerator<'_> { } } LiftLower::LowerArgsLiftResults => { - 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) {{ + 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); }}" - ); + ); + } } }, SpecialMethod::Dtor => { @@ -1434,53 +1444,53 @@ impl CppInterfaceGenerator<'_> { } } SpecialMethod::ResourceNew => { - 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 - ); - // let classname = class_namespace(self, func, variant).join("::"); - // let args = func - // .params - // .iter() - // .map(|(nm, _ty)| nm.clone()) - // .collect::>() - // .join(","); - // uwriteln!( - // self.gen.c_src.src, - // "return {classname}::New({args})->handle;" - // ); - // self.gen.c_src.src.push_str("// new logic: call r-new\n"); - //let f = Function { name: String::new(), kind: FunctionKind::Static(Id), params: Vec::new(), results: Vec::new(), docs: Docs::default() }; + 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 => { - 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 - ); + 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::Deallocate => self.gen.c_src.src.push_str("// deallocate\n"), SpecialMethod::None => { // normal methods let namespace = if matches!(func.kind, FunctionKind::Freestanding) { diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index bbc4525ee..2719714ea 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -28,7 +28,7 @@ extern "C" void fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t arg0) { foo::foo::resources::R::Dtor(*ptr); } extern "C" int32_t fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t arg0) { - auto result0 = (foo::foo::resources::R::New((uint32_t(arg0)))).release(); + auto result0 = foo::foo::resources::R::New((uint32_t(arg0))).release(); return (*(result0)).get_handle(); } extern "C" void fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(int32_t arg0, @@ -49,20 +49,21 @@ extern "C" void fooX3AfooX2FresourcesX00consume(int32_t arg0) { } exports::foo::foo::resources::R::~R() { - if (this->rep) + if (this->rep) { fooX3AfooX2FresourcesX23X5BdtorX5Dr(this->rep); + } } exports::foo::foo::resources::R::R(uint32_t a) { auto ret = fooX3AfooX2FresourcesX23X5BconstructorX5Dr((int32_t(a))); - this->index = ret; + this->index = wit::ResourceExportBase{ret}.get_handle(); this->rep = *lookup_resource(ret); } 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)) {} +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)); From 1d2c675e7ce79788a0cb8b85d17d6f81f5bbab25 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 7 May 2024 23:34:49 +0200 Subject: [PATCH 205/672] change compiler flags for runtime test --- tests/runtime/main.rs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/tests/runtime/main.rs b/tests/runtime/main.rs index e42bd5572..b5239c625 100644 --- a/tests/runtime/main.rs +++ b/tests/runtime/main.rs @@ -278,11 +278,6 @@ fn tests(name: &str, dir_name: &str) -> Result> { 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("utf16") { - // opts.string_encoding = wit_component::StringEncoding::UTF16; - // } - // } opts.build().generate(&resolve, world, &mut files).unwrap(); for (file, contents) in files.iter() { @@ -307,20 +302,17 @@ fn tests(name: &str, dir_name: &str) -> Result> { .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/helper-types")) + .arg(&(String::from(env!("CARGO_MANIFEST_DIR")) + "/crates/cpp/test_headers")) .arg("-Wall") .arg("-Wextra") - .arg("-Werror") +// .arg("-Werror") .arg("-Wno-unused-parameter") .arg("-mexec-model=reactor") .arg("-std=c++17") .arg("-g") .arg("-o") .arg(&out_wasm); - // Disable the warning about compiling a `.c` file in C++ mode. - // if compiler.ends_with("++") { - // cmd.arg("-Wno-deprecated"); - // } println!("{:?}", cmd); let output = match cmd.output() { Ok(output) => output, From 998c595a7db98ceae46aea65c4376b126a715c0a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 8 May 2024 00:22:14 +0200 Subject: [PATCH 206/672] properly pass rep to borrowing args on host --- crates/cpp/src/lib.rs | 6 +++++- tests/runtime/main.rs | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 4c72b4bc2..972fe9546 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2673,7 +2673,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { .. } => { let op = &operands[0]; - results.push(format!("{op}.get_handle()")); + if self.gen.gen.opts.host_side() { + results.push(format!("{op}.get_rep()")); + } else { + results.push(format!("{op}.get_handle()")); + } } abi::Instruction::HandleLift { handle, .. } => { let op = &operands[0]; diff --git a/tests/runtime/main.rs b/tests/runtime/main.rs index b5239c625..be02797e0 100644 --- a/tests/runtime/main.rs +++ b/tests/runtime/main.rs @@ -302,11 +302,11 @@ fn tests(name: &str, dir_name: &str) -> Result> { .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/helper-types")) .arg(&(String::from(env!("CARGO_MANIFEST_DIR")) + "/crates/cpp/test_headers")) .arg("-Wall") .arg("-Wextra") -// .arg("-Werror") + // .arg("-Werror") .arg("-Wno-unused-parameter") .arg("-mexec-model=reactor") .arg("-std=c++17") From 69f541bf3b437cec7c22dc148b84e7aa5c83e19d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 9 May 2024 14:52:41 +0200 Subject: [PATCH 207/672] dependency update --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 39b90df12..7b3a71b4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2378,7 +2378,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.206.0", + "wasm-encoder 0.207.0", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", From ea5b857546fbc7cba190594d28c2ce38c08d48ed Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 9 May 2024 15:21:22 +0200 Subject: [PATCH 208/672] regenerate (still works) --- .../native_strings/rust/src/the_world.rs | 6 ++--- crates/cpp/tests/native_strings/the_world.cpp | 23 ++++++++++++++----- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/crates/cpp/tests/native_strings/rust/src/the_world.rs b/crates/cpp/tests/native_strings/rust/src/the_world.rs index db636d098..5c05d404b 100644 --- a/crates/cpp/tests/native_strings/rust/src/the_world.rs +++ b/crates/cpp/tests/native_strings/rust/src/the_world.rs @@ -1,4 +1,4 @@ -// Generated by `wit-bindgen` 0.23.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.24.0. DO NOT EDIT! // Options used: #[allow(dead_code)] pub mod foo { @@ -244,7 +244,7 @@ macro_rules! __export_the_world_impl { pub(crate) use __export_the_world_impl as export; #[cfg(target_arch = "wasm32")] -#[link_section = "component-type:wit-bindgen:0.23.0:the-world:encoded world"] +#[link_section = "component-type:wit-bindgen:0.24.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\ @@ -253,7 +253,7 @@ A\x04\x01B\x06\x01@\x01\x01xs\x01\0\x04\0\x01a\x01\0\x01@\0\0s\x04\0\x01b\x01\x0 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.202.0\x10wit-bindgen-rust\x060.23.0"; +processed-by\x02\x0dwit-component\x070.207.0\x10wit-bindgen-rust\x060.24.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/cpp/tests/native_strings/the_world.cpp b/crates/cpp/tests/native_strings/the_world.cpp index c90a75210..be592888d 100644 --- a/crates/cpp/tests/native_strings/the_world.cpp +++ b/crates/cpp/tests/native_strings/the_world.cpp @@ -1,4 +1,13 @@ // 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 // realloc @@ -73,9 +82,10 @@ fooX3AfooX2FstringsX23b() { *((uint8_t **)(ptr1 + 0)) = ptr2; return ptr1; } -extern "C" __attribute__((__weak__, - __export_name__("cabi_post_foo:foo/strings#b"))) void -cabi_post_fooX3AfooX2FstringsX23b(uint8_t *arg0) { +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)))); } @@ -101,9 +111,10 @@ fooX3AfooX2FstringsX23c(uint8_t *arg0, size_t arg1, uint8_t *arg2, *((uint8_t **)(ptr3 + 0)) = ptr4; return ptr3; } -extern "C" __attribute__((__weak__, - __export_name__("cabi_post_foo:foo/strings#c"))) void -cabi_post_fooX3AfooX2FstringsX23c(uint8_t *arg0) { +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)))); } From cd1db36c3723c2022da3863addef926b4bec5a90 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 9 May 2024 15:35:35 +0200 Subject: [PATCH 209/672] fix guest import borrow --- crates/cpp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 972fe9546..a1a5806ed 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2676,7 +2676,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if self.gen.gen.opts.host_side() { results.push(format!("{op}.get_rep()")); } else { - results.push(format!("{op}.get_handle()")); + results.push(format!("{op}.get().get_handle()")); } } abi::Instruction::HandleLift { handle, .. } => { From aa7f4d0e0ef9570cb1f643d82c1116fe1be7877f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 9 May 2024 16:31:10 +0200 Subject: [PATCH 210/672] get two more guest resource usages right --- crates/cpp/src/lib.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index a1a5806ed..588db8312 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2665,7 +2665,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { results.push(format!("{op}.store_resource(std::move({op}))")); } } else { - results.push(format!("{op}.into_handle()")); + if matches!(self.variant, AbiVariant::GuestImport) { + results.push(format!("{op}.into_handle()")); + } else { + results.push(format!("{op}.release()->handle")); + } } } abi::Instruction::HandleLower { @@ -2675,6 +2679,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let op = &operands[0]; if self.gen.gen.opts.host_side() { results.push(format!("{op}.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()")); } @@ -3189,7 +3196,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } self.push_str(&operands.join(", ")); - if matches!(func.kind, FunctionKind::Constructor(_)) + if false + && matches!(func.kind, FunctionKind::Constructor(_)) && !self.gen.gen.opts.is_only_handle(self.variant) { // acquire object from unique_ptr From 7753930c12da335e84fbc34606405babb173c3fa Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 9 May 2024 17:29:33 +0200 Subject: [PATCH 211/672] fix exported consuming funcs --- crates/cpp/src/lib.rs | 22 +++++++++++++++++-- .../native_resources/guest/the_world.cpp | 12 +++++----- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 588db8312..1b0518d64 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2692,8 +2692,26 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { (Handle::Own(_), true) => { results.push(format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}{{{op}}}")) } - (Handle::Own(_), false) => { - results.push(format!("wit::{RESOURCE_IMPORT_BASE_CLASS_NAME}{{{op}}}")) + (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})); + {var}->into_handle();" + ); + results.push(format!("std::move({var})")) + } + } } (Handle::Borrow(_), _) => results.push(op.clone()), } diff --git a/crates/cpp/tests/native_resources/guest/the_world.cpp b/crates/cpp/tests/native_resources/guest/the_world.cpp index c6c842334..8f0219705 100644 --- a/crates/cpp/tests/native_resources/guest/the_world.cpp +++ b/crates/cpp/tests/native_resources/guest/the_world.cpp @@ -86,9 +86,8 @@ fooX3AfooX2FresourcesX23X5BdtorX5Dr(uint8_t *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))).release(); - return (*(result0)).into_handle(); + 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 @@ -118,9 +117,10 @@ fooX3AfooX2FresourcesX23borrows(int8_t* arg0) { } extern "C" __attribute__((__export_name__("foo:foo/resources#consume"))) void fooX3AfooX2FresourcesX23consume(int32_t arg0) { - auto obj = exports::foo::foo::resources::R::Owned(exports::foo::foo::resources::R::ResourceRep(arg0)); - obj->into_handle(); - exports::foo::foo::resources::Consume(std::move(obj)); + 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 From 02cca33ec5c94746898feadc18d0a3c1b708e765 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 10 May 2024 08:48:43 +0200 Subject: [PATCH 212/672] bring native reference closer to generated code --- .../native_resources/the_world_native.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index 2719714ea..22857dfb7 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -1,11 +1,10 @@ // Generated by `wit-bindgen` 0.3.0. DO NOT EDIT! #include "the_world_cpp_native.h" -#include template std::map wit::ResourceTable::resources; - +#include extern "C" __attribute__((import_module("foo:foo/resources"))) -__attribute__((import_name("[dtor]r"))) -void fooX3AfooX2FresourcesX23X5BdtorX5Dr(uint8_t*); +__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); @@ -21,15 +20,14 @@ __attribute__((import_name("borrows"))) void 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))).release(); - return (*(result0)).get_handle(); + 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) { @@ -47,7 +45,6 @@ extern "C" void fooX3AfooX2FresourcesX00consume(int32_t arg0) { assert(objptr.has_value()); foo::foo::resources::Consume(foo::foo::resources::R::Owned(*objptr)); } - exports::foo::foo::resources::R::~R() { if (this->rep) { fooX3AfooX2FresourcesX23X5BdtorX5Dr(this->rep); @@ -59,11 +56,10 @@ exports::foo::foo::resources::R::R(uint32_t a) { this->rep = *lookup_resource(ret); } void exports::foo::foo::resources::R::Add(uint32_t b) const { - fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd((*this).get_rep(), - (int32_t(b))); + fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd((*this).get_rep(), (int32_t(b))); } exports::foo::foo::resources::R::R(wit::ResourceExportBase &&b) - : wit::ResourceExportBase(std::move(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)); From 04e4398010e1c16e9b8e7f426d307c09bfd08b64 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 12 May 2024 16:34:57 +0200 Subject: [PATCH 213/672] proper release of resources returned --- crates/cpp/src/lib.rs | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 1b0518d64..d5de40b4d 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2660,7 +2660,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let op = &operands[0]; if self.gen.gen.opts.host_side() { if matches!(self.variant, AbiVariant::GuestImport) { - results.push(format!("{op}.get_handle()")); + results.push(format!("{op}.release()->get_handle()")); } else { results.push(format!("{op}.store_resource(std::move({op}))")); } @@ -2692,27 +2692,26 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { (Handle::Own(_), true) => { results.push(format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}{{{op}}}")) } - (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})); + (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})); {var}->into_handle();" - ); - results.push(format!("std::move({var})")) - } + ); + results.push(format!("std::move({var})")) } - } + }, (Handle::Borrow(_), _) => results.push(op.clone()), } } From 3498f46fb2a3680795e157bc5bdf5f041b3bf81b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 12 May 2024 17:54:51 +0200 Subject: [PATCH 214/672] mostly working host resource codegen --- crates/cpp/src/lib.rs | 48 +++++++++++++++---- .../native_resources/the_world_native.cpp | 13 ++--- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index d5de40b4d..8849ec2f6 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2662,7 +2662,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if matches!(self.variant, AbiVariant::GuestImport) { results.push(format!("{op}.release()->get_handle()")); } else { - results.push(format!("{op}.store_resource(std::move({op}))")); + 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) { @@ -2678,7 +2681,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } => { let op = &operands[0]; if self.gen.gen.opts.host_side() { - results.push(format!("{op}.get_rep()")); + 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()")); @@ -2689,9 +2692,26 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::HandleLift { handle, .. } => { let op = &operands[0]; match (handle, self.gen.gen.opts.host_side()) { - (Handle::Own(_), true) => { - results.push(format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}{{{op}}}")) - } + (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})")); + } + }, (Handle::Own(ty), false) => match self.variant { AbiVariant::GuestImport => { results.push(format!("wit::{RESOURCE_IMPORT_BASE_CLASS_NAME}{{{op}}}")) @@ -2712,7 +2732,15 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { results.push(format!("std::move({var})")) } }, - (Handle::Borrow(_), _) => results.push(op.clone()), + (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(_), false) => results.push(op.clone()), } } abi::Instruction::TupleLower { tuple, .. } => { @@ -3174,12 +3202,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { //self.gen.gen.c_src.qualify(&namespace); // relative.namespace = self.namespace.clone(); if self.gen.gen.opts.host_side() { - let mut relative = SourceWithState::default(); - relative.qualify(&namespace); + // let mut relative = SourceWithState::default(); + // relative.qualify(&namespace); uwrite!( self.src, - "(*{}lookup_resource({this}))->", - relative.src.to_string() + "({this}).", + // relative.src.to_string() ); } else { let objtype = namespace.join("::"); diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index 22857dfb7..d8a345320 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -31,7 +31,7 @@ extern "C" int32_t fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t arg0) { } extern "C" void fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(int32_t arg0, int32_t arg1) { - (*foo::foo::resources::R::lookup_resource(arg0))->Add((uint32_t(arg1))); + (**foo::foo::resources::R::lookup_resource(arg0)).Add((uint32_t(arg1))); } extern "C" int32_t fooX3AfooX2FresourcesX00create() { auto result0 = foo::foo::resources::Create(); @@ -41,9 +41,9 @@ 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 objptr = foo::foo::resources::R::remove_resource(arg0); - assert(objptr.has_value()); - foo::foo::resources::Consume(foo::foo::resources::R::Owned(*objptr)); + auto obj0 = foo::foo::resources::R::remove_resource(arg0); + assert(obj0.has_value()); + foo::foo::resources::Consume(foo::foo::resources::R::Owned(*obj0)); } exports::foo::foo::resources::R::~R() { if (this->rep) { @@ -56,7 +56,8 @@ exports::foo::foo::resources::R::R(uint32_t a) { this->rep = *lookup_resource(ret); } void exports::foo::foo::resources::R::Add(uint32_t b) const { - fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd((*this).get_rep(), (int32_t(b))); + fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd((*this).get_rep(), + (int32_t(b))); } exports::foo::foo::resources::R::R(wit::ResourceExportBase &&b) : wit::ResourceExportBase(std::move(b)) {} @@ -80,7 +81,7 @@ void exports::foo::foo::resources::Borrows(std::reference_wrapper o) { fooX3AfooX2FresourcesX23borrows(o.get().get_rep()); } void exports::foo::foo::resources::Consume(R &&o) { - auto rep = o.take_rep(); + auto rep0 = o.take_rep(); fooX3AfooX2FresourcesX23consume(o.get_handle()); } From 78d2a2fc60b81668a91b25cadd6c81ba868a1019 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 12 May 2024 17:58:56 +0200 Subject: [PATCH 215/672] fix small glitch --- crates/cpp/src/lib.rs | 6 +++++- crates/cpp/tests/native_resources/the_world_native.cpp | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 8849ec2f6..34ec011de 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2681,7 +2681,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } => { let op = &operands[0]; if self.gen.gen.opts.host_side() { - results.push(format!("{op}.get().get_rep()")); + 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()")); diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index d8a345320..c9c5319f7 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -56,8 +56,7 @@ exports::foo::foo::resources::R::R(uint32_t a) { this->rep = *lookup_resource(ret); } void exports::foo::foo::resources::R::Add(uint32_t b) const { - fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd((*this).get_rep(), - (int32_t(b))); + fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd((*this).get_rep(), (int32_t(b))); } exports::foo::foo::resources::R::R(wit::ResourceExportBase &&b) : wit::ResourceExportBase(std::move(b)) {} From 15ba30473d92d93a2de47968819be581fa05827c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 12 May 2024 18:19:44 +0200 Subject: [PATCH 216/672] generated code isn't that pretty but it works --- crates/cpp/src/lib.rs | 27 ++++++++++--------- .../native_resources/guest/the_world.cpp | 9 ++++--- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 34ec011de..6bc7865bc 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2744,7 +2744,17 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { ); results.push(format!("**{tname}::lookup_resource({op})")); } - (Handle::Borrow(_), false) => results.push(op.clone()), + (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})")); + } + }, } } abi::Instruction::TupleLower { tuple, .. } => { @@ -3203,19 +3213,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { .func_namespace_name(func, !self.gen.gen.opts.host_side(), true); if matches!(func.kind, FunctionKind::Method(_)) { let this = operands.remove(0); - //self.gen.gen.c_src.qualify(&namespace); - // relative.namespace = self.namespace.clone(); if self.gen.gen.opts.host_side() { - // let mut relative = SourceWithState::default(); - // relative.qualify(&namespace); - uwrite!( - self.src, - "({this}).", - // relative.src.to_string() - ); + uwrite!(self.src, "({this})."); } else { - let objtype = namespace.join("::"); - uwrite!(self.src, "(({objtype}*){this})->",); + //let objtype = namespace.join("::"); + uwrite!(self.src, "({this}).get()."); + // uwrite!(self.src, "(({objtype}*){this})->",); } } else { if matches!(func.kind, FunctionKind::Constructor(_)) diff --git a/crates/cpp/tests/native_resources/guest/the_world.cpp b/crates/cpp/tests/native_resources/guest/the_world.cpp index 8f0219705..8f10466ca 100644 --- a/crates/cpp/tests/native_resources/guest/the_world.cpp +++ b/crates/cpp/tests/native_resources/guest/the_world.cpp @@ -92,7 +92,9 @@ extern "C" extern "C" __attribute__((__export_name__("foo:foo/resources#[method]r.add"))) void fooX3AfooX2FresourcesX23X5BmethodX5DrX2Eadd(uint8_t *arg0, int32_t arg1) { - ((exports::foo::foo::resources::R *)arg0)->Add((uint32_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( @@ -112,8 +114,9 @@ fooX3AfooX2FresourcesX23create() { return result0.release()->handle; } extern "C" __attribute__((__export_name__("foo:foo/resources#borrows"))) void -fooX3AfooX2FresourcesX23borrows(int8_t* arg0) { - exports::foo::foo::resources::Borrows(std::cref(*(exports::foo::foo::resources::R const*)arg0)); +fooX3AfooX2FresourcesX23borrows(int32_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) { From 0197779538b81468d3bce05395787da6babc1d24 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Wed, 1 May 2024 19:21:18 -0600 Subject: [PATCH 217/672] add future and stream support Signed-off-by: Joel Dice --- Cargo.toml | 10 +- crates/core/src/abi.rs | 129 ++++++++-- crates/core/src/lib.rs | 9 +- crates/guest-rust/macro/src/lib.rs | 16 +- crates/rust/src/async_support.rs | 296 ++++++++++++++++++++-- crates/rust/src/bindgen.rs | 87 +++++-- crates/rust/src/interface.rs | 386 ++++++++++++++++++++++++++--- crates/rust/src/lib.rs | 15 ++ src/bin/wit-bindgen.rs | 1 + 9 files changed, 829 insertions(+), 120 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d16c03ae8..decf552f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,11 +29,11 @@ clap = { version = "4.3.19", features = ["derive"] } env_logger = "0.10.0" indexmap = "2.0.0" -wasmparser = "0.201.0" -wasm-encoder = "0.201.0" -wasm-metadata = "0.201.0" -wit-parser = "0.201.0" -wit-component = "0.201.0" +wasmparser = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wasm-encoder = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wasm-metadata = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wit-parser = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wit-component = { git = "https://github.com/dicej/wasm-tools", branch = "async" } wit-bindgen-core = { path = 'crates/core', version = '0.22.0' } wit-bindgen-c = { path = 'crates/c', version = '0.22.0' } diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 4533b57e7..a8541ddc4 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -350,6 +350,40 @@ def_instruction! { ty: TypeId, } : [1] => [1], + /// Create an `i32` from a future. + FutureLower { + payload: &'a Option, + ty: TypeId, + } : [1] => [1], + + /// Create a future from an `i32`. + FutureLift { + payload: &'a Option, + ty: TypeId, + } : [1] => [1], + + /// Create an `i32` from a stream. + StreamLower { + payload: &'a Type, + ty: TypeId, + } : [1] => [1], + + /// Create a stream from an `i32`. + StreamLift { + payload: &'a Type, + ty: TypeId, + } : [1] => [1], + + /// Create an `i32` from an error. + ErrorLower { + ty: TypeId, + } : [1] => [1], + + /// Create a error from an `i32`. + ErrorLift { + ty: TypeId, + } : [1] => [1], + /// Pops a tuple value off the stack, decomposes the tuple to all of /// its fields, and then pushes the fields onto the stack. TupleLower { @@ -534,6 +568,8 @@ def_instruction! { AsyncPostCallInterface { func: &'a Function } : [1] => [func.results.len() + 1], AsyncCallReturn { name: &'a str, params: &'a [WasmType] } : [params.len()] => [0], + + Flush { amt: usize } : [*amt] => [*amt], } } @@ -751,7 +787,9 @@ fn needs_post_return(resolve: &Resolve, ty: &Type) -> bool { .filter_map(|t| t.as_ref()) .any(|t| needs_post_return(resolve, t)), TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => false, - TypeDefKind::Future(_) | TypeDefKind::Stream(_) => unimplemented!(), + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Error => { + unimplemented!() + } TypeDefKind::Unknown => unreachable!(), }, @@ -764,8 +802,8 @@ fn needs_post_return(resolve: &Resolve, ty: &Type) -> bool { | Type::S32 | Type::U64 | Type::S64 - | Type::Float32 - | Type::Float64 + | Type::F32 + | Type::F64 | Type::Char => false, } } @@ -867,6 +905,9 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); self.stack.pop().unwrap() } + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } }; lower_to_memory(self, ptr); } @@ -920,7 +961,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { let ptr = match self.variant { // imports into guests means it's a wasm module // calling an imported function. We supplied the - // return poitner as the last argument (saved in + // return pointer as the last argument (saved in // `self.return_pointer`) so we use that to read // the result of the function from memory. AbiVariant::GuestImport => { @@ -932,9 +973,16 @@ impl<'a, B: Bindgen> Generator<'a, B> { // calling wasm so wasm returned a pointer to where // the result is stored AbiVariant::GuestExport => self.stack.pop().unwrap(), + + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } }; self.read_results_from_memory(&func.results, ptr.clone(), 0); + self.emit(&Instruction::Flush { + amt: func.results.len(), + }); if let Some((size, align)) = dealloc_size_align { self.stack.push(ptr); @@ -1088,6 +1136,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.write_params_to_memory(func.results.iter_types(), ptr.clone(), 0); self.stack.push(ptr); } + + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } } } @@ -1204,8 +1256,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { Type::S64 => self.emit(&I64FromS64), Type::U64 => self.emit(&I64FromU64), Type::Char => self.emit(&I32FromChar), - Type::Float32 => self.emit(&F32FromFloat32), - Type::Float64 => self.emit(&F64FromFloat64), + Type::F32 => self.emit(&F32FromFloat32), + Type::F64 => self.emit(&F64FromFloat64), Type::String => { let realloc = self.list_realloc(); self.emit(&StringLower { realloc }); @@ -1305,8 +1357,21 @@ impl<'a, B: Bindgen> Generator<'a, B> { results: &results, }); } - TypeDefKind::Future(_) => todo!("lower future"), - TypeDefKind::Stream(_) => todo!("lower stream"), + TypeDefKind::Future(ty) => { + self.emit(&FutureLower { + payload: ty, + ty: id, + }); + } + TypeDefKind::Stream(ty) => { + self.emit(&StreamLower { + payload: ty, + ty: id, + }); + } + TypeDefKind::Error => { + self.emit(&ErrorLower { ty: id }); + } TypeDefKind::Unknown => unreachable!(), }, } @@ -1393,8 +1458,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { Type::S64 => self.emit(&S64FromI64), Type::U64 => self.emit(&U64FromI64), Type::Char => self.emit(&CharFromI32), - Type::Float32 => self.emit(&Float32FromF32), - Type::Float64 => self.emit(&Float64FromF64), + Type::F32 => self.emit(&Float32FromF32), + Type::F64 => self.emit(&Float64FromF64), Type::String => self.emit(&StringLift), Type::Id(id) => match &self.resolve.types[id].kind { TypeDefKind::Type(t) => self.lift(t), @@ -1490,8 +1555,21 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&ResultLift { result: r, ty: id }); } - TypeDefKind::Future(_) => todo!("lift future"), - TypeDefKind::Stream(_) => todo!("lift stream"), + TypeDefKind::Future(ty) => { + self.emit(&FutureLift { + payload: ty, + ty: id, + }); + } + TypeDefKind::Stream(ty) => { + self.emit(&StreamLift { + payload: ty, + ty: id, + }); + } + TypeDefKind::Error => { + self.emit(&ErrorLift { ty: id }); + } TypeDefKind::Unknown => unreachable!(), }, } @@ -1551,15 +1629,18 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.lower_and_emit(ty, addr, &I32Store { offset }) } Type::U64 | Type::S64 => self.lower_and_emit(ty, addr, &I64Store { offset }), - Type::Float32 => self.lower_and_emit(ty, addr, &F32Store { offset }), - Type::Float64 => self.lower_and_emit(ty, addr, &F64Store { offset }), + Type::F32 => self.lower_and_emit(ty, addr, &F32Store { offset }), + Type::F64 => self.lower_and_emit(ty, addr, &F64Store { offset }), Type::String => self.write_list_to_memory(ty, addr, offset), Type::Id(id) => match &self.resolve.types[id].kind { TypeDefKind::Type(t) => self.write_to_memory(t, addr, offset), TypeDefKind::List(_) => self.write_list_to_memory(ty, addr, offset), - TypeDefKind::Handle(_) => self.lower_and_emit(ty, addr, &I32Store { offset }), + TypeDefKind::Future(_) + | TypeDefKind::Stream(_) + | TypeDefKind::Error + | TypeDefKind::Handle(_) => self.lower_and_emit(ty, addr, &I32Store { offset }), // Decompose the record into its components and then write all // the components into memory one-by-one. @@ -1649,8 +1730,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.store_intrepr(offset, e.tag()); } - TypeDefKind::Future(_) => todo!("write future to memory"), - TypeDefKind::Stream(_) => todo!("write stream to memory"), TypeDefKind::Unknown => unreachable!(), }, } @@ -1739,8 +1818,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { Type::S16 => self.emit_and_lift(ty, addr, &I32Load16S { offset }), Type::U32 | Type::S32 | Type::Char => self.emit_and_lift(ty, addr, &I32Load { offset }), Type::U64 | Type::S64 => self.emit_and_lift(ty, addr, &I64Load { offset }), - Type::Float32 => self.emit_and_lift(ty, addr, &F32Load { offset }), - Type::Float64 => self.emit_and_lift(ty, addr, &F64Load { offset }), + Type::F32 => self.emit_and_lift(ty, addr, &F32Load { offset }), + Type::F64 => self.emit_and_lift(ty, addr, &F64Load { offset }), Type::String => self.read_list_from_memory(ty, addr, offset), Type::Id(id) => match &self.resolve.types[id].kind { @@ -1748,7 +1827,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::List(_) => self.read_list_from_memory(ty, addr, offset), - TypeDefKind::Handle(_) => self.emit_and_lift(ty, addr, &I32Load { offset }), + TypeDefKind::Future(_) + | TypeDefKind::Stream(_) + | TypeDefKind::Error + | TypeDefKind::Handle(_) => self.emit_and_lift(ty, addr, &I32Load { offset }), TypeDefKind::Resource => { todo!(); @@ -1832,8 +1914,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.lift(ty); } - TypeDefKind::Future(_) => todo!("read future from memory"), - TypeDefKind::Stream(_) => todo!("read stream from memory"), TypeDefKind::Unknown => unreachable!(), }, } @@ -1936,8 +2016,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { | Type::Char | Type::U64 | Type::S64 - | Type::Float32 - | Type::Float64 => {} + | Type::F32 + | Type::F64 => {} Type::Id(id) => match &self.resolve.types[id].kind { TypeDefKind::Type(t) => self.deallocate(t, addr, offset), @@ -2004,6 +2084,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::Future(_) => todo!("read future from memory"), TypeDefKind::Stream(_) => todo!("read stream from memory"), + TypeDefKind::Error => todo!("read error from memory"), TypeDefKind::Unknown => unreachable!(), }, } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 378d4f340..c4ad0abda 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -208,13 +208,7 @@ impl Types { info = self.optional_type_info(resolve, r.ok.as_ref()); info |= self.optional_type_info(resolve, r.err.as_ref()); } - TypeDefKind::Future(ty) => { - info = self.optional_type_info(resolve, ty.as_ref()); - } - TypeDefKind::Stream(stream) => { - info = self.optional_type_info(resolve, stream.element.as_ref()); - info |= self.optional_type_info(resolve, stream.end.as_ref()); - } + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Error => {} TypeDefKind::Unknown => unreachable!(), } let prev = self.type_info.insert(ty, info); @@ -396,6 +390,7 @@ pub trait InterfaceGenerator<'a> { TypeDefKind::Future(_) => todo!("generate for future"), TypeDefKind::Stream(_) => todo!("generate for stream"), TypeDefKind::Handle(_) => todo!("generate for handle"), + TypeDefKind::Error => todo!("generate for error"), TypeDefKind::Unknown => unreachable!(), } } diff --git a/crates/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs index 74b2c2ad6..a4518d4c7 100644 --- a/crates/guest-rust/macro/src/lib.rs +++ b/crates/guest-rust/macro/src/lib.rs @@ -30,6 +30,7 @@ struct Config { resolve: Resolve, world: WorldId, files: Vec, + debug: bool, } /// The source of the wit package definition @@ -47,6 +48,7 @@ impl Parse for Config { let mut world = None; let mut source = None; let mut async_configured = false; + let mut debug = false; if input.peek(token::Brace) { let content; @@ -114,6 +116,9 @@ impl Parse for Config { Opt::PubExportMacro(enable) => { opts.pub_export_macro = enable.value(); } + Opt::Debug(enable) => { + debug = enable.value(); + } Opt::Async(val, span) => { if async_configured { return Err(Error::new(span, "cannot specify second async config")); @@ -139,6 +144,7 @@ impl Parse for Config { resolve, world, files, + debug, }) } } @@ -164,6 +170,8 @@ fn parse_source(source: &Option) -> anyhow::Result<(Resolve, PackageId, None => parse(&root.join("wit"))?, }; + resolve.add_future_and_stream_results(); + Ok((resolve, pkg, files)) } @@ -181,7 +189,7 @@ impl Config { // place a formatted version of the expanded code into a file. This file // will then show up in rustc error messages for any codegen issues and can // be inspected manually. - if std::env::var("WIT_BINDGEN_DEBUG").is_ok() { + if std::env::var("WIT_BINDGEN_DEBUG").is_ok() || self.debug { static INVOCATION: AtomicUsize = AtomicUsize::new(0); let root = Path::new(env!("DEBUG_OUTPUT_DIR")); let world_name = &self.resolve.worlds[self.world].name; @@ -240,6 +248,7 @@ mod kw { syn::custom_keyword!(export_macro_name); syn::custom_keyword!(pub_export_macro); syn::custom_keyword!(imports); + syn::custom_keyword!(debug); } #[derive(Clone)] @@ -295,6 +304,7 @@ enum Opt { ExportMacroName(syn::LitStr), PubExportMacro(syn::LitBool), Async(AsyncConfig, Span), + Debug(syn::LitBool), } impl Parse for Opt { @@ -413,6 +423,10 @@ impl Parse for Opt { input.parse::()?; input.parse::()?; Ok(Opt::PubExportMacro(input.parse()?)) + } else if l.peek(kw::debug) { + input.parse::()?; + input.parse::()?; + Ok(Opt::Debug(input.parse()?)) } else if l.peek(Token![async]) { let span = input.parse::()?.span; input.parse::()?; diff --git a/crates/rust/src/async_support.rs b/crates/rust/src/async_support.rs index 53ac54275..3ef923220 100644 --- a/crates/rust/src/async_support.rs +++ b/crates/rust/src/async_support.rs @@ -1,10 +1,18 @@ use { - futures::{channel::oneshot, future::FutureExt}, + futures::{ + channel::oneshot, + future::FutureExt, + sink::Sink, + stream::{FuturesUnordered, Stream, StreamExt}, + }, once_cell::sync::Lazy, std::{ - alloc::Layout, + alloc::{self, Layout}, collections::HashMap, - future::Future, + fmt::{self, Debug, Display}, + future::{Future, IntoFuture}, + marker::PhantomData, + mem::ManuallyDrop, pin::{pin, Pin}, ptr, sync::Arc, @@ -14,10 +22,12 @@ use { type BoxFuture = Pin + 'static>>; -struct FutureState(BoxFuture); +struct FutureState(FuturesUnordered); static mut CALLS: Lazy>> = Lazy::new(HashMap::new); +static mut SPAWNED: Vec = Vec::new(); + fn dummy_waker() -> Waker { struct DummyWaker; @@ -30,18 +40,34 @@ fn dummy_waker() -> Waker { WAKER.clone().into() } +unsafe fn poll(state: *mut FutureState) -> Poll<()> { + loop { + let poll = pin!((*state).0.next()).poll(&mut Context::from_waker(&dummy_waker())); + + if SPAWNED.is_empty() { + match poll { + Poll::Ready(Some(())) => (), + Poll::Ready(None) => break Poll::Ready(()), + Poll::Pending => break Poll::Pending, + } + } else { + (*state).0.extend(SPAWNED.drain(..)); + } + } +} + pub fn first_poll( future: impl Future + 'static, fun: impl FnOnce(T) + 'static, ) -> *mut u8 { - let mut future = Box::pin(future.map(fun)) as BoxFuture; - - match future - .as_mut() - .poll(&mut Context::from_waker(&dummy_waker())) - { + let state = Box::into_raw(Box::new(FutureState( + [Box::pin(future.map(fun)) as BoxFuture] + .into_iter() + .collect(), + ))); + match unsafe { poll(state) } { Poll::Ready(()) => ptr::null_mut(), - Poll::Pending => Box::into_raw(Box::new(FutureState(future))) as _, + Poll::Pending => state as _, } } @@ -91,13 +117,8 @@ pub unsafe fn callback(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i if let Some(call) = CALLS.remove(&event1) { call.send(()); - match (*(ctx as *mut FutureState)) - .0 - .as_mut() - .poll(&mut Context::from_waker(&dummy_waker())) - { + match poll(ctx as *mut FutureState) { Poll::Ready(()) => { - // TODO: consider spawned task before returning "done" here drop(Box::from_raw(ctx as *mut FutureState)); 1 } @@ -110,3 +131,244 @@ pub unsafe fn callback(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i _ => unreachable!(), } } + +#[doc(hidden)] +pub trait FuturePayload: Sized + 'static { + fn new() -> (u32, u32); + async fn send(sender: u32, value: Self) -> Result<(), Error>; + async fn receive(receiver: u32) -> Result; + fn drop_sender(sender: u32); + fn drop_receiver(receiver: u32); +} + +pub struct FutureSender { + handle: u32, + _phantom: PhantomData, +} + +impl FutureSender { + pub async fn send(self, v: T) -> Result<(), Error> { + T::send(ManuallyDrop::new(self).handle, v).await + } +} + +impl Drop for FutureSender { + fn drop(&mut self) { + T::drop_sender(self.handle) + } +} + +pub struct FutureReceiver { + handle: u32, + _phantom: PhantomData, +} + +impl FutureReceiver { + #[doc(hidden)] + pub fn into_handle(self) -> u32 { + ManuallyDrop::new(self).handle + } +} + +impl IntoFuture for FutureReceiver { + type Output = Result; + type IntoFuture = Pin + 'static>>; + + fn into_future(self) -> Self::IntoFuture { + Box::pin(T::receive(ManuallyDrop::new(self).handle)) + } +} + +impl Drop for FutureReceiver { + fn drop(&mut self) { + T::drop_receiver(self.handle) + } +} + +#[doc(hidden)] +pub trait StreamPayload: Unpin + Sized + 'static { + fn new() -> (u32, u32); + async fn send(sender: u32, values: Vec) -> Result<(), Error>; + async fn receive(receiver: u32) -> Option, Error>>; + fn drop_sender(sender: u32); + fn drop_receiver(receiver: u32); +} + +pub struct StreamSender { + handle: u32, + future: Option> + 'static>>>, + _phantom: PhantomData, +} + +impl Sink> for StreamSender { + type Error = Error; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let me = self.get_mut(); + + if let Some(future) = &mut me.future { + match future.as_mut().poll(cx) { + Poll::Ready(v) => { + me.future = None; + Poll::Ready(v) + } + Poll::Pending => Poll::Pending, + } + } else { + Poll::Ready(Ok(())) + } + } + + fn start_send(self: Pin<&mut Self>, item: Vec) -> Result<(), Self::Error> { + assert!(self.future.is_none()); + self.get_mut().future = Some(Box::pin(T::send(self.handle, item))); + Ok(()) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } +} + +impl Drop for StreamSender { + fn drop(&mut self) { + T::drop_sender(self.handle) + } +} + +pub struct StreamReceiver { + handle: u32, + future: Option, Error>>> + 'static>>>, + _phantom: PhantomData, +} + +impl StreamReceiver { + #[doc(hidden)] + pub fn from_handle(handle: u32) -> Self { + Self { + handle, + future: None, + _phantom: PhantomData, + } + } + + #[doc(hidden)] + pub fn into_handle(self) -> u32 { + ManuallyDrop::new(self).handle + } +} + +impl Stream for StreamReceiver { + type Item = Result, Error>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let me = self.get_mut(); + + if me.future.is_none() { + me.future = Some(Box::pin(T::receive(me.handle))); + } + + match me.future.as_mut().unwrap().as_mut().poll(cx) { + Poll::Ready(v) => { + me.future = None; + Poll::Ready(v) + } + Poll::Pending => Poll::Pending, + } + } +} + +impl Drop for StreamReceiver { + fn drop(&mut self) { + T::drop_receiver(self.handle) + } +} + +pub struct Error { + handle: u32, +} + +impl Error { + #[doc(hidden)] + pub fn from_handle(handle: u32) -> Self { + Self { handle } + } + + #[doc(hidden)] + pub fn handle(&self) -> u32 { + self.handle + } +} + +impl Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Error").finish() + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Error") + } +} + +impl std::error::Error for Error {} + +impl Drop for Error { + fn drop(&mut self) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[error-drop]"] + fn drop(_: u32); + } + if self.handle != 0 { + unsafe { drop(self.handle) } + } + } + } +} + +pub fn new_future() -> (FutureSender, FutureReceiver) { + let (tx, rx) = T::new(); + ( + FutureSender { + handle: tx, + _phantom: PhantomData, + }, + FutureReceiver { + handle: rx, + _phantom: PhantomData, + }, + ) +} + +pub fn new_stream() -> (StreamSender, StreamReceiver) { + let (tx, rx) = T::new(); + ( + StreamSender { + handle: tx, + future: None, + _phantom: PhantomData, + }, + StreamReceiver { + handle: rx, + future: None, + _phantom: PhantomData, + }, + ) +} + +pub fn spawn(future: impl Future + 'static) { + unsafe { SPAWNED.push(Box::pin(future)) } +} diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index f4d901a56..7dcd561cd 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -9,6 +9,7 @@ pub(super) struct FunctionBindgen<'a, 'b> { pub gen: &'b mut InterfaceGenerator<'a>, params: Vec, async_: bool, + wasm_import_module: &'b str, pub src: Source, blocks: Vec, block_storage: Vec<(Source, Vec<(String, String)>)>, @@ -25,11 +26,13 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { gen: &'b mut InterfaceGenerator<'a>, params: Vec, async_: bool, + wasm_import_module: &'b str, ) -> FunctionBindgen<'a, 'b> { FunctionBindgen { gen, params, async_, + wasm_import_module, src: Default::default(), blocks: Vec::new(), block_storage: Vec::new(), @@ -61,13 +64,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } - fn declare_import( - &mut self, - module_name: &str, - name: &str, - params: &[WasmType], - results: &[WasmType], - ) -> String { + fn declare_import(&mut self, name: &str, params: &[WasmType], results: &[WasmType]) -> String { // Define the actual function we're calling inline let tmp = self.tmp(); let mut sig = "(".to_owned(); @@ -82,6 +79,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { sig.push_str(" -> "); sig.push_str(wasm_type(*result)); } + let module_name = self.wasm_import_module; uwrite!( self.src, " @@ -460,6 +458,43 @@ impl Bindgen for FunctionBindgen<'_, '_> { results.push(result); } + Instruction::FutureLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).into_handle() as i32")) + } + + Instruction::FutureLift { .. } => { + let async_support = self.gen.path_to_async_support(); + let op = &operands[0]; + results.push(format!( + "{async_support}::FutureReceiver::from_handle({op} as u32)" + )) + } + + Instruction::StreamLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).into_handle() as i32")) + } + + Instruction::StreamLift { .. } => { + let async_support = self.gen.path_to_async_support(); + let op = &operands[0]; + results.push(format!( + "{async_support}::StreamReceiver::from_handle({op} as u32)" + )) + } + + Instruction::ErrorLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).handle() as i32")) + } + + Instruction::ErrorLift { .. } => { + let async_support = self.gen.path_to_async_support(); + let op = &operands[0]; + results.push(format!("{async_support}::Error::from_handle({op} as u32)")) + } + Instruction::RecordLower { ty, record, .. } => { self.record_lower(*ty, record, &operands[0], results); } @@ -783,12 +818,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::IterBasePointer => results.push("base".to_string()), Instruction::CallWasm { name, sig, .. } => { - let func = self.declare_import( - self.gen.wasm_import_module, - name, - &sig.params, - &sig.results, - ); + let func = self.declare_import(name, &sig.params, &sig.results); // ... then call the function with all our operands if !sig.results.is_empty() { @@ -802,14 +832,9 @@ impl Bindgen for FunctionBindgen<'_, '_> { } Instruction::AsyncCallWasm { name, size, align } => { - let func = self.declare_import( - self.gen.wasm_import_module, - name, - &[WasmType::Pointer; 3], - &[WasmType::I32], - ); + let func = self.declare_import(name, &[WasmType::Pointer; 3], &[WasmType::I32]); - let await_result = self.gen.path_to_await_result(); + let async_support = self.gen.path_to_async_support(); let tmp = self.tmp(); let layout = format!("layout{tmp}"); let alloc = self.gen.path_to_std_alloc_module(); @@ -819,7 +844,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { let operands = operands.join(", "); uwriteln!( self.src, - "{await_result}({func}, {layout}, {operands}).await;" + "{async_support}::await_result({func}, {layout}, {operands}).await;" ); } @@ -890,8 +915,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { params, results: call_results, } => { - let func = - self.declare_import(self.gen.wasm_import_module, name, params, call_results); + let func = self.declare_import(name, params, call_results); if !call_results.is_empty() { self.push_str("let ret = "); @@ -912,17 +936,17 @@ impl Bindgen for FunctionBindgen<'_, '_> { }) .collect::>() .join(", "); - let first_poll = self.gen.path_to_first_poll(); + let async_support = self.gen.path_to_async_support(); uwriteln!( self.src, "\ - let result = {first_poll}({result}, |{params}| {{ + let result = {async_support}::first_poll({result}, |{params}| {{ " ); } Instruction::AsyncCallReturn { name, params } => { - let func = self.declare_import(self.gen.wasm_import_module, name, params, &[]); + let func = self.declare_import(name, params, &[]); uwriteln!( self.src, @@ -934,6 +958,15 @@ impl Bindgen for FunctionBindgen<'_, '_> { ); } + Instruction::Flush { amt } => { + for i in 0..*amt { + let tmp = self.tmp(); + let result = format!("result{}", tmp); + uwriteln!(self.src, "let {result} = {};", operands[i]); + results.push(result); + } + } + Instruction::Return { amt, .. } => { self.emit_cleanup(); match amt { @@ -963,7 +996,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { let tmp = self.tmp(); uwriteln!( self.src, - "let l{tmp} = i32::from(*{}.add({offset}).cast::());", + "let l{tmp} = i32::from(*{0}.add({offset}).cast::());", operands[0] ); results.push(format!("l{tmp}")); diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 90a8c1369..b230c8201 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -159,7 +159,7 @@ impl InterfaceGenerator<'_> { AsyncConfig::All => true, AsyncConfig::Some { exports, .. } => { exports.contains(&if let Some((_, key)) = interface { - format!("{}/{}", self.resolve.name_world_key(key), func.name) + format!("{}#{}", self.resolve.name_world_key(key), func.name) } else { func.name.clone() }) @@ -174,7 +174,7 @@ impl InterfaceGenerator<'_> { funcs_to_export.push((func, resource, async_)); let (trait_name, methods) = traits.get_mut(&resource).unwrap(); - self.generate_guest_export(func, &trait_name, async_); + self.generate_guest_export(func, interface.map(|(_, k)| k), &trait_name, async_); let prev = mem::take(&mut self.src); let mut sig = FnSig { @@ -269,7 +269,7 @@ fn _resource_rep(handle: u32) -> *mut u8 None => { let world = match self.identifier { Identifier::World(w) => w, - Identifier::Interface(..) => unreachable!(), + Identifier::None | Identifier::Interface(..) => unreachable!(), }; let world = self.resolve.worlds[world].name.to_snake_case(); format!("__export_world_{world}_cabi") @@ -320,7 +320,7 @@ macro_rules! {macro_name} {{ for name in resources_to_drop { let module = match self.identifier { Identifier::Interface(_, key) => self.resolve.name_world_key(key), - Identifier::World(_) => unreachable!(), + Identifier::None | Identifier::World(_) => unreachable!(), }; let camel = name.to_upper_camel_case(); uwriteln!( @@ -459,16 +459,308 @@ macro_rules! {macro_name} {{ map.push((module, module_path)) } + fn generate_payloads(&mut self, prefix: &str, func: &Function, interface: Option<&WorldKey>) { + for (index, ty) in func + .find_futures_and_streams(self.resolve) + .into_iter() + .enumerate() + { + let payload_result = self.gen.payload_results[&ty]; + let module = format!( + "{prefix}{}", + interface + .map(|name| self.resolve.name_world_key(name)) + .unwrap_or_else(|| "$root".into()) + ); + let func_name = &func.name; + let type_mode = TypeMode { + lifetime: None, + lists_borrowed: false, + style: TypeOwnershipStyle::Owned, + }; + let async_support = self.path_to_async_support(); + + match &self.resolve.types[ty].kind { + TypeDefKind::Future(payload_type) => { + let (name, full_name) = if let Some(payload_type) = payload_type { + ( + { + let old = mem::take(&mut self.src); + self.print_ty(&payload_type, type_mode); + String::from(mem::replace(&mut self.src, old)) + }, + { + let old = mem::take(&mut self.src); + let old_identifier = + mem::replace(&mut self.identifier, Identifier::None); + self.print_ty(&payload_type, type_mode); + self.identifier = old_identifier; + String::from(mem::replace(&mut self.src, old)) + }, + ) + } else { + ("()".into(), "()".into()) + }; + + if self.gen.future_payloads_emitted.insert(full_name) { + let send = Function { + name: format!("[future-send-{index}]{func_name}"), + kind: FunctionKind::Freestanding, + params: if let Some(payload_type) = payload_type { + vec![ + ("sender".into(), Type::U32), + ("value".into(), *payload_type), + ] + } else { + vec![("sender".into(), Type::U32)] + }, + results: Results::Anon(Type::Id(self.gen.unit_result.unwrap())), + docs: Default::default(), + }; + let old = mem::take(&mut self.src); + self.generate_guest_import_body( + &module, + &send, + if payload_type.is_some() { + vec!["sender".into(), "value".into()] + } else { + vec!["sender".into()] + }, + true, + ); + let send = String::from(mem::replace(&mut self.src, old)); + + let receive = Function { + name: format!("[future-receive-{index}]{func_name}"), + kind: FunctionKind::Freestanding, + params: vec![("receiver".into(), Type::U32)], + results: Results::Anon(Type::Id(payload_result)), + docs: Default::default(), + }; + let old = mem::take(&mut self.src); + self.generate_guest_import_body( + &module, + &receive, + vec!["receiver".into()], + true, + ); + let receive = String::from(mem::replace(&mut self.src, old)); + + uwriteln!( + self.src, + r#" +impl {async_support}::FuturePayload for {name} {{ + fn new() -> (u32, u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-new-{index}]{func_name}"] + fn new(_: *mut u32); + }} + let mut results = [0u32; 2]; + unsafe {{ new(results.as_mut_ptr()) }}; + (results[0], results[1]) + }} + }} + + async fn send(sender: u32, value: Self) -> Result<(), {async_support}::Error> {{ + unsafe {{ {send} }} + }} + + async fn receive(receiver: u32) -> Result {{ + unsafe {{ {receive} }} + }} + + fn drop_sender(sender: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-drop-sender-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(sender) }} + }} + }} + + fn drop_receiver(receiver: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-drop-receiver-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(receiver) }} + }} + }} +}} + "#, + ); + } + } + TypeDefKind::Stream(payload_type) => { + let name = { + let old = mem::take(&mut self.src); + self.print_ty(&payload_type, type_mode); + String::from(mem::replace(&mut self.src, old)) + }; + + let full_name = { + let old = mem::take(&mut self.src); + let old_identifier = mem::replace(&mut self.identifier, Identifier::None); + self.print_ty(&payload_type, type_mode); + self.identifier = old_identifier; + String::from(mem::replace(&mut self.src, old)) + }; + + let TypeDefKind::Option(Type::Id(result_ty)) = + &self.resolve.types[payload_result].kind + else { + unreachable!() + }; + let TypeDefKind::Result(Result_ { + ok: Some(list_ty), .. + }) = &self.resolve.types[*result_ty].kind + else { + unreachable!() + }; + + if self.gen.stream_payloads_emitted.insert(full_name) { + let send = Function { + name: format!("[stream-send-{index}]{func_name}"), + kind: FunctionKind::Freestanding, + params: vec![("sender".into(), Type::U32), ("values".into(), *list_ty)], + results: Results::Anon(Type::Id(self.gen.unit_result.unwrap())), + docs: Default::default(), + }; + let old = mem::take(&mut self.src); + self.generate_guest_import_body( + &module, + &send, + vec!["sender".into(), "values".into()], + true, + ); + let send = String::from(mem::replace(&mut self.src, old)); + + let receive = Function { + name: format!("[stream-receive-{index}]{func_name}"), + kind: FunctionKind::Freestanding, + params: vec![("receiver".into(), Type::U32)], + results: Results::Anon(Type::Id(payload_result)), + docs: Default::default(), + }; + let old_src = mem::take(&mut self.src); + self.generate_guest_import_body( + &module, + &receive, + vec!["receiver".into()], + true, + ); + let receive = String::from(mem::replace(&mut self.src, old_src)); + + uwriteln!( + self.src, + r#" +impl {async_support}::StreamPayload for {name} {{ + fn new() -> (u32, u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-new-{index}]{func_name}"] + fn new(_: *mut u32); + }} + let mut results = [0u32; 2]; + unsafe {{ new(results.as_mut_ptr()) }}; + (results[0], results[1]) + }} + }} + + async fn send(sender: u32, values: Vec) -> Result<(), {async_support}::Error> {{ + unsafe {{ {send} }} + }} + + async fn receive(receiver: u32) -> Option, {async_support}::Error>> {{ + unsafe {{ {receive} }} + }} + + fn drop_sender(sender: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-drop-sender-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(sender) }} + }} + }} + + fn drop_receiver(receiver: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-drop-receiver-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(receiver) }} + }} + }} +}} + "# + ); + } + } + _ => unreachable!(), + } + } + } + fn generate_guest_import(&mut self, func: &Function, interface: Option<&WorldKey>) { if self.gen.skip.contains(&func.name) { return; } + self.generate_payloads("[import-payload]", func, interface); + let async_ = match &self.gen.opts.async_ { AsyncConfig::None => false, AsyncConfig::All => true, AsyncConfig::Some { imports, .. } => imports.contains(&if let Some(key) = interface { - format!("{}/{}", self.resolve.name_world_key(key), func.name) + format!("{}#{}", self.resolve.name_world_key(key), func.name) } else { func.name.clone() }), @@ -495,7 +787,27 @@ macro_rules! {macro_name} {{ self.src.push_str("{\n"); self.src.push_str("unsafe {\n"); - let mut f = FunctionBindgen::new(self, params, async_); + self.generate_guest_import_body(&self.wasm_import_module, func, params, async_); + + self.src.push_str("}\n"); + self.src.push_str("}\n"); + + match func.kind { + FunctionKind::Freestanding => {} + FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::Constructor(_) => { + self.src.push_str("}\n"); + } + } + } + + fn generate_guest_import_body( + &mut self, + module: &str, + func: &Function, + params: Vec, + async_: bool, + ) { + let mut f = FunctionBindgen::new(self, params, async_, module); abi::call( f.gen.resolve, AbiVariant::GuestImport, @@ -529,21 +841,19 @@ macro_rules! {macro_name} {{ ); } self.src.push_str(&String::from(src)); - - self.src.push_str("}\n"); - self.src.push_str("}\n"); - - match func.kind { - FunctionKind::Freestanding => {} - FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::Constructor(_) => { - self.src.push_str("}\n"); - } - } } - fn generate_guest_export(&mut self, func: &Function, trait_name: &str, async_: bool) { + fn generate_guest_export( + &mut self, + func: &Function, + interface: Option<&WorldKey>, + trait_name: &str, + async_: bool, + ) { let name_snake = func.name.to_snake_case().replace('.', "_"); + self.generate_payloads("[export-payload]", func, interface); + uwrite!( self.src, "\ @@ -576,7 +886,7 @@ macro_rules! {macro_name} {{ ); } - let mut f = FunctionBindgen::new(self, params, async_); + let mut f = FunctionBindgen::new(self, params, async_, self.wasm_import_module); abi::call( f.gen.resolve, AbiVariant::GuestExport, @@ -600,14 +910,14 @@ macro_rules! {macro_name} {{ self.src.push_str("}\n"); if async_ { - let callback = self.path_to_callback(); + let async_support = self.path_to_async_support(); uwrite!( self.src, "\ #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn __callback_{name_snake}(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 {{ - {callback}(ctx, event0, event1, event2) + {async_support}::callback(ctx, event0, event1, event2) }} " ); @@ -623,7 +933,7 @@ macro_rules! {macro_name} {{ let params = self.print_post_return_sig(func); self.src.push_str("{\n"); - let mut f = FunctionBindgen::new(self, params, async_); + let mut f = FunctionBindgen::new(self, params, async_, self.wasm_import_module); abi::post_return(f.gen.resolve, func, &mut f, async_); let FunctionBindgen { needs_cleanup_list, @@ -649,6 +959,7 @@ macro_rules! {macro_name} {{ let wasm_module_export_name = match self.identifier { Identifier::Interface(_, key) => Some(self.resolve.name_world_key(key)), Identifier::World(_) => None, + Identifier::None => unreachable!(), }; let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); let export_name = func.core_export_name(wasm_module_export_name.as_deref()); @@ -1198,8 +1509,8 @@ macro_rules! {macro_name} {{ Type::S16 => self.push_str("i16"), Type::S32 => self.push_str("i32"), Type::S64 => self.push_str("i64"), - Type::Float32 => self.push_str("f32"), - Type::Float64 => self.push_str("f64"), + Type::F32 => self.push_str("f32"), + Type::F64 => self.push_str("f64"), Type::Char => self.push_str("char"), Type::String => { assert_eq!(mode.lists_borrowed, mode.lifetime.is_some()); @@ -1334,17 +1645,21 @@ macro_rules! {macro_name} {{ panic!("unsupported anonymous type reference: enum") } TypeDefKind::Future(ty) => { - self.push_str("Future<"); + let async_support = self.path_to_async_support(); + uwrite!(self.src, "{async_support}::FutureReceiver<"); self.print_optional_ty(ty.as_ref(), mode); self.push_str(">"); } - TypeDefKind::Stream(stream) => { - self.push_str("Stream<"); - self.print_optional_ty(stream.element.as_ref(), mode); - self.push_str(","); - self.print_optional_ty(stream.end.as_ref(), mode); + TypeDefKind::Stream(ty) => { + let async_support = self.path_to_async_support(); + uwrite!(self.src, "{async_support}::StreamReceiver<"); + self.print_ty(ty, mode); self.push_str(">"); } + TypeDefKind::Error => { + let async_support = self.path_to_async_support(); + uwrite!(self.src, "{async_support}::Error"); + } TypeDefKind::Handle(Handle::Own(ty)) => { self.print_ty(&Type::Id(*ty), mode); @@ -2027,16 +2342,8 @@ macro_rules! {macro_name} {{ self.path_from_runtime_module(RuntimeItem::StdAllocModule, "alloc") } - pub fn path_to_await_result(&mut self) -> String { - self.path_from_runtime_module(RuntimeItem::AsyncSupport, "await_result") - } - - pub fn path_to_first_poll(&mut self) -> String { - self.path_from_runtime_module(RuntimeItem::AsyncSupport, "first_poll") - } - - pub fn path_to_callback(&mut self) -> String { - self.path_from_runtime_module(RuntimeItem::AsyncSupport, "callback") + pub fn path_to_async_support(&mut self) -> String { + self.path_from_runtime_module(RuntimeItem::AsyncSupport, "async_support") } fn path_from_runtime_module( @@ -2101,6 +2408,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { let module = match self.identifier { Identifier::Interface(_, key) => self.resolve.name_world_key(key), Identifier::World(_) => unimplemented!("resource exports from worlds"), + Identifier::None => unreachable!(), }; let box_path = self.path_to_box(); uwriteln!( diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 86a14f21e..a3dad54f3 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -46,6 +46,11 @@ struct RustWasm { rt_module: IndexSet, export_macros: Vec<(String, String)>, with: HashMap, + + unit_result: Option, + payload_results: HashMap, + future_payloads_emitted: HashSet, + stream_payloads_emitted: HashSet, } #[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)] @@ -339,6 +344,9 @@ impl RustWasm { } } self.src.push_str("}\n"); + if emitted.contains(&RuntimeItem::AsyncSupport) { + self.src.push_str("pub use _rt::async_support;\n"); + } } fn emit_runtime_item(&mut self, item: RuntimeItem) { @@ -572,7 +580,9 @@ impl Drop for Resource { } RuntimeItem::AsyncSupport => { + self.src.push_str("pub mod async_support {"); self.src.push_str(include_str!("async_support.rs")); + self.src.push_str("}"); } } } @@ -893,6 +903,10 @@ impl WorldGenerator for RustWasm { for (k, v) in self.opts.with.iter() { self.with.insert(k.clone(), v.clone()); } + + let (unit_result, payload_results) = resolve.find_future_and_stream_results(); + self.unit_result = unit_result; + self.payload_results = payload_results; } fn import_interface( @@ -1163,6 +1177,7 @@ fn compute_module_path(name: &WorldKey, resolve: &Resolve, is_export: bool) -> V } enum Identifier<'a> { + None, World(WorldId), Interface(InterfaceId, &'a WorldKey), } diff --git a/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs index 4e306ede3..81276e170 100644 --- a/src/bin/wit-bindgen.rs +++ b/src/bin/wit-bindgen.rs @@ -156,6 +156,7 @@ fn gen_world( ) -> Result<()> { let mut resolve = Resolve::default(); let (pkg, _files) = resolve.push_path(&opts.wit)?; + self.resolve.add_future_and_stream_results(); let world = resolve.select_world(pkg, opts.world.as_deref())?; generator.generate(&resolve, world, files)?; From 02c7762c0576d40d3e6befc274d5d6e85462f1fa Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 19 May 2024 23:21:57 +0200 Subject: [PATCH 218/672] bridge skeleton --- Cargo.lock | 12 ++++++ Cargo.toml | 6 ++- crates/bridge/Cargo.toml | 24 ++++++++++++ crates/bridge/src/lib.rs | 79 ++++++++++++++++++++++++++++++++++++++++ crates/cpp/Cargo.toml | 2 +- src/bin/wit-bindgen.rs | 4 -- 6 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 crates/bridge/Cargo.toml create mode 100644 crates/bridge/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 7b3a71b4a..1c3091e40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2322,6 +2322,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.24.0" @@ -2349,6 +2360,7 @@ dependencies = [ "wasmparser 0.207.0", "wasmtime", "wasmtime-wasi", + "wit-bindgen-bridge", "wit-bindgen-c", "wit-bindgen-core", "wit-bindgen-cpp", diff --git a/Cargo.toml b/Cargo.toml index 1d05dfc5d..8a7300430 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ wit-bindgen-csharp = { path = 'crates/csharp', version = '0.24.0' } wit-bindgen-markdown = { path = 'crates/markdown', version = '0.24.0' } wit-bindgen = { path = 'crates/guest-rust', version = '0.24.0', default-features = false } wit-bindgen-cpp = { path = 'crates/cpp', version = '0.3.0' } +wit-bindgen-bridge = { path = 'crates/bridge', version = '0.1.0' } [[bin]] name = "wit-bindgen" @@ -53,13 +54,14 @@ clap = { workspace = true } wit-bindgen-core = { workspace = true } wit-bindgen-rust = { workspace = true, features = ['clap'], optional = true } wit-bindgen-c = { workspace = true, features = ['clap'], optional = true } -wit-bindgen-cpp = { workspace = true, features = ['clap'], optional = true } wit-bindgen-markdown = { workspace = true, features = ['clap'], optional = true } wit-bindgen-teavm-java = { workspace = true, features = ['clap'], optional = true } wit-bindgen-go = { workspace = true, features = ['clap'], optional = true } wit-bindgen-csharp = { workspace = true, features = ['clap'], optional = 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 = [ @@ -70,7 +72,9 @@ default = [ 'go', 'csharp-naot', 'cpp', + 'bridge', ] +bridge = ['dep:wit-bindgen-bridge'] c = ['dep:wit-bindgen-c'] cpp = ['dep:wit-bindgen-cpp'] rust = ['dep:wit-bindgen-rust'] 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 "] +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..72cabd08f --- /dev/null +++ b/crates/bridge/src/lib.rs @@ -0,0 +1,79 @@ +use wit_bindgen_core::{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, +} + +impl Opts { + pub fn build(&self) -> Box { + let mut r = Bridge::default(); + r.opts = self.clone(); + Box::new(r) + } +} + +impl WorldGenerator for Bridge { + fn import_interface( + &mut self, + resolve: &wit_bindgen_core::wit_parser::Resolve, + name: &wit_bindgen_core::wit_parser::WorldKey, + iface: wit_bindgen_core::wit_parser::InterfaceId, + files: &mut wit_bindgen_core::Files, + ) { + todo!() + } + + fn export_interface( + &mut self, + resolve: &wit_bindgen_core::wit_parser::Resolve, + name: &wit_bindgen_core::wit_parser::WorldKey, + iface: wit_bindgen_core::wit_parser::InterfaceId, + files: &mut wit_bindgen_core::Files, + ) -> anyhow::Result<()> { + todo!() + } + + fn import_funcs( + &mut self, + resolve: &wit_bindgen_core::wit_parser::Resolve, + world: wit_bindgen_core::wit_parser::WorldId, + funcs: &[(&str, &wit_bindgen_core::wit_parser::Function)], + files: &mut wit_bindgen_core::Files, + ) { + todo!() + } + + fn export_funcs( + &mut self, + resolve: &wit_bindgen_core::wit_parser::Resolve, + world: wit_bindgen_core::wit_parser::WorldId, + funcs: &[(&str, &wit_bindgen_core::wit_parser::Function)], + files: &mut wit_bindgen_core::Files, + ) -> anyhow::Result<()> { + todo!() + } + + fn import_types( + &mut self, + resolve: &wit_bindgen_core::wit_parser::Resolve, + world: wit_bindgen_core::wit_parser::WorldId, + types: &[(&str, wit_bindgen_core::wit_parser::TypeId)], + files: &mut wit_bindgen_core::Files, + ) { + todo!() + } + + fn finish(&mut self, resolve: &wit_bindgen_core::wit_parser::Resolve, world: wit_bindgen_core::wit_parser::WorldId, files: &mut wit_bindgen_core::Files) -> anyhow::Result<()> { + todo!() + } +} diff --git a/crates/cpp/Cargo.toml b/crates/cpp/Cargo.toml index 38102ee58..b9d9a5139 100644 --- a/crates/cpp/Cargo.toml +++ b/crates/cpp/Cargo.toml @@ -6,7 +6,7 @@ edition.workspace = true repository = 'https://github.com/cpetig/wit-bindgen' license = "Apache-2.0 WITH LLVM-exception" description = """ -C host binding generator for WIT and the component model, targeting the common C API. +C++ guest and host binding generator for WIT and the component model. """ [lib] diff --git a/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs index 69266f412..f775c2cbf 100644 --- a/src/bin/wit-bindgen.rs +++ b/src/bin/wit-bindgen.rs @@ -104,12 +104,8 @@ fn main() -> Result<()> { Opt::Markdown { opts, args } => (opts.build(), args), #[cfg(feature = "c")] Opt::C { opts, args } => (opts.build(), args), - #[cfg(feature = "c-host")] - Opt::CHost { opts, args } => (opts.build(), args), #[cfg(feature = "cpp")] Opt::Cpp { opts, args } => (opts.build(), args), - #[cfg(feature = "cpp-host")] - Opt::CppHost { opts, args } => (opts.build(), args), #[cfg(feature = "rust")] Opt::Rust { opts, args } => (opts.build(), args), #[cfg(feature = "teavm-java")] From 573b111bb45ad02113ec467f9456e235c5f2e346 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 19 May 2024 23:46:10 +0200 Subject: [PATCH 219/672] initial bridge output --- crates/bridge/src/lib.rs | 71 +++++++++++++++++++++++++++------------- src/bin/wit-bindgen.rs | 11 ++++++- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/crates/bridge/src/lib.rs b/crates/bridge/src/lib.rs index 72cabd08f..1f0e1ef8a 100644 --- a/crates/bridge/src/lib.rs +++ b/crates/bridge/src/lib.rs @@ -1,4 +1,9 @@ -use wit_bindgen_core::{Source, WorldGenerator}; +use std::fmt::Write; +use wit_bindgen_core::{ + uwriteln, + wit_parser::{self, WorldKey}, + Source, WorldGenerator, +}; #[derive(Default)] struct Bridge { @@ -25,55 +30,75 @@ impl Opts { impl WorldGenerator for Bridge { fn import_interface( &mut self, - resolve: &wit_bindgen_core::wit_parser::Resolve, - name: &wit_bindgen_core::wit_parser::WorldKey, - iface: wit_bindgen_core::wit_parser::InterfaceId, + resolve: &wit_parser::Resolve, + name: &WorldKey, + iface: wit_parser::InterfaceId, files: &mut wit_bindgen_core::Files, ) { - todo!() + 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}"); } fn export_interface( &mut self, - resolve: &wit_bindgen_core::wit_parser::Resolve, - name: &wit_bindgen_core::wit_parser::WorldKey, - iface: wit_bindgen_core::wit_parser::InterfaceId, + resolve: &wit_parser::Resolve, + name: &WorldKey, + iface: wit_parser::InterfaceId, files: &mut wit_bindgen_core::Files, ) -> anyhow::Result<()> { - todo!() + 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}"); + Ok(()) } fn import_funcs( &mut self, - resolve: &wit_bindgen_core::wit_parser::Resolve, - world: wit_bindgen_core::wit_parser::WorldId, - funcs: &[(&str, &wit_bindgen_core::wit_parser::Function)], + resolve: &wit_parser::Resolve, + world: wit_parser::WorldId, + funcs: &[(&str, &wit_parser::Function)], files: &mut wit_bindgen_core::Files, ) { - todo!() + let world = &resolve.worlds[world]; + uwriteln!(self.src, "Import Funcs {}", world.name); } fn export_funcs( &mut self, - resolve: &wit_bindgen_core::wit_parser::Resolve, - world: wit_bindgen_core::wit_parser::WorldId, - funcs: &[(&str, &wit_bindgen_core::wit_parser::Function)], + resolve: &wit_parser::Resolve, + world: wit_parser::WorldId, + funcs: &[(&str, &wit_parser::Function)], files: &mut wit_bindgen_core::Files, ) -> anyhow::Result<()> { - todo!() + let world = &resolve.worlds[world]; + uwriteln!(self.src, "Export Funcs {}", world.name); + Ok(()) } fn import_types( &mut self, - resolve: &wit_bindgen_core::wit_parser::Resolve, - world: wit_bindgen_core::wit_parser::WorldId, - types: &[(&str, wit_bindgen_core::wit_parser::TypeId)], + resolve: &wit_parser::Resolve, + world: wit_parser::WorldId, + types: &[(&str, wit_parser::TypeId)], files: &mut wit_bindgen_core::Files, ) { - todo!() + let world = &resolve.worlds[world]; + uwriteln!(self.src, "Import Types {}", world.name); } - fn finish(&mut self, resolve: &wit_bindgen_core::wit_parser::Resolve, world: wit_bindgen_core::wit_parser::WorldId, files: &mut wit_bindgen_core::Files) -> anyhow::Result<()> { - todo!() + 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(()) } } diff --git a/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs index f775c2cbf..3d05e5019 100644 --- a/src/bin/wit-bindgen.rs +++ b/src/bin/wit-bindgen.rs @@ -38,6 +38,14 @@ 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 { @@ -46,7 +54,6 @@ enum Opt { #[clap(flatten)] args: Common, }, - /// Generates bindings for TeaVM-based Java guest modules. #[cfg(feature = "teavm-java")] TeavmJava { @@ -104,6 +111,8 @@ fn main() -> Result<()> { Opt::Markdown { 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")] From 49b3bce0ebfaa840b490ed86a22d00539ba1cf36 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 20 May 2024 00:37:23 +0200 Subject: [PATCH 220/672] function names --- crates/bridge/src/lib.rs | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/crates/bridge/src/lib.rs b/crates/bridge/src/lib.rs index 1f0e1ef8a..b44afd9ce 100644 --- a/crates/bridge/src/lib.rs +++ b/crates/bridge/src/lib.rs @@ -1,7 +1,8 @@ use std::fmt::Write; use wit_bindgen_core::{ + abi::AbiVariant, uwriteln, - wit_parser::{self, WorldKey}, + wit_parser::{self, Function, Resolve, TypeOwner, WorldKey}, Source, WorldGenerator, }; @@ -33,13 +34,18 @@ impl WorldGenerator for Bridge { resolve: &wit_parser::Resolve, name: &WorldKey, iface: wit_parser::InterfaceId, - files: &mut wit_bindgen_core::Files, + _files: &mut wit_bindgen_core::Files, ) { 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); + } } fn export_interface( @@ -102,3 +108,20 @@ impl WorldGenerator for Bridge { Ok(()) } } + +impl Bridge { + fn interface<'a>(&'a mut self, resolve: &'a Resolve) -> BridgeInterfaceGenerator<'a> { + BridgeInterfaceGenerator { gen: self, resolve } + } +} + +struct BridgeInterfaceGenerator<'a> { + gen: &'a mut Bridge, + resolve: &'a Resolve, +} + +impl<'a> BridgeInterfaceGenerator<'a> { + fn generate_function(&mut self, func: &Function, owner: &TypeOwner, variant: AbiVariant) { + uwriteln!(self.gen.src, "Func {} {:?}", func.name, variant); + } +} From f3b5e594ee30ce54dfa9f67056eb62637326fd92 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 20 May 2024 01:01:28 +0200 Subject: [PATCH 221/672] prologue --- crates/bridge/src/lib.rs | 59 ++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/crates/bridge/src/lib.rs b/crates/bridge/src/lib.rs index b44afd9ce..329b90383 100644 --- a/crates/bridge/src/lib.rs +++ b/crates/bridge/src/lib.rs @@ -2,7 +2,7 @@ use std::fmt::Write; use wit_bindgen_core::{ abi::AbiVariant, uwriteln, - wit_parser::{self, Function, Resolve, TypeOwner, WorldKey}, + wit_parser::{self, Function, Resolve, TypeOwner, WorldId, WorldKey}, Source, WorldGenerator, }; @@ -29,6 +29,34 @@ impl Opts { } impl WorldGenerator for Bridge { + fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { + let world = &resolve.worlds[world]; + let name = world.name.clone(); + uwriteln!( + self.src, + r#" + #include + #include + #include "{name}.h" + + 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, @@ -53,36 +81,49 @@ impl WorldGenerator for Bridge { resolve: &wit_parser::Resolve, name: &WorldKey, iface: wit_parser::InterfaceId, - files: &mut wit_bindgen_core::Files, + _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, - world: wit_parser::WorldId, + worldid: wit_parser::WorldId, funcs: &[(&str, &wit_parser::Function)], - files: &mut wit_bindgen_core::Files, + _files: &mut wit_bindgen_core::Files, ) { - let world = &resolve.worlds[world]; + 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, - world: wit_parser::WorldId, + worldid: wit_parser::WorldId, funcs: &[(&str, &wit_parser::Function)], - files: &mut wit_bindgen_core::Files, + _files: &mut wit_bindgen_core::Files, ) -> anyhow::Result<()> { - let world = &resolve.worlds[world]; + 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(()) } @@ -94,7 +135,7 @@ impl WorldGenerator for Bridge { files: &mut wit_bindgen_core::Files, ) { let world = &resolve.worlds[world]; - uwriteln!(self.src, "Import Types {}", world.name); + uwriteln!(self.src, "// Import Types {}", world.name); } fn finish( From 713377cae9f53b412a3775f2d4e3f723641e9222 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 20 May 2024 01:06:11 +0200 Subject: [PATCH 222/672] custom name --- crates/bridge/src/lib.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/crates/bridge/src/lib.rs b/crates/bridge/src/lib.rs index 329b90383..8141bde86 100644 --- a/crates/bridge/src/lib.rs +++ b/crates/bridge/src/lib.rs @@ -18,6 +18,9 @@ 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))] + instance: String, } impl Opts { @@ -31,7 +34,11 @@ impl Opts { impl WorldGenerator for Bridge { fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { let world = &resolve.worlds[world]; - let name = world.name.clone(); + let name = if self.opts.instance.is_empty() { + world.name.clone() + } else { + self.opts.instance.clone() + }; uwriteln!( self.src, r#" @@ -68,7 +75,7 @@ impl WorldGenerator for Bridge { WorldKey::Name(n) => n.clone(), WorldKey::Interface(i) => resolve.interfaces[*i].name.clone().unwrap_or_default(), }; - uwriteln!(self.src, "Import IF {world}"); + uwriteln!(self.src, "// Import IF {world}"); let mut gen = self.interface(resolve); for (_name, func) in resolve.interfaces[iface].functions.iter() { @@ -87,7 +94,7 @@ impl WorldGenerator for Bridge { WorldKey::Name(n) => n.clone(), WorldKey::Interface(i) => resolve.interfaces[*i].name.clone().unwrap_or_default(), }; - uwriteln!(self.src, "Export IF {world}"); + uwriteln!(self.src, "// Export IF {world}"); let mut gen = self.interface(resolve); for (_name, func) in resolve.interfaces[iface].functions.iter() { @@ -104,7 +111,7 @@ impl WorldGenerator for Bridge { _files: &mut wit_bindgen_core::Files, ) { let world = &resolve.worlds[worldid]; - uwriteln!(self.src, "Import Funcs {}", world.name); + 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); @@ -119,7 +126,7 @@ impl WorldGenerator for Bridge { _files: &mut wit_bindgen_core::Files, ) -> anyhow::Result<()> { let world = &resolve.worlds[worldid]; - uwriteln!(self.src, "Export Funcs {}", world.name); + 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); From f3ebc95b94eb1e8d02f8c33bfefeb2f8d215019f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 20 May 2024 01:29:32 +0200 Subject: [PATCH 223/672] work in progress --- crates/bridge/src/lib.rs | 53 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/crates/bridge/src/lib.rs b/crates/bridge/src/lib.rs index 8141bde86..39039d91b 100644 --- a/crates/bridge/src/lib.rs +++ b/crates/bridge/src/lib.rs @@ -1,7 +1,7 @@ use std::fmt::Write; use wit_bindgen_core::{ - abi::AbiVariant, - uwriteln, + abi::{AbiVariant, WasmType}, + make_external_symbol, uwriteln, wit_parser::{self, Function, Resolve, TypeOwner, WorldId, WorldKey}, Source, WorldGenerator, }; @@ -161,6 +161,33 @@ 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> { @@ -168,8 +195,28 @@ struct BridgeInterfaceGenerator<'a> { 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); + uwriteln!(self.gen.src, "// Func {} {:?}", func.name, variant); + let result_var = match variant { + AbiVariant::GuestImport => TypeVariant::W2C2, + AbiVariant::GuestExport => TypeVariant::Native, + }; + let signature = self.resolve.wasm_signature(variant, func); + let return_via_pointer = signature.retptr; + 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); } } From 0be350aeace5cf9f0b28c02b310ae49f046ee7fa Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 20 May 2024 09:23:34 +0200 Subject: [PATCH 224/672] initial w2c2 example --- crates/cpp/tests/native_strings/Makefile | 12 +++++++ crates/cpp/tests/native_strings/the_world.cpp | 20 ++++++------ .../cpp/tests/native_strings/w2c2/.gitignore | 5 +++ crates/cpp/tests/native_strings/w2c2/Makefile | 32 +++++++++++++++++++ 4 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 crates/cpp/tests/native_strings/w2c2/.gitignore create mode 100644 crates/cpp/tests/native_strings/w2c2/Makefile diff --git a/crates/cpp/tests/native_strings/Makefile b/crates/cpp/tests/native_strings/Makefile index 832fe9925..54cdcdf09 100644 --- a/crates/cpp/tests/native_strings/Makefile +++ b/crates/cpp/tests/native_strings/Makefile @@ -20,5 +20,17 @@ bindgen: wit/strings.wit 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/the_world.cpp b/crates/cpp/tests/native_strings/the_world.cpp index be592888d..9ab7b9c5c 100644 --- a/crates/cpp/tests/native_strings/the_world.cpp +++ b/crates/cpp/tests/native_strings/the_world.cpp @@ -41,10 +41,10 @@ void foo::foo::strings::A(std::string_view x) { fooX3AfooX2FstringsX00a(ptr0, len0); } wit::string foo::foo::strings::B() { - uint64_t ret_area[2]; + size_t ret_area[2]; uint8_t *ptr0 = (uint8_t *)(&ret_area); fooX3AfooX2FstringsX00b(ptr0); - auto len1 = *((size_t *)(ptr0 + 8)); + auto len1 = *((size_t *)(ptr0 + sizeof(size_t))); return wit::string((char const *)(*((uint8_t **)(ptr0 + 0))), len1); } @@ -55,10 +55,10 @@ wit::string foo::foo::strings::C(std::string_view a, std::string_view b) { auto const &vec1 = b; auto ptr1 = (uint8_t *)(vec1.data()); auto len1 = (size_t)(vec1.size()); - uint64_t ret_area[2]; + size_t ret_area[2]; uint8_t *ptr2 = (uint8_t *)(&ret_area); fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2); - auto len3 = *((size_t *)(ptr2 + 8)); + auto len3 = *((size_t *)(ptr2 + sizeof(size_t))); return wit::string((char const *)(*((uint8_t **)(ptr2 + 0))), len3); } @@ -71,14 +71,14 @@ fooX3AfooX2FstringsX23a(uint8_t *arg0, size_t arg1) { 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]; + static size_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; + *((size_t *)(ptr1 + sizeof(size_t))) = len2; *((uint8_t **)(ptr1 + 0)) = ptr2; return ptr1; } @@ -86,7 +86,7 @@ extern "C" __attribute__((__weak__, __export_name__("cabi_post_fooX3AfooX2FstringsX23b"))) void cabi_post_fooX3AfooX2FstringsX23b(uint8_t *arg0) { - if ((*((size_t *)(arg0 + 8))) > 0) { + if ((*((size_t *)(arg0 + sizeof(size_t)))) > 0) { wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); } } @@ -100,14 +100,14 @@ fooX3AfooX2FstringsX23c(uint8_t *arg0, size_t arg1, uint8_t *arg2, 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]; + static size_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; + *((size_t *)(ptr3 + sizeof(size_t))) = len4; *((uint8_t **)(ptr3 + 0)) = ptr4; return ptr3; } @@ -115,7 +115,7 @@ extern "C" __attribute__((__weak__, __export_name__("cabi_post_fooX3AfooX2FstringsX23c"))) void cabi_post_fooX3AfooX2FstringsX23c(uint8_t *arg0) { - if ((*((size_t *)(arg0 + 8))) > 0) { + if ((*((size_t *)(arg0 + sizeof(size_t)))) > 0) { wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); } } 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..fb124a073 --- /dev/null +++ b/crates/cpp/tests/native_strings/w2c2/Makefile @@ -0,0 +1,32 @@ +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 + $(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 $^ $@ + +x.c: + $(WIT_BINDGEN) bridge ../wit --instance guestrelease From 064929dea7336eea0a056dbd4b45197f7b6f895e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 20 May 2024 10:33:11 +0200 Subject: [PATCH 225/672] initial w2c2 prototype --- crates/bridge/src/lib.rs | 16 +++- .../tests/native_strings/the_world_native.cpp | 4 +- crates/cpp/tests/native_strings/w2c2/Makefile | 6 +- .../w2c2/the-world_bridge_target.c | 82 +++++++++++++++++++ .../tests/native_strings/w2c2/wasi_dummy.c | 13 +++ 5 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 crates/cpp/tests/native_strings/w2c2/the-world_bridge_target.c create mode 100644 crates/cpp/tests/native_strings/w2c2/wasi_dummy.c diff --git a/crates/bridge/src/lib.rs b/crates/bridge/src/lib.rs index 39039d91b..4801ad01e 100644 --- a/crates/bridge/src/lib.rs +++ b/crates/bridge/src/lib.rs @@ -19,8 +19,11 @@ pub struct Opts { #[cfg_attr(feature = "clap", arg(long))] wamr: bool, /// w2c2 Instance name (derived from wasm file) - #[cfg_attr(feature = "clap", arg(long))] + #[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 { @@ -39,12 +42,17 @@ impl WorldGenerator for Bridge { } 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 #include - #include "{name}.h" + #include "{include}" static {name}Instance* instance; static {name}Instance app_instance; @@ -209,6 +217,10 @@ impl<'a> BridgeInterfaceGenerator<'a> { }; 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 { diff --git a/crates/cpp/tests/native_strings/the_world_native.cpp b/crates/cpp/tests/native_strings/the_world_native.cpp index b5b17521e..2ba8a4083 100644 --- a/crates/cpp/tests/native_strings/the_world_native.cpp +++ b/crates/cpp/tests/native_strings/the_world_native.cpp @@ -20,7 +20,7 @@ extern "C" void fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { foo::foo::strings::A(std::string_view((char const *)(arg0), len0)); } -extern "C" void fooX3AfooX2FstringsX00b(uint8_t *arg0, uint8_t *resultptr) { +extern "C" void fooX3AfooX2FstringsX00b(uint8_t *arg0) { auto result0 = foo::foo::strings::B(); auto const &vec1 = result0; auto ptr1 = vec1.data(); @@ -30,7 +30,7 @@ extern "C" void fooX3AfooX2FstringsX00b(uint8_t *arg0, uint8_t *resultptr) { } extern "C" void fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3, - uint8_t *arg4, uint8_t *resultptr) { + uint8_t *arg4) { auto len0 = arg1; auto len1 = arg3; diff --git a/crates/cpp/tests/native_strings/w2c2/Makefile b/crates/cpp/tests/native_strings/w2c2/Makefile index fb124a073..285eabfe2 100644 --- a/crates/cpp/tests/native_strings/w2c2/Makefile +++ b/crates/cpp/tests/native_strings/w2c2/Makefile @@ -10,7 +10,7 @@ w2c2_base.h: $(W2C2_PATH)/w2c2/w2c2_base.h %.pie.o: %.c $(CC) $(CXXFLAGS) -fPIE -o $@ -c $^ -libstrings.so: w2c2_guest.pie.o +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 @@ -28,5 +28,5 @@ valgrind: w2c2_guest.c: guest_release.wasm $(W2C2_PATH)/build/w2c2/w2c2 $^ $@ -x.c: - $(WIT_BINDGEN) bridge ../wit --instance guestrelease +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..0b8b50783 --- /dev/null +++ b/crates/cpp/tests/native_strings/w2c2/the-world_bridge_target.c @@ -0,0 +1,82 @@ + +#include +#include +#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; + fooX3AfooX2FstringsX00b(linmem+arg0); +} +// 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; + fooX3AfooX2FstringsX00c(linmem+arg0, arg1, linmem+arg2, arg3, linmem+arg4); +} +// 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()); + return result+linmem; +} +__attribute__ ((visibility ("default"))) +void cabi_post_fooX3AfooX2FstringsX23b(uint8_t * arg0) { + uint8_t *linmem = guestrelease_memory(get_app())->data; + guestrelease_cabi_post_fooX583AfooX582FstringsX5823b(get_app(), arg0-linmem); +} +// 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); + return result+linmem; +} +__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(), arg0-linmem); +} 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(); +} From 25a8005630d7a23eceaf73d56ca34a5411cbff8f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 20 May 2024 10:34:14 +0200 Subject: [PATCH 226/672] fmt --- crates/bridge/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/bridge/src/lib.rs b/crates/bridge/src/lib.rs index 4801ad01e..901410108 100644 --- a/crates/bridge/src/lib.rs +++ b/crates/bridge/src/lib.rs @@ -219,7 +219,9 @@ impl<'a> BridgeInterfaceGenerator<'a> { 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"))) "#); + self.gen + .src + .push_str(r#"__attribute__ ((visibility ("default"))) "#); } let res = if signature.results.is_empty() || return_via_pointer { "void".into() From 0c229c8ab430466ee0e89ed922fdbc65ab7d6b1e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 20 May 2024 10:56:45 +0200 Subject: [PATCH 227/672] more complex than expected, but solveable --- .../w2c2/the-world_bridge_target.c | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) 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 index 0b8b50783..c72ef0729 100644 --- a/crates/cpp/tests/native_strings/w2c2/the-world_bridge_target.c +++ b/crates/cpp/tests/native_strings/w2c2/the-world_bridge_target.c @@ -60,23 +60,31 @@ __attribute__ ((visibility ("default"))) uint8_t * fooX3AfooX2FstringsX23b() { uint8_t *linmem = guestrelease_memory(get_app())->data; uint32_t result = guestrelease_fooX3AfooX2FstringsX23b(get_app()); - return result+linmem; + 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(), arg0-linmem); + //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); - return result+linmem; + 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(), arg0-linmem); + //uint8_t *linmem = guestrelease_memory(get_app())->data; + guestrelease_cabi_post_fooX583AfooX582FstringsX5823c(get_app(), ((size_t*)arg0)[2]); } From 67b77017bf6674c3362df4e83f297f6dd5fd56a8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 20 May 2024 14:20:21 +0200 Subject: [PATCH 228/672] oh, the to be generated code is quite complex due to 32bit vs 64bit --- .../native_strings/w2c2/the-world_bridge_target.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) 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 index c72ef0729..3c2d967f5 100644 --- a/crates/cpp/tests/native_strings/w2c2/the-world_bridge_target.c +++ b/crates/cpp/tests/native_strings/w2c2/the-world_bridge_target.c @@ -38,7 +38,11 @@ void fooX3AfooX2Fstrings__a(void*app,U32 arg0,U32 arg1) { extern void fooX3AfooX2FstringsX00b(uint8_t *arg0); void fooX3AfooX2Fstrings__b(void*app,U32 arg0) { uint8_t *linmem = guestrelease_memory(get_app())->data; - fooX3AfooX2FstringsX00b(linmem+arg0); + 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, @@ -46,7 +50,11 @@ extern void fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, 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; - fooX3AfooX2FstringsX00c(linmem+arg0, arg1, linmem+arg2, arg3, linmem+arg4); + 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 From 7899b4ac88da4524e10f2bac611abca9cc8ae314 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 20 May 2024 14:30:42 +0200 Subject: [PATCH 229/672] add a readme and point out incomplete code generation --- crates/cpp/tests/README.md | 12 ++++++++++++ crates/cpp/tests/native_strings/w2c2/Makefile | 8 ++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 crates/cpp/tests/README.md diff --git a/crates/cpp/tests/README.md b/crates/cpp/tests/README.md new file mode 100644 index 000000000..7a23de197 --- /dev/null +++ b/crates/cpp/tests/README.md @@ -0,0 +1,12 @@ + +This folder contains examples on how to use the caninical 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. + +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. diff --git a/crates/cpp/tests/native_strings/w2c2/Makefile b/crates/cpp/tests/native_strings/w2c2/Makefile index 285eabfe2..423211f7d 100644 --- a/crates/cpp/tests/native_strings/w2c2/Makefile +++ b/crates/cpp/tests/native_strings/w2c2/Makefile @@ -28,5 +28,9 @@ valgrind: w2c2_guest.c: guest_release.wasm $(W2C2_PATH)/build/w2c2/w2c2 $^ $@ -the-world_bridge.c: $(WIT_BINDGEN) - $(WIT_BINDGEN) bridge ../wit --instance guestrelease --include w2c2_guest.h +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 From da94a038c54f581980bd6051d39d8d60957fb137 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 23 May 2024 12:35:07 +0200 Subject: [PATCH 230/672] implement initial splitting prototype --- crates/bridge/src/lib.rs | 4 +- crates/cpp/src/lib.rs | 143 +++++++++++++++++++++++++++------------ 2 files changed, 100 insertions(+), 47 deletions(-) diff --git a/crates/bridge/src/lib.rs b/crates/bridge/src/lib.rs index 901410108..0a64f6849 100644 --- a/crates/bridge/src/lib.rs +++ b/crates/bridge/src/lib.rs @@ -146,8 +146,8 @@ impl WorldGenerator for Bridge { &mut self, resolve: &wit_parser::Resolve, world: wit_parser::WorldId, - types: &[(&str, wit_parser::TypeId)], - files: &mut wit_bindgen_core::Files, + _types: &[(&str, wit_parser::TypeId)], + _files: &mut wit_bindgen_core::Files, ) { let world = &resolve.worlds[world]; uwriteln!(self.src, "// Import Types {}", world.name); diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 6bc7865bc..4e0a0d270 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -99,6 +99,8 @@ struct Cpp { c_src: SourceWithState, h_src: SourceWithState, c_src_head: Source, + // interface_includes: Vec, + // interface_header: SourceWithState, extern_c_decls: Source, dependencies: Includes, includes: Vec, @@ -353,6 +355,94 @@ impl Cpp { } } } + + fn finish_includes(&mut self) { + self.include(""); + self.include(""); // for std::move + if self.dependencies.needs_string { + self.include(""); + } + if self.dependencies.needs_string_view { + self.include(""); + } + if self.dependencies.needs_vector { + self.include(""); + } + if self.dependencies.needs_expected { + self.include(""); + } + if self.dependencies.needs_optional { + self.include(""); + } + if self.dependencies.needs_cstring { + self.include(""); + } + if self.dependencies.needs_imported_resources { + self.include(""); + } + if self.dependencies.needs_exported_resources { + self.include(""); + } + if self.dependencies.needs_variant { + self.include(""); + } + if self.dependencies.needs_tuple { + self.include(""); + } + if self.dependencies.needs_wit { + if self.opts.host_side() { + self.include(""); + } else { + self.include(""); + } + } + if self.dependencies.needs_memory { + self.include(""); + } + } + + fn start_new_file(&mut self) -> FileContext { + if 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, files: &mut Files, namespace: &[String], store: FileContext) { + if self.opts.split_interfaces { + let mut header = Source::default(); + self.finish_includes(); + self.h_src.change_namespace(&Default::default()); + uwriteln!(header, "#pragma once"); + 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); + } + files.push(&filename, header.as_bytes()); + + 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, + src: SourceWithState, + dependencies: Includes, } impl WorldGenerator for Cpp { @@ -389,8 +479,9 @@ impl WorldGenerator for Cpp { resolve: &Resolve, name: &WorldKey, id: InterfaceId, - _files: &mut Files, + files: &mut Files, ) { + let store = self.start_new_file(); self.imported_interfaces.insert(id); let wasm_import_module = resolve.name_world_key(name); let binding = Some(name); @@ -405,6 +496,7 @@ impl WorldGenerator for Cpp { gen.generate_function(func, &TypeOwner::Interface(id), AbiVariant::GuestImport); } } + self.finish_file(files, &namespace, store); } fn export_interface( @@ -412,8 +504,9 @@ impl WorldGenerator for Cpp { resolve: &Resolve, name: &WorldKey, id: InterfaceId, - _files: &mut Files, + files: &mut Files, ) -> anyhow::Result<()> { + let store = self.start_new_file(); self.h_src .src .push_str(&format!("// export_interface {name:?}\n")); @@ -431,6 +524,7 @@ impl WorldGenerator for Cpp { gen.generate_function(func, &TypeOwner::Interface(id), AbiVariant::GuestExport); } } + self.finish_file(files, &namespace, store); Ok(()) } @@ -531,48 +625,7 @@ impl WorldGenerator for Cpp { world.name.to_shouty_snake_case(), ); } - self.include(""); - self.include(""); // for std::move - if self.dependencies.needs_string { - self.include(""); - } - if self.dependencies.needs_string_view { - self.include(""); - } - if self.dependencies.needs_vector { - self.include(""); - } - if self.dependencies.needs_expected { - self.include(""); - } - if self.dependencies.needs_optional { - self.include(""); - } - if self.dependencies.needs_cstring { - self.include(""); - } - if self.dependencies.needs_imported_resources { - self.include(""); - } - if self.dependencies.needs_exported_resources { - self.include(""); - } - if self.dependencies.needs_variant { - self.include(""); - } - if self.dependencies.needs_tuple { - self.include(""); - } - if self.dependencies.needs_wit { - if self.opts.host_side() { - self.include(""); - } else { - self.include(""); - } - } - if self.dependencies.needs_memory { - self.include(""); - } + self.finish_includes(); if self.opts.short_cut { uwriteln!(h_str.src, "#define WIT_HOST_DIRECT"); @@ -3260,7 +3313,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } abi::Instruction::Return { amt, func } => { - let guest_import = matches!(self.variant, AbiVariant::GuestImport); + // let guest_import = matches!(self.variant, AbiVariant::GuestImport); match amt { 0 => {} _ => { From cc0c0558308b02c7a551f1ab81cf804f47b7c5fa Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 23 May 2024 13:57:54 +0200 Subject: [PATCH 231/672] separate files for resources --- crates/cpp/src/lib.rs | 80 +++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 4e0a0d270..d51dd3075 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -298,7 +298,7 @@ impl Cpp { } } - fn clang_format(code: &mut Source) { + fn clang_format(code: &mut String) { let mut child = Command::new("clang-format") .stdin(Stdio::piped()) .stdout(Stdio::piped()) @@ -310,13 +310,8 @@ impl Cpp { .unwrap() .write_all(code.as_bytes()) .unwrap(); - code.as_mut_string().truncate(0); - child - .stdout - .take() - .unwrap() - .read_to_string(code.as_mut_string()) - .unwrap(); + code.truncate(0); + child.stdout.take().unwrap().read_to_string(code).unwrap(); let status = child.wait().unwrap(); assert!(status.success()); } @@ -401,8 +396,8 @@ impl Cpp { } } - fn start_new_file(&mut self) -> FileContext { - if self.opts.split_interfaces { + fn start_new_file(&mut self, condition: Option) -> 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()), @@ -413,9 +408,10 @@ impl Cpp { } } - fn finish_file(&mut self, files: &mut Files, namespace: &[String], store: FileContext) { - if self.opts.split_interfaces { - let mut header = Source::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"); @@ -423,12 +419,12 @@ impl Cpp { uwriteln!(header, "#include {include}"); } header.push_str(&self.h_src.src); - let mut filename = namespace.join("_"); + let mut filename = namespace.join("-"); filename.push_str(".h"); if self.opts.format { Self::clang_format(&mut header); } - files.push(&filename, header.as_bytes()); + 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); @@ -479,9 +475,9 @@ impl WorldGenerator for Cpp { resolve: &Resolve, name: &WorldKey, id: InterfaceId, - files: &mut Files, + _files: &mut Files, ) { - let store = self.start_new_file(); + 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); @@ -496,7 +492,7 @@ impl WorldGenerator for Cpp { gen.generate_function(func, &TypeOwner::Interface(id), AbiVariant::GuestImport); } } - self.finish_file(files, &namespace, store); + self.finish_file(&namespace, store); } fn export_interface( @@ -504,9 +500,9 @@ impl WorldGenerator for Cpp { resolve: &Resolve, name: &WorldKey, id: InterfaceId, - files: &mut Files, + _files: &mut Files, ) -> anyhow::Result<()> { - let store = self.start_new_file(); + let store = self.start_new_file(None); self.h_src .src .push_str(&format!("// export_interface {name:?}\n")); @@ -524,7 +520,7 @@ impl WorldGenerator for Cpp { gen.generate_function(func, &TypeOwner::Interface(id), AbiVariant::GuestExport); } } - self.finish_file(files, &namespace, store); + self.finish_file(&namespace, store); Ok(()) } @@ -736,8 +732,8 @@ impl WorldGenerator for Cpp { ); if self.opts.format { - Self::clang_format(&mut c_str.src); - Self::clang_format(&mut h_str.src); + 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 { @@ -1948,21 +1944,24 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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 mut headerfile = SourceWithState::default(); let namespc = namespace(self.resolve, &type_.owner, !guest_import, &self.gen.opts); let pascal = name.to_upper_camel_case(); - let user_filename = namespc.join("-") + "-" + &pascal + ".h"; + 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()); + //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); + //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 {user_filename}.template. + * Updated versions of this file are generated into {pascal}.template. */"# ); } @@ -2091,18 +2090,19 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> self.generate_function(&func2, &TypeOwner::Interface(intf), variant); } uwriteln!(self.gen.h_src.src, "}};\n"); - 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()); - } + 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()); + // } } } From 32c35e990ea510a3b4fde7492d12ca10f5c76fe6 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Tue, 28 May 2024 08:18:30 -0600 Subject: [PATCH 232/672] support `task.wait` Signed-off-by: Joel Dice --- crates/rust/src/async_support.rs | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/crates/rust/src/async_support.rs b/crates/rust/src/async_support.rs index 3ef923220..df3e41996 100644 --- a/crates/rust/src/async_support.rs +++ b/crates/rust/src/async_support.rs @@ -372,3 +372,40 @@ pub fn new_stream() -> (StreamSender, StreamReceiver) { pub fn spawn(future: impl Future + 'static) { unsafe { SPAWNED.push(Box::pin(future)) } } + +fn wait(state: &mut FutureState) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-wait]"] + fn wait(_: *mut i32) -> i32; + } + let mut payload = [0i32; 2]; + unsafe { + let event0 = wait(payload.as_mut_ptr()); + callback(state as *mut _ as _, event0, payload[0], payload[1]); + } + } +} + +// TODO: refactor so `'static` bounds aren't necessary +pub fn block_on(future: impl Future + 'static) -> T { + let (mut tx, mut rx) = oneshot::channel(); + let state = &mut FutureState( + [Box::pin(future.map(move |v| drop(tx.send(v)))) as BoxFuture] + .into_iter() + .collect(), + ); + loop { + match unsafe { poll(state) } { + Poll::Ready(()) => break rx.try_recv().unwrap().unwrap(), + Poll::Pending => wait(state), + } + } +} From 4c57c309558529b906b778a6b14e7846471a9ce5 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 30 May 2024 00:08:05 +0200 Subject: [PATCH 233/672] more of the mesh skeleton --- crates/cpp/tests/native_mesh/Makefile | 11 +++ .../tests/native_mesh/component_a/Makefile | 9 +++ .../cpp/tests/native_mesh/component_a/a.cpp | 66 +++++++++++++++ .../cpp/tests/native_mesh/component_a/a_cpp.h | 28 +++++++ .../native_mesh/component_a/libcomponent_b.so | 1 + .../tests/native_mesh/component_a/main.cpp | 12 +++ .../tests/native_mesh/component_b/Makefile | 9 +++ .../cpp/tests/native_mesh/component_b/b.cpp | 81 +++++++++++++++++++ .../cpp/tests/native_mesh/component_b/b_cpp.h | 19 +++++ .../component_b/exports-foo-foo-resources-R.h | 31 +++++++ .../tests/native_mesh/component_b/impl.cpp | 8 ++ crates/cpp/tests/native_mesh/mesh/Makefile | 9 +++ crates/cpp/tests/native_mesh/mesh/Makefile~ | 9 +++ .../mesh/exports-foo-foo-resources-R.h | 24 ++++++ .../mesh/exports-foo-foo-resources.h | 15 ++++ .../tests/native_mesh/mesh/libcomponent_b.so | 1 + .../tests/native_mesh/mesh/mesh_cpp_native.h | 31 +++++++ .../tests/native_mesh/mesh/mesh_native.cpp | 78 ++++++++++++++++++ .../native_mesh/wit/resources_simple.wit | 22 +++++ 19 files changed, 464 insertions(+) create mode 100644 crates/cpp/tests/native_mesh/Makefile create mode 100644 crates/cpp/tests/native_mesh/component_a/Makefile create mode 100644 crates/cpp/tests/native_mesh/component_a/a.cpp create mode 100644 crates/cpp/tests/native_mesh/component_a/a_cpp.h create mode 120000 crates/cpp/tests/native_mesh/component_a/libcomponent_b.so create mode 100644 crates/cpp/tests/native_mesh/component_a/main.cpp create mode 100644 crates/cpp/tests/native_mesh/component_b/Makefile create mode 100644 crates/cpp/tests/native_mesh/component_b/b.cpp create mode 100644 crates/cpp/tests/native_mesh/component_b/b_cpp.h create mode 100644 crates/cpp/tests/native_mesh/component_b/exports-foo-foo-resources-R.h create mode 100644 crates/cpp/tests/native_mesh/component_b/impl.cpp create mode 100644 crates/cpp/tests/native_mesh/mesh/Makefile create mode 100644 crates/cpp/tests/native_mesh/mesh/Makefile~ create mode 100644 crates/cpp/tests/native_mesh/mesh/exports-foo-foo-resources-R.h create mode 100644 crates/cpp/tests/native_mesh/mesh/exports-foo-foo-resources.h create mode 120000 crates/cpp/tests/native_mesh/mesh/libcomponent_b.so create mode 100644 crates/cpp/tests/native_mesh/mesh/mesh_cpp_native.h create mode 100644 crates/cpp/tests/native_mesh/mesh/mesh_native.cpp create mode 100644 crates/cpp/tests/native_mesh/wit/resources_simple.wit diff --git a/crates/cpp/tests/native_mesh/Makefile b/crates/cpp/tests/native_mesh/Makefile new file mode 100644 index 000000000..243e972a2 --- /dev/null +++ b/crates/cpp/tests/native_mesh/Makefile @@ -0,0 +1,11 @@ + +all: + make -C component_b $@ + make -C mesh $@ + make -C component_a $@ + +bindgen: + 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..de9203652 --- /dev/null +++ b/crates/cpp/tests/native_mesh/component_a/Makefile @@ -0,0 +1,9 @@ +CXXFLAGS=-g -O0 -I../../../helper-types + +all: component_a + +component_a: a.cpp main.cpp + $(CXX) -shared $(CXXFLAGS) -o $@ $^ + +bindgen: + ../../../../../target/debug/wit-bindgen cpp ../wit -w a --wasm64 --format 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..5a02f8030 --- /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 // 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("consume"))) void + fooX3AfooX2FresourcesX00consume(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::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..4b588eca6 --- /dev/null +++ b/crates/cpp/tests/native_mesh/component_a/a_cpp.h @@ -0,0 +1,28 @@ +// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT! +#ifndef __CPP_GUEST_BINDINGS_A_H +#define __CPP_GUEST_BINDINGS_A_H +#include +#include +#include +#include +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/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/Makefile b/crates/cpp/tests/native_mesh/component_b/Makefile new file mode 100644 index 000000000..f6dd75f5f --- /dev/null +++ b/crates/cpp/tests/native_mesh/component_b/Makefile @@ -0,0 +1,9 @@ +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 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..6d0f7db37 --- /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 // 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 +#include +// 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 +#include +#include +#include +/* 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 { + 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..e5b5a738a --- /dev/null +++ b/crates/cpp/tests/native_mesh/mesh/Makefile @@ -0,0 +1,9 @@ +CXXFLAGS=-fPIC -g -O0 -I../../../helper-types + +all: libmesh.so + +libmesh.so: mesh_native.cpp + $(CXX) -shared $(CXXFLAGS) -o $@ $^ + +bindgen: + ../../../../../target/debug/wit-bindgen cpp ../wit -w mesh --wasm64 --format --direct --split-interfaces diff --git a/crates/cpp/tests/native_mesh/mesh/Makefile~ b/crates/cpp/tests/native_mesh/mesh/Makefile~ new file mode 100644 index 000000000..f6dd75f5f --- /dev/null +++ b/crates/cpp/tests/native_mesh/mesh/Makefile~ @@ -0,0 +1,9 @@ +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 diff --git a/crates/cpp/tests/native_mesh/mesh/exports-foo-foo-resources-R.h b/crates/cpp/tests/native_mesh/mesh/exports-foo-foo-resources-R.h new file mode 100644 index 000000000..355db7b2d --- /dev/null +++ b/crates/cpp/tests/native_mesh/mesh/exports-foo-foo-resources-R.h @@ -0,0 +1,24 @@ +#pragma once +#include +#include +#include +#include +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 diff --git a/crates/cpp/tests/native_mesh/mesh/exports-foo-foo-resources.h b/crates/cpp/tests/native_mesh/mesh/exports-foo-foo-resources.h new file mode 100644 index 000000000..11c3bb1ee --- /dev/null +++ b/crates/cpp/tests/native_mesh/mesh/exports-foo-foo-resources.h @@ -0,0 +1,15 @@ +#pragma once +#include "exports-foo-foo-resources-R.h" +#include +#include +// export_interface Interface(Id { idx: 0 }) +namespace exports { +namespace foo { +namespace foo { +namespace resources { +R Create(); +void Consume(R &&o); +} // namespace resources +} // namespace foo +} // namespace foo +} // namespace exports 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_cpp_native.h b/crates/cpp/tests/native_mesh/mesh/mesh_cpp_native.h new file mode 100644 index 000000000..a2be252ec --- /dev/null +++ b/crates/cpp/tests/native_mesh/mesh/mesh_cpp_native.h @@ -0,0 +1,31 @@ +// 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 "exports-foo-foo-resources.h" +#include +#include +#include +#include +/* User class definition file, autogenerated once, then user modified + * Updated versions of this file are generated into R.template. + */ +namespace foo { +namespace foo { +namespace resources { +class R : public wit::ResourceImportBase { + +public: + static void Dtor(R *self) { delete self; } + R(uint32_t a); + static Owned New(uint32_t a) { return Owned(new R(a)); } + void Add(uint32_t b); +}; + +R::Owned Create(); +void Consume(R::Owned o); +} // namespace resources +} // namespace foo +} // namespace foo + +#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..18744da36 --- /dev/null +++ b/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp @@ -0,0 +1,78 @@ +// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT! +#include "mesh_cpp_native.h" +template std::map wit::ResourceTable::resources; +#include +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("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 fooX3AfooX2FresourcesX00consume(int32_t arg0) { + auto obj0 = foo::foo::resources::R::remove_resource(arg0); + assert(obj0.has_value()); + 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))); + this->index = wit::ResourceExportBase{ret}.get_handle(); + this->rep = *lookup_resource(ret); +} +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) { + exports::foo::foo::resources::R::remove_resource(arg0); +} +exports::foo::foo::resources::R exports::foo::foo::resources::Create() { + auto ret = fooX3AfooX2FresourcesX23create(); + return wit::ResourceExportBase{ret}; +} +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_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); + consume: func(o: r); +} + +world a { + import resources; +} +world b { + export resources; +} +world mesh { + import resources; + export resources; +} From 98efeb8261e3e173da233990cca1298dd6f500d9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 30 May 2024 00:11:32 +0200 Subject: [PATCH 234/672] proper prefix --- crates/cpp/tests/native_mesh/mesh/Makefile | 4 +-- ...R.h => mesh-exports-foo-foo-resources-R.h} | 2 ++ ...ces.h => mesh-exports-foo-foo-resources.h} | 4 ++- .../tests/native_mesh/mesh/mesh_cpp_native.h | 4 ++- .../tests/native_mesh/mesh/mesh_native.cpp | 34 ++++++++++--------- 5 files changed, 28 insertions(+), 20 deletions(-) rename crates/cpp/tests/native_mesh/mesh/{exports-foo-foo-resources-R.h => mesh-exports-foo-foo-resources-R.h} (92%) rename crates/cpp/tests/native_mesh/mesh/{exports-foo-foo-resources.h => mesh-exports-foo-foo-resources.h} (77%) diff --git a/crates/cpp/tests/native_mesh/mesh/Makefile b/crates/cpp/tests/native_mesh/mesh/Makefile index e5b5a738a..1ae13141c 100644 --- a/crates/cpp/tests/native_mesh/mesh/Makefile +++ b/crates/cpp/tests/native_mesh/mesh/Makefile @@ -3,7 +3,7 @@ CXXFLAGS=-fPIC -g -O0 -I../../../helper-types all: libmesh.so libmesh.so: mesh_native.cpp - $(CXX) -shared $(CXXFLAGS) -o $@ $^ + $(CXX) -shared $(CXXFLAGS) -o $@ $^ libcomponent_b.so bindgen: - ../../../../../target/debug/wit-bindgen cpp ../wit -w mesh --wasm64 --format --direct --split-interfaces + ../../../../../target/debug/wit-bindgen cpp ../wit -w mesh --wasm64 --format --direct --split-interfaces --internal-prefix mesh diff --git a/crates/cpp/tests/native_mesh/mesh/exports-foo-foo-resources-R.h b/crates/cpp/tests/native_mesh/mesh/mesh-exports-foo-foo-resources-R.h similarity index 92% rename from crates/cpp/tests/native_mesh/mesh/exports-foo-foo-resources-R.h rename to crates/cpp/tests/native_mesh/mesh/mesh-exports-foo-foo-resources-R.h index 355db7b2d..790301f35 100644 --- a/crates/cpp/tests/native_mesh/mesh/exports-foo-foo-resources-R.h +++ b/crates/cpp/tests/native_mesh/mesh/mesh-exports-foo-foo-resources-R.h @@ -3,6 +3,7 @@ #include #include #include +namespace mesh { namespace exports { namespace foo { namespace foo { @@ -22,3 +23,4 @@ class R : public wit::ResourceExportBase { } // namespace foo } // namespace foo } // namespace exports +} // namespace mesh diff --git a/crates/cpp/tests/native_mesh/mesh/exports-foo-foo-resources.h b/crates/cpp/tests/native_mesh/mesh/mesh-exports-foo-foo-resources.h similarity index 77% rename from crates/cpp/tests/native_mesh/mesh/exports-foo-foo-resources.h rename to crates/cpp/tests/native_mesh/mesh/mesh-exports-foo-foo-resources.h index 11c3bb1ee..4e4bc5e58 100644 --- a/crates/cpp/tests/native_mesh/mesh/exports-foo-foo-resources.h +++ b/crates/cpp/tests/native_mesh/mesh/mesh-exports-foo-foo-resources.h @@ -1,8 +1,9 @@ #pragma once -#include "exports-foo-foo-resources-R.h" +#include "mesh-exports-foo-foo-resources-R.h" #include #include // export_interface Interface(Id { idx: 0 }) +namespace mesh { namespace exports { namespace foo { namespace foo { @@ -13,3 +14,4 @@ void Consume(R &&o); } // 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 index a2be252ec..614c32e27 100644 --- a/crates/cpp/tests/native_mesh/mesh/mesh_cpp_native.h +++ b/crates/cpp/tests/native_mesh/mesh/mesh_cpp_native.h @@ -2,7 +2,7 @@ #ifndef __CPP_NATIVE_BINDINGS_MESH_H #define __CPP_NATIVE_BINDINGS_MESH_H #define WIT_HOST_DIRECT -#include "exports-foo-foo-resources.h" +#include "mesh-exports-foo-foo-resources.h" #include #include #include @@ -10,6 +10,7 @@ /* 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 { @@ -27,5 +28,6 @@ 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 index 18744da36..873dc276b 100644 --- a/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp +++ b/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp @@ -18,59 +18,61 @@ 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); + auto ptr = mesh::foo::foo::resources::R::remove_resource(arg0); assert(ptr.has_value()); - foo::foo::resources::R::Dtor(*ptr); + mesh::foo::foo::resources::R::Dtor(*ptr); } extern "C" int32_t fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t arg0) { - auto result0 = foo::foo::resources::R::New((uint32_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) { - (**foo::foo::resources::R::lookup_resource(arg0)).Add((uint32_t(arg1))); + (**mesh::foo::foo::resources::R::lookup_resource(arg0)).Add((uint32_t(arg1))); } extern "C" int32_t fooX3AfooX2FresourcesX00create() { - auto result0 = foo::foo::resources::Create(); + auto result0 = mesh::foo::foo::resources::Create(); return result0.release()->get_handle(); } extern "C" void fooX3AfooX2FresourcesX00consume(int32_t arg0) { - auto obj0 = foo::foo::resources::R::remove_resource(arg0); + auto obj0 = mesh::foo::foo::resources::R::remove_resource(arg0); assert(obj0.has_value()); - foo::foo::resources::Consume(foo::foo::resources::R::Owned(*obj0)); + mesh::foo::foo::resources::Consume( + mesh::foo::foo::resources::R::Owned(*obj0)); } -exports::foo::foo::resources::R::~R() { +mesh::exports::foo::foo::resources::R::~R() { if (this->rep) { fooX3AfooX2FresourcesX23X5BdtorX5Dr(this->rep); } } -exports::foo::foo::resources::R::R(uint32_t a) { +mesh::exports::foo::foo::resources::R::R(uint32_t a) { auto ret = fooX3AfooX2FresourcesX23X5BconstructorX5Dr((int32_t(a))); this->index = wit::ResourceExportBase{ret}.get_handle(); this->rep = *lookup_resource(ret); } -void exports::foo::foo::resources::R::Add(uint32_t b) const { +void mesh::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) +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 exports::foo::foo::resources::R::store_resource(std::move(arg0)); + return mesh::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); + return *mesh::exports::foo::foo::resources::R::lookup_resource(arg0); } extern "C" void X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t arg0) { - exports::foo::foo::resources::R::remove_resource(arg0); + mesh::exports::foo::foo::resources::R::remove_resource(arg0); } -exports::foo::foo::resources::R exports::foo::foo::resources::Create() { +mesh::exports::foo::foo::resources::R +mesh::exports::foo::foo::resources::Create() { auto ret = fooX3AfooX2FresourcesX23create(); return wit::ResourceExportBase{ret}; } -void exports::foo::foo::resources::Consume(R &&o) { +void mesh::exports::foo::foo::resources::Consume(R &&o) { auto rep0 = o.take_rep(); fooX3AfooX2FresourcesX23consume(o.get_handle()); } From 25f5e4c9f0ab647d65efe7d28c748d1cf28a2c98 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 30 May 2024 00:52:12 +0200 Subject: [PATCH 235/672] mesh example --- .../tests/native_mesh/component_a/Makefile | 2 +- .../tests/native_mesh/component_a/libmesh.so | 1 + crates/cpp/tests/native_mesh/mesh/Makefile | 2 +- crates/cpp/tests/native_mesh/mesh/impl.cpp | 22 +++++++++++++++++++ .../tests/native_mesh/mesh/mesh_cpp_native.h | 6 ++++- .../tests/native_mesh/mesh/mesh_native.cpp | 20 +++++------------ 6 files changed, 35 insertions(+), 18 deletions(-) create mode 120000 crates/cpp/tests/native_mesh/component_a/libmesh.so create mode 100644 crates/cpp/tests/native_mesh/mesh/impl.cpp diff --git a/crates/cpp/tests/native_mesh/component_a/Makefile b/crates/cpp/tests/native_mesh/component_a/Makefile index de9203652..55cdd9287 100644 --- a/crates/cpp/tests/native_mesh/component_a/Makefile +++ b/crates/cpp/tests/native_mesh/component_a/Makefile @@ -3,7 +3,7 @@ CXXFLAGS=-g -O0 -I../../../helper-types all: component_a component_a: a.cpp main.cpp - $(CXX) -shared $(CXXFLAGS) -o $@ $^ + $(CXX) $(CXXFLAGS) -o $@ $^ libmesh.so libcomponent_b.so bindgen: ../../../../../target/debug/wit-bindgen cpp ../wit -w a --wasm64 --format 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/mesh/Makefile b/crates/cpp/tests/native_mesh/mesh/Makefile index 1ae13141c..d17a17024 100644 --- a/crates/cpp/tests/native_mesh/mesh/Makefile +++ b/crates/cpp/tests/native_mesh/mesh/Makefile @@ -2,7 +2,7 @@ CXXFLAGS=-fPIC -g -O0 -I../../../helper-types all: libmesh.so -libmesh.so: mesh_native.cpp +libmesh.so: mesh_native.cpp impl.cpp $(CXX) -shared $(CXXFLAGS) -o $@ $^ libcomponent_b.so bindgen: 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/mesh_cpp_native.h b/crates/cpp/tests/native_mesh/mesh/mesh_cpp_native.h index 614c32e27..be64eda70 100644 --- a/crates/cpp/tests/native_mesh/mesh/mesh_cpp_native.h +++ b/crates/cpp/tests/native_mesh/mesh/mesh_cpp_native.h @@ -15,12 +15,16 @@ namespace foo { namespace foo { namespace resources { class R : public wit::ResourceImportBase { - + 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(); diff --git a/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp b/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp index 873dc276b..51af4afb0 100644 --- a/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp +++ b/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp @@ -2,21 +2,11 @@ #include "mesh_cpp_native.h" template std::map wit::ResourceTable::resources; #include -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("consume"))) void - fooX3AfooX2FresourcesX23consume(int32_t); +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()); From a76be92ff04b47e9bdbe225b955b1dff71e461bd Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 31 May 2024 10:54:44 +0200 Subject: [PATCH 236/672] remove backup file --- crates/cpp/tests/native_mesh/mesh/Makefile~ | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 crates/cpp/tests/native_mesh/mesh/Makefile~ diff --git a/crates/cpp/tests/native_mesh/mesh/Makefile~ b/crates/cpp/tests/native_mesh/mesh/Makefile~ deleted file mode 100644 index f6dd75f5f..000000000 --- a/crates/cpp/tests/native_mesh/mesh/Makefile~ +++ /dev/null @@ -1,9 +0,0 @@ -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 From fa09681e196f35309c454736a63b23b5a324bedb Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 31 May 2024 11:04:30 +0200 Subject: [PATCH 237/672] fix compilation with latest upstream changes --- Cargo.lock | 2 +- crates/bridge/src/lib.rs | 2 +- crates/cpp/src/lib.rs | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e7a7d064..708baa805 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2392,7 +2392,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.208.0", + "wasm-encoder 0.209.0", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", diff --git a/crates/bridge/src/lib.rs b/crates/bridge/src/lib.rs index 0a64f6849..50f1e2b6c 100644 --- a/crates/bridge/src/lib.rs +++ b/crates/bridge/src/lib.rs @@ -170,7 +170,7 @@ impl Bridge { BridgeInterfaceGenerator { gen: self, resolve } } - fn wasm_type(&self, ty: WasmType, var: TypeVariant) -> String { + fn wasm_type(&self, ty: WasmType, _var: TypeVariant) -> String { match ty { WasmType::I32 => todo!(), WasmType::I64 => todo!(), diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index d51dd3075..1f61c9125 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -12,7 +12,7 @@ use wit_bindgen_core::{ make_external_component, make_external_symbol, uwrite, uwriteln, wit_parser::{ AddressSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, Resolve, Results, - SizeAlign, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, + SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, }, Files, InterfaceGenerator, Source, WorldGenerator, }; @@ -2014,6 +2014,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> params: vec![("self".into(), Type::Id(id))], results: Results::Named(vec![]), docs: Docs::default(), + stability: Stability::Unknown, }; self.generate_function(&func, &TypeOwner::Interface(intf), variant); } @@ -2041,6 +2042,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> params: func.params.clone(), results: Results::Anon(Type::Id(id)), docs: Docs::default(), + stability: Stability::Unknown, }; self.generate_function(&func2, &TypeOwner::Interface(intf), variant); } @@ -2068,6 +2070,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> params: vec![("self".into(), Type::Id(id))], results: Results::Anon(Type::S32), docs: Docs::default(), + stability: Stability::Unknown, }; self.generate_function(&func, &TypeOwner::Interface(intf), variant); @@ -2077,6 +2080,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> params: vec![("id".into(), Type::S32)], results: Results::Anon(Type::Id(id)), docs: Docs::default(), + stability: Stability::Unknown, }; self.generate_function(&func1, &TypeOwner::Interface(intf), variant); @@ -2086,6 +2090,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> params: vec![("id".into(), Type::S32)], results: Results::Named(vec![]), docs: Docs::default(), + stability: Stability::Unknown, }; self.generate_function(&func2, &TypeOwner::Interface(intf), variant); } From ed631cdf8765eb02bc46c5a1000d879e9fac6d52 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 31 May 2024 17:33:36 +0200 Subject: [PATCH 238/672] more extensive type documentation --- crates/cpp/helper-types/wit-common.h | 6 +----- crates/cpp/helper-types/wit-guest.h | 15 +++++++++++++++ crates/cpp/helper-types/wit-host.h | 8 +++++++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h index 225f57755..c8316ba62 100644 --- a/crates/cpp/helper-types/wit-common.h +++ b/crates/cpp/helper-types/wit-common.h @@ -13,7 +13,7 @@ namespace wit { #if __cplusplus > 202001L using std::span; #else -// minimal implementation to get things going +/// Minimal span (vector view) implementation for older C++ environments template class span { T const *address; size_t length; @@ -32,8 +32,4 @@ template class span { span(std::vector const&vec) : address(vec.data()), length(vec.size()) {} }; #endif - -template struct Owned { - T *ptr; -}; } // namespace wit diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 745b268df..699d3cb9e 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -6,6 +6,10 @@ #include "wit-common.h" 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; @@ -43,6 +47,10 @@ class string { } }; +/// 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 vector { T *data_; @@ -81,6 +89,10 @@ class vector { } }; +/// @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 template class ResourceExportBase { public: struct Deleter { @@ -102,6 +114,9 @@ template class ResourceExportBase { int32_t into_handle() { int32_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: static const int32_t invalid = -1; diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 11dcfb56e..df435e269 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -43,6 +43,7 @@ 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_; @@ -102,6 +103,7 @@ class string { #endif }; +/// A vector in linear memory (host-side handle) template class vector { guest_address data_; @@ -124,6 +126,7 @@ class vector { guest_size size() const { return length; } }; +/// Wrapper for specialized de-allocation of a returned type (calling cabi_post_*) template class guest_owned : public T { guest_address data_; #ifdef WIT_HOST_WAMR @@ -183,6 +186,8 @@ template class guest_owned : public T { #endif }; +/// @brief Helper class to map between IDs and resources +/// @tparam R Type of the Resource template class ResourceTable { static std::map resources; @@ -208,7 +213,7 @@ template class ResourceTable { } }; -// guest exported resource +/// Guest exported resource (host side handle) class ResourceExportBase : public ResourceTable { protected: guest_address rep; @@ -230,6 +235,7 @@ class ResourceExportBase : public ResourceTable { guest_address take_rep() { guest_address res = rep; rep=0; return res; } }; +/// Host defined resource (host side definition) template class ResourceImportBase : public ResourceTable { int32_t index; From 257842f03458f8d9d34379b8c4d98479328de032 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 1 Jun 2024 18:35:19 +0200 Subject: [PATCH 239/672] make clean + forwarder start --- crates/cpp/helper-types/wit-host.h | 18 ++++++++++++++++++ crates/cpp/tests/native_mesh/Makefile | 5 +++++ .../cpp/tests/native_mesh/component_a/Makefile | 3 +++ .../cpp/tests/native_mesh/component_b/Makefile | 3 +++ crates/cpp/tests/native_mesh/mesh/Makefile | 3 +++ 5 files changed, 32 insertions(+) diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index df435e269..a6a9b3408 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -257,4 +257,22 @@ class ResourceImportBase : public ResourceTable { } }; +/// Host representation of a resource defined in another component +/// +/// Acts like ResourceImportBase (e.g. definition); +/// R should derive from ResourceExportBase +template +class ResourceForwarder : public R { + typedef R Owned; + ResourceForwarder(int32_t id) : R(ResourceExportBase(id)) {} + std::optional lookup_resource(int32_t id) { + // TODO: Handle not found + return R(ResourceExportBase(id)); + } + std::optional remove_resource(int32_t id) { + std::optional result = R::remove_resource(id); + if (!result.has_value()) return std::optional(); + return *result; + } +}; } // namespace wit diff --git a/crates/cpp/tests/native_mesh/Makefile b/crates/cpp/tests/native_mesh/Makefile index 243e972a2..497019c6d 100644 --- a/crates/cpp/tests/native_mesh/Makefile +++ b/crates/cpp/tests/native_mesh/Makefile @@ -8,4 +8,9 @@ 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 index 55cdd9287..829b6275e 100644 --- a/crates/cpp/tests/native_mesh/component_a/Makefile +++ b/crates/cpp/tests/native_mesh/component_a/Makefile @@ -7,3 +7,6 @@ component_a: a.cpp main.cpp 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_b/Makefile b/crates/cpp/tests/native_mesh/component_b/Makefile index f6dd75f5f..2fff54abd 100644 --- a/crates/cpp/tests/native_mesh/component_b/Makefile +++ b/crates/cpp/tests/native_mesh/component_b/Makefile @@ -7,3 +7,6 @@ libcomponent_b.so: b.cpp impl.cpp bindgen: ../../../../../target/debug/wit-bindgen cpp ../wit -w b --wasm64 --format + +clean: + -rm *~ *.so *.o diff --git a/crates/cpp/tests/native_mesh/mesh/Makefile b/crates/cpp/tests/native_mesh/mesh/Makefile index d17a17024..523755e76 100644 --- a/crates/cpp/tests/native_mesh/mesh/Makefile +++ b/crates/cpp/tests/native_mesh/mesh/Makefile @@ -7,3 +7,6 @@ libmesh.so: mesh_native.cpp impl.cpp bindgen: ../../../../../target/debug/wit-bindgen cpp ../wit -w mesh --wasm64 --format --direct --split-interfaces --internal-prefix mesh + +clean: + -rm *~ libmesh.so *.o From 7ea34ebba526033d2ed950c7b949947d4fb9522d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 6 Jun 2024 22:07:52 +0900 Subject: [PATCH 240/672] fix memory leak --- crates/cpp/helper-types/wit-host.h | 7 ++++++- crates/cpp/tests/native_resources/Makefile | 3 +++ crates/cpp/tests/native_resources/the_world_native.cpp | 5 +++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index a6a9b3408..bb93ae756 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -219,7 +219,7 @@ class ResourceExportBase : public ResourceTable { guest_address rep; int32_t index; public: - ResourceExportBase() : rep(0), index(-1) {} + ResourceExportBase() : rep(nullptr), 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; @@ -230,6 +230,11 @@ class ResourceExportBase : public ResourceTable { index = b.index; b.rep = 0; } + ~ResourceExportBase() { + if (index>=0 && rep!=nullptr) { + 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; } diff --git a/crates/cpp/tests/native_resources/Makefile b/crates/cpp/tests/native_resources/Makefile index a53c0bc1d..32746aaa4 100644 --- a/crates/cpp/tests/native_resources/Makefile +++ b/crates/cpp/tests/native_resources/Makefile @@ -16,3 +16,6 @@ clean: 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/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index c9c5319f7..97136bbc6 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -52,8 +52,9 @@ exports::foo::foo::resources::R::~R() { } exports::foo::foo::resources::R::R(uint32_t a) { auto ret = fooX3AfooX2FresourcesX23X5BconstructorX5Dr((int32_t(a))); - this->index = wit::ResourceExportBase{ret}.get_handle(); - this->rep = *lookup_resource(ret); + 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))); From aebac6567607d4115effe8239f182e000a6b9d88 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 6 Jun 2024 22:11:18 +0900 Subject: [PATCH 241/672] clang format --- crates/cpp/helper-types/wit-common.h | 5 +- crates/cpp/helper-types/wit-guest.h | 57 ++++++----- crates/cpp/helper-types/wit-host.h | 147 ++++++++++++++------------- 3 files changed, 109 insertions(+), 100 deletions(-) diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h index c8316ba62..a645bef09 100644 --- a/crates/cpp/helper-types/wit-common.h +++ b/crates/cpp/helper-types/wit-common.h @@ -1,8 +1,9 @@ #pragma once #include -#include #include +#include +#include // size_t #if __cplusplus > 202001L #include #else @@ -29,7 +30,7 @@ template class span { T const &operator[](size_t index) { return address[index]; } // create from any compatible vector (borrows data!) template - span(std::vector const&vec) : address(vec.data()), length(vec.size()) {} + span(std::vector const &vec) : address(vec.data()), length(vec.size()) {} }; #endif } // namespace wit diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 699d3cb9e..77e3d024b 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -1,9 +1,9 @@ +#include "wit-common.h" #include +#include // unique_ptr #include -#include #include -#include // unique_ptr -#include "wit-common.h" +#include namespace wit { /// A string in linear memory, freed unconditionally using free @@ -51,8 +51,7 @@ class string { /// /// 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 vector { +template class vector { T *data_; size_t length; @@ -72,8 +71,8 @@ class vector { vector(T *d, size_t l) : data_(d), length(l) {} 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]; } + T &operator[](size_t n) { return data_[n]; } + T const &operator[](size_t n) const { return data_[n]; } size_t size() const { return length; } ~vector() { if (data_) { @@ -84,34 +83,40 @@ class vector { void leak() { data_ = nullptr; } // typically called by post static void drop_raw(void *ptr) { free(ptr); } - wit::span get_view() const { - return wit::span(data_, length); - } + wit::span get_view() const { return wit::span(data_, length); } }; /// @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 template class ResourceExportBase { - public: - struct Deleter { - void operator()(R* ptr) const { R::Dtor(ptr); } - }; - typedef std::unique_ptr Owned; +public: + struct Deleter { + void operator()(R *ptr) const { R::Dtor(ptr); } + }; + typedef std::unique_ptr Owned; - static const int32_t invalid = -1; + static const int32_t invalid = -1; - int32_t handle; + int32_t handle; - ResourceExportBase() : handle(R::ResourceNew((R*)this)) {} - ~ResourceExportBase() { if (handle>=0) { R::ResourceDrop(handle); } } - ResourceExportBase(ResourceExportBase const&) = delete; - ResourceExportBase(ResourceExportBase &&) = delete; - ResourceExportBase& operator=(ResourceExportBase &&b) = delete; - ResourceExportBase& operator=(ResourceExportBase const&) = delete; - int32_t get_handle() const { return handle; } - int32_t into_handle() { int32_t result = handle; handle=invalid; return result; } + ResourceExportBase() : handle(R::ResourceNew((R *)this)) {} + ~ResourceExportBase() { + if (handle >= 0) { + R::ResourceDrop(handle); + } + } + ResourceExportBase(ResourceExportBase const &) = delete; + ResourceExportBase(ResourceExportBase &&) = delete; + ResourceExportBase &operator=(ResourceExportBase &&b) = delete; + ResourceExportBase &operator=(ResourceExportBase const &) = delete; + int32_t get_handle() const { return handle; } + int32_t into_handle() { + int32_t result = handle; + handle = invalid; + return result; + } }; /// @brief A Resource imported from the host (guest side) diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index bb93ae756..96875312d 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -1,12 +1,12 @@ #pragma once +#include "wit-common.h" #include +#include // unique_ptr +#include #include #include #include -#include -#include // unique_ptr -#include "wit-common.h" #ifndef WIT_HOST_DIRECT #define WIT_HOST_WAMR @@ -18,7 +18,7 @@ namespace wit { #ifdef WIT_HOST_DIRECT -typedef uint8_t* guest_address; +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); @@ -32,8 +32,8 @@ typedef uint32_t guest_size; } // namespace wit #ifdef WIT_HOST_WAMR -#include #include +#include #endif namespace wit { @@ -68,35 +68,33 @@ class string { #if defined(WIT_HOST_DIRECT) static string from_view(std::string_view v) { - void* addr = cabi_realloc(nullptr, 0, 1, v.length()); + 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)*"); + 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_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()*/), + 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); + wasm_args); assert(wasm_ok); - assert(wasm_results[0].kind==WASM_I32); + 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); + 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()); } @@ -104,8 +102,7 @@ class string { }; /// A vector in linear memory (host-side handle) -template -class vector { +template class vector { guest_address data_; guest_size length; @@ -113,8 +110,8 @@ class vector { #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); + wasm_runtime_get_module_inst(inst), data_), + length); } #elif defined(WIT_HOST_DIRECT) std::string_view get_view() const { @@ -126,7 +123,8 @@ class vector { guest_size size() const { return length; } }; -/// Wrapper for specialized de-allocation of a returned type (calling cabi_post_*) +/// Wrapper for specialized de-allocation of a returned type (calling +/// cabi_post_*) template class guest_owned : public T { guest_address data_; #ifdef WIT_HOST_WAMR @@ -175,7 +173,7 @@ template class guest_owned : public T { #endif { } - T const& inner() const { return *static_cast(this); } + T const &inner() const { return *static_cast(this); } #ifdef WIT_HOST_WAMR // not necessary? as the only way to get a guest_owned object @@ -202,10 +200,10 @@ template class ResourceTable { resources.insert(std::pair(id, std::move(value))); return id; } - static std::optional remove_resource(int32_t id) { + static std::optional remove_resource(int32_t id) { auto iter = resources.find(id); std::optional result; - if (iter!=resources.end()) { + if (iter != resources.end()) { result = std::move(iter->second); resources.erase(iter); } @@ -215,59 +213,63 @@ template class ResourceTable { /// Guest exported resource (host side handle) class ResourceExportBase : public ResourceTable { - protected: - guest_address rep; - int32_t index; - public: - ResourceExportBase() : rep(nullptr), 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!=nullptr) { - remove_resource(index); - } +protected: + guest_address rep; + int32_t index; + +public: + ResourceExportBase() : rep(nullptr), 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 != nullptr) { + 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; } + } + 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 ResourceImportBase : public ResourceTable { - int32_t index; - public: - struct Deleter { - void operator()(R* ptr) const { R::Dtor(ptr); } - }; - typedef std::unique_ptr Owned; +template class ResourceImportBase : public ResourceTable { + int32_t index; - 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; - } +public: + struct Deleter { + void operator()(R *ptr) const { R::Dtor(ptr); } + }; + typedef std::unique_ptr 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 ResourceForwarder : public R { +template class ResourceForwarder : public R { typedef R Owned; ResourceForwarder(int32_t id) : R(ResourceExportBase(id)) {} std::optional lookup_resource(int32_t id) { @@ -275,8 +277,9 @@ class ResourceForwarder : public R { return R(ResourceExportBase(id)); } std::optional remove_resource(int32_t id) { - std::optional result = R::remove_resource(id); - if (!result.has_value()) return std::optional(); + std::optional result = R::remove_resource(id); + if (!result.has_value()) + return std::optional(); return *result; } }; From 0b3538577d271b88f80642c010a432b739fbaa9e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 6 Jun 2024 22:16:48 +0900 Subject: [PATCH 242/672] fix crash hidden by leak --- crates/cpp/tests/native_mesh/mesh/mesh_native.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp b/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp index 51af4afb0..a3d1a9af5 100644 --- a/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp +++ b/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp @@ -37,8 +37,9 @@ mesh::exports::foo::foo::resources::R::~R() { } mesh::exports::foo::foo::resources::R::R(uint32_t a) { auto ret = fooX3AfooX2FresourcesX23X5BconstructorX5Dr((int32_t(a))); - this->index = wit::ResourceExportBase{ret}.get_handle(); - this->rep = *lookup_resource(ret); + 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))); From 91e4982d885de0ed82adfeb18a4f2ef743668d7f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 8 Jun 2024 20:38:42 +0900 Subject: [PATCH 243/672] correct object destruction logic and fix wrong address still memory leak in Rust guest --- crates/cpp/tests/native_resources/guest/the_world.cpp | 4 ++-- crates/cpp/tests/native_resources/the_world_native.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/cpp/tests/native_resources/guest/the_world.cpp b/crates/cpp/tests/native_resources/guest/the_world.cpp index 8f10466ca..d57fdd97b 100644 --- a/crates/cpp/tests/native_resources/guest/the_world.cpp +++ b/crates/cpp/tests/native_resources/guest/the_world.cpp @@ -114,7 +114,7 @@ fooX3AfooX2FresourcesX23create() { return result0.release()->handle; } extern "C" __attribute__((__export_name__("foo:foo/resources#borrows"))) void -fooX3AfooX2FresourcesX23borrows(int32_t arg0) { +fooX3AfooX2FresourcesX23borrows(uint8_t* arg0) { exports::foo::foo::resources::Borrows( std::ref(*(exports::foo::foo::resources::R *)arg0)); } @@ -122,7 +122,7 @@ 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(); +// obj0->into_handle(); exports::foo::foo::resources::Consume(std::move(obj0)); } diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index 97136bbc6..6d62d39f9 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -41,8 +41,8 @@ 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::remove_resource(arg0); - assert(obj0.has_value()); + 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() { From 90082e568dbffca9078949cb4a3ad3d31029c2d2 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 8 Jun 2024 21:00:08 +0900 Subject: [PATCH 244/672] proper code fix, no leak in Rust, double free in C++ --- crates/cpp/tests/native_resources/the_world_native.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/cpp/tests/native_resources/the_world_native.cpp b/crates/cpp/tests/native_resources/the_world_native.cpp index 6d62d39f9..eb9522ded 100644 --- a/crates/cpp/tests/native_resources/the_world_native.cpp +++ b/crates/cpp/tests/native_resources/the_world_native.cpp @@ -71,7 +71,8 @@ X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(int32_t arg0) { } extern "C" void X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t arg0) { - exports::foo::foo::resources::R::remove_resource(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(); From 1fe949b9412f1224920f4f2920beb097b2688361 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 9 Jun 2024 21:52:39 +0900 Subject: [PATCH 245/672] =?UTF-8?q?fix=20double=20free=20problem=20in=20th?= =?UTF-8?q?e=20C++=20guest=20(=C3=A9xported=20resources)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/cpp/helper-types/wit-common.h | 2 +- crates/cpp/helper-types/wit-guest.h | 22 ++++++++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h index a645bef09..89f045920 100644 --- a/crates/cpp/helper-types/wit-common.h +++ b/crates/cpp/helper-types/wit-common.h @@ -2,8 +2,8 @@ #include #include -#include #include // size_t +#include #if __cplusplus > 202001L #include #else diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 77e3d024b..59e08a55a 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -90,23 +90,29 @@ template class vector { /// /// 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 ResourceExportBase { public: - struct Deleter { - void operator()(R *ptr) const { R::Dtor(ptr); } + struct Deregister { + void operator()(R *ptr) const { + // probably always true because of unique_ptr wrapping, TODO: check + if (ptr->handle >= 0) { + // we can't deallocate because the host calls Dtor + R::ResourceDrop(ptr->handle); + } + } }; - typedef std::unique_ptr Owned; + typedef std::unique_ptr Owned; static const int32_t invalid = -1; int32_t handle; ResourceExportBase() : handle(R::ResourceNew((R *)this)) {} - ~ResourceExportBase() { - if (handle >= 0) { - R::ResourceDrop(handle); - } - } + // 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; From 0faf9bf6f23fbba95331bb4af4e279ccd4cbcc87 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 10 Jun 2024 00:42:10 +0900 Subject: [PATCH 246/672] properly delete the object, only one leak remaining --- crates/cpp/tests/native_mesh/mesh/mesh_native.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp b/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp index a3d1a9af5..e399d8cd7 100644 --- a/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp +++ b/crates/cpp/tests/native_mesh/mesh/mesh_native.cpp @@ -56,7 +56,8 @@ X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_repX5Dr(int32_t arg0) { } extern "C" void X5BexportX5DfooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t arg0) { - mesh::exports::foo::foo::resources::R::remove_resource(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() { From 72e0eb295676eae2ec258cb78e995d90a9efc157 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 10 Jun 2024 00:43:56 +0900 Subject: [PATCH 247/672] here is the second part of the necessary change, no leaks anymore --- crates/cpp/tests/native_mesh/component_b/b.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/tests/native_mesh/component_b/b.cpp b/crates/cpp/tests/native_mesh/component_b/b.cpp index 6d0f7db37..7627a7cc2 100644 --- a/crates/cpp/tests/native_mesh/component_b/b.cpp +++ b/crates/cpp/tests/native_mesh/component_b/b.cpp @@ -74,7 +74,7 @@ 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(); + //obj0->into_handle(); exports::foo::foo::resources::Consume(std::move(obj0)); } From 078c06f5d975fbea1ae79906e9fa5adfdfa8c7d9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 15 Jun 2024 23:44:43 +0900 Subject: [PATCH 248/672] add wamr example --- .gitmodules | 3 + .../tests/native_strings/wamr/CMakeLists.txt | 23 ++ .../native_strings/wamr/the-world_bridge.c | 201 ++++++++++++++++++ .../tests/native_strings/wamr/wasi_dummy.c | 13 ++ crates/cpp/tests/wasm-micro-runtime | 1 + 5 files changed, 241 insertions(+) create mode 100644 crates/cpp/tests/native_strings/wamr/CMakeLists.txt create mode 100644 crates/cpp/tests/native_strings/wamr/the-world_bridge.c create mode 100644 crates/cpp/tests/native_strings/wamr/wasi_dummy.c create mode 160000 crates/cpp/tests/wasm-micro-runtime 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/crates/cpp/tests/native_strings/wamr/CMakeLists.txt b/crates/cpp/tests/native_strings/wamr/CMakeLists.txt new file mode 100644 index 000000000..87488c6f7 --- /dev/null +++ b/crates/cpp/tests/native_strings/wamr/CMakeLists.txt @@ -0,0 +1,23 @@ +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 + executor.c the-world_bridge.c wasi_dummy.c) +target_include_directories(strings PUBLIC ${CMAKE_CURRENT_LIST_DIR}) +target_link_libraries (strings vmlib) 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..a008cdcff --- /dev/null +++ b/crates/cpp/tests/native_strings/wamr/the-world_bridge.c @@ -0,0 +1,201 @@ + +#include +#include +#include +#include "executor.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/wasi_dummy.c b/crates/cpp/tests/native_strings/wamr/wasi_dummy.c new file mode 100644 index 000000000..7a7b5f75a --- /dev/null +++ b/crates/cpp/tests/native_strings/wamr/wasi_dummy.c @@ -0,0 +1,13 @@ +#include "executor.h" + +uint32_t wasi_snapshot_preview1__args_get(void*,uint32_t,uint32_t) { + abort(); +} + +uint32_t wasi_snapshot_preview1__args_sizes_get(void*,uint32_t,uint32_t) { + abort(); +} + +void wasi_snapshot_preview1__proc_exit(void*,uint32_t) { + abort(); +} 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 From a1807ed2fa1950df61d1f4c145430cf7da21ad74 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 15 Jun 2024 23:47:35 +0900 Subject: [PATCH 249/672] add more wamr parts --- crates/cpp/tests/native_strings/wamr/Makefile | 36 +++++++++++++++++++ .../tests/native_strings/wamr/libstrings.so | 1 + 2 files changed, 37 insertions(+) create mode 100644 crates/cpp/tests/native_strings/wamr/Makefile create mode 120000 crates/cpp/tests/native_strings/wamr/libstrings.so diff --git a/crates/cpp/tests/native_strings/wamr/Makefile b/crates/cpp/tests/native_strings/wamr/Makefile new file mode 100644 index 000000000..423211f7d --- /dev/null +++ b/crates/cpp/tests/native_strings/wamr/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/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 From afb1cb70a29e6ef4a308d31678d204f2da664737 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 16 Jun 2024 00:45:01 +0900 Subject: [PATCH 250/672] fully complete wamr example --- .../cpp/tests/native_strings/wamr/.gitignore | 1 + .../tests/native_strings/wamr/CMakeLists.txt | 5 +- crates/cpp/tests/native_strings/wamr/Makefile | 29 ++----- .../native_strings/wamr/the-world_bridge.c | 2 +- .../cpp/tests/native_strings/wamr/wamr_env.c | 86 +++++++++++++++++++ .../cpp/tests/native_strings/wamr/wamr_env.h | 18 ++++ .../tests/native_strings/wamr/wasi_dummy.c | 13 --- 7 files changed, 117 insertions(+), 37 deletions(-) create mode 100644 crates/cpp/tests/native_strings/wamr/.gitignore create mode 100644 crates/cpp/tests/native_strings/wamr/wamr_env.c create mode 100644 crates/cpp/tests/native_strings/wamr/wamr_env.h delete mode 100644 crates/cpp/tests/native_strings/wamr/wasi_dummy.c 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 index 87488c6f7..9437f4ba0 100644 --- a/crates/cpp/tests/native_strings/wamr/CMakeLists.txt +++ b/crates/cpp/tests/native_strings/wamr/CMakeLists.txt @@ -18,6 +18,7 @@ include (${WAMR_ROOT_DIR}/build-scripts/runtime_lib.cmake) add_library(vmlib ${WAMR_RUNTIME_LIB_SOURCE}) add_library(strings SHARED - executor.c the-world_bridge.c wasi_dummy.c) -target_include_directories(strings PUBLIC ${CMAKE_CURRENT_LIST_DIR}) + 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 index 423211f7d..59bb4273b 100644 --- a/crates/cpp/tests/native_strings/wamr/Makefile +++ b/crates/cpp/tests/native_strings/wamr/Makefile @@ -1,35 +1,22 @@ -CXXFLAGS=-g -O0 -I../../../helper-types WIT_BINDGEN=../../../../../target/debug/wit-bindgen -W2C2_PATH=$(HOME)/github/w2c2 -all: libstrings.so +all: libstrings.so guest_release.wasm -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 +libstrings.so: + mkdir build + (cd build; cmake .. ; make) guest_release.wasm: ../the_world.cpp ../guest.cpp - /opt/wasi-sdk/bin/clang++ -o $@ $^ $(CXXFLAGS) -g0 -O3 + /opt/wasi-sdk/bin/clang++ -o $@ $^ -I../../../helper-types -g0 -O3 clean: - -rm *.o libstrings.so + -rm -r build guest_release.wasm 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 $^ $@ +# valgrind: +# LD_LIBRARY_PATH=. valgrind ../app-strings # not yet up to the task #the-world_bridge.c: $(WIT_BINDGEN) diff --git a/crates/cpp/tests/native_strings/wamr/the-world_bridge.c b/crates/cpp/tests/native_strings/wamr/the-world_bridge.c index a008cdcff..c6fa3a13c 100644 --- a/crates/cpp/tests/native_strings/wamr/the-world_bridge.c +++ b/crates/cpp/tests/native_strings/wamr/the-world_bridge.c @@ -2,7 +2,7 @@ #include #include #include -#include "executor.h" +#include "wamr_env.h" #define nullptr 0 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/wamr/wasi_dummy.c b/crates/cpp/tests/native_strings/wamr/wasi_dummy.c deleted file mode 100644 index 7a7b5f75a..000000000 --- a/crates/cpp/tests/native_strings/wamr/wasi_dummy.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "executor.h" - -uint32_t wasi_snapshot_preview1__args_get(void*,uint32_t,uint32_t) { - abort(); -} - -uint32_t wasi_snapshot_preview1__args_sizes_get(void*,uint32_t,uint32_t) { - abort(); -} - -void wasi_snapshot_preview1__proc_exit(void*,uint32_t) { - abort(); -} From 6e96c8491c29436f938511a255d813fbd1050e46 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 16 Jun 2024 00:48:56 +0900 Subject: [PATCH 251/672] Update readme --- crates/cpp/tests/README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/cpp/tests/README.md b/crates/cpp/tests/README.md index 7a23de197..5b93d9cf8 100644 --- a/crates/cpp/tests/README.md +++ b/crates/cpp/tests/README.md @@ -4,9 +4,17 @@ 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. +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. From a5b54effca5ed8698c7716a16ec7efc8e7162886 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 20 Jun 2024 21:53:16 +0200 Subject: [PATCH 252/672] smallvec hash correction --- Cargo.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3e98114a7..e3f88abe0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1354,7 +1354,7 @@ checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" dependencies = [ "serde", ] @@ -2409,7 +2409,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.209.0", + "wasm-encoder 0.211.0", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", From 254ee77bbdd21a9d45c89970097c432d27fd760f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 22 Jun 2024 10:45:12 +0200 Subject: [PATCH 253/672] adapt to new interface signature --- crates/bridge/src/lib.rs | 3 ++- crates/cpp/src/lib.rs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/bridge/src/lib.rs b/crates/bridge/src/lib.rs index 50f1e2b6c..8f39fc20b 100644 --- a/crates/bridge/src/lib.rs +++ b/crates/bridge/src/lib.rs @@ -78,7 +78,7 @@ impl WorldGenerator for Bridge { 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(), @@ -89,6 +89,7 @@ impl WorldGenerator for Bridge { for (_name, func) in resolve.interfaces[iface].functions.iter() { gen.generate_function(func, &TypeOwner::Interface(iface), AbiVariant::GuestImport); } + Ok(()) } fn export_interface( diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 1f61c9125..6fb8d316b 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -476,7 +476,7 @@ impl WorldGenerator for Cpp { name: &WorldKey, id: InterfaceId, _files: &mut Files, - ) { + ) -> anyhow::Result<()> { let store = self.start_new_file(None); self.imported_interfaces.insert(id); let wasm_import_module = resolve.name_world_key(name); @@ -493,6 +493,7 @@ impl WorldGenerator for Cpp { } } self.finish_file(&namespace, store); + Ok(()) } fn export_interface( From c3c6d93b3d93957e7525201ae5b8f45aef91e14f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 23 Jun 2024 22:09:54 +0200 Subject: [PATCH 254/672] first example of meshless resource usage --- crates/cpp/helper-types/wit-common.h | 28 ++++++ crates/cpp/helper-types/wit-host.h | 28 ------ crates/cpp/tests/meshless_resources/Makefile | 13 +++ .../meshless_resources/component_a/Makefile | 12 +++ .../meshless_resources/component_a/a.cpp | 1 + .../meshless_resources/component_a/a_cpp.h | 1 + .../meshless_resources/component_a/main.cpp | 1 + .../meshless_resources/component_b/Makefile | 12 +++ .../meshless_resources/component_b/b.cpp | 87 +++++++++++++++++++ .../meshless_resources/component_b/b_cpp.h | 1 + .../component_b/exports-foo-foo-resources-R.h | 1 + .../meshless_resources/component_b/impl.cpp | 1 + .../wit/resources_simple.wit | 1 + 13 files changed, 159 insertions(+), 28 deletions(-) create mode 100644 crates/cpp/tests/meshless_resources/Makefile create mode 100644 crates/cpp/tests/meshless_resources/component_a/Makefile create mode 120000 crates/cpp/tests/meshless_resources/component_a/a.cpp create mode 120000 crates/cpp/tests/meshless_resources/component_a/a_cpp.h create mode 120000 crates/cpp/tests/meshless_resources/component_a/main.cpp create mode 100644 crates/cpp/tests/meshless_resources/component_b/Makefile create mode 100644 crates/cpp/tests/meshless_resources/component_b/b.cpp create mode 120000 crates/cpp/tests/meshless_resources/component_b/b_cpp.h create mode 120000 crates/cpp/tests/meshless_resources/component_b/exports-foo-foo-resources-R.h create mode 120000 crates/cpp/tests/meshless_resources/component_b/impl.cpp create mode 120000 crates/cpp/tests/meshless_resources/wit/resources_simple.wit diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h index 89f045920..d0a57d0c5 100644 --- a/crates/cpp/helper-types/wit-common.h +++ b/crates/cpp/helper-types/wit-common.h @@ -2,6 +2,7 @@ #include #include +#include #include // size_t #include #if __cplusplus > 202001L @@ -33,4 +34,31 @@ template class span { span(std::vector 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 ResourceTable { + static std::map 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(id, std::move(value))); + return id; + } + static std::optional remove_resource(int32_t id) { + auto iter = resources.find(id); + std::optional result; + if (iter != resources.end()) { + result = std::move(iter->second); + resources.erase(iter); + } + return std::move(result); + } +}; } // namespace wit diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 96875312d..8049ba582 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -3,7 +3,6 @@ #include "wit-common.h" #include #include // unique_ptr -#include #include #include #include @@ -184,33 +183,6 @@ template class guest_owned : public T { #endif }; -/// @brief Helper class to map between IDs and resources -/// @tparam R Type of the Resource -template class ResourceTable { - static std::map 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(id, std::move(value))); - return id; - } - static std::optional remove_resource(int32_t id) { - auto iter = resources.find(id); - std::optional result; - if (iter != resources.end()) { - result = std::move(iter->second); - resources.erase(iter); - } - return std::move(result); - } -}; - /// Guest exported resource (host side handle) class ResourceExportBase : public ResourceTable { protected: 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..ae570a356 --- /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 --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..34b4630f7 --- /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 --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..87a596408 --- /dev/null +++ b/crates/cpp/tests/meshless_resources/component_b/b.cpp @@ -0,0 +1,87 @@ +// 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 // 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; +} + +//static wit::ResourceTable r_table; +template std::map wit::ResourceTable::resources; + +// 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" + int32_t + fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t arg0) { + auto result0 = exports::foo::foo::resources::R::New((uint32_t(arg0))); + return result0.release()->handle; +} +extern "C" + void + fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(int32_t arg0, int32_t arg1) { + exports::foo::foo::resources::R::ResourceRep(arg0) + ->Add((uint32_t(arg1))); +} +int32_t exports::foo::foo::resources::R::ResourceNew(R *self) { + return wit::ResourceTable::store_resource(std::move(self)); +} +exports::foo::foo::resources::R * +exports::foo::foo::resources::R::ResourceRep(int32_t id) { + return *wit::ResourceTable::lookup_resource(id); +} +void exports::foo::foo::resources::R::ResourceDrop(int32_t id) { + auto obj = wit::ResourceTable::remove_resource(id); + assert(obj.has_value()); + exports::foo::foo::resources::R::Dtor(*obj); +} +extern "C" int32_t +fooX3AfooX2FresourcesX00create() { + auto result0 = exports::foo::foo::resources::Create(); + return result0.release()->handle; +} +extern "C" void +fooX3AfooX2FresourcesX00consume(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)); +} + +extern "C" void +fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t arg0) { + exports::foo::foo::resources::R::ResourceDrop(arg0); +} +// 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 120000 index 000000000..6a787422e --- /dev/null +++ b/crates/cpp/tests/meshless_resources/component_b/b_cpp.h @@ -0,0 +1 @@ +../../native_mesh/component_b/b_cpp.h \ No newline at end of file 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 120000 index 000000000..de6072793 --- /dev/null +++ b/crates/cpp/tests/meshless_resources/component_b/exports-foo-foo-resources-R.h @@ -0,0 +1 @@ +../../native_mesh/component_b/exports-foo-foo-resources-R.h \ No newline at end of file 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/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 From d0ede2bc1c8c69d7dd56d41c41b9698dbaee969c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 23 Jun 2024 22:11:19 +0200 Subject: [PATCH 255/672] explain meshless folder --- crates/cpp/tests/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/cpp/tests/README.md b/crates/cpp/tests/README.md index 5b93d9cf8..e30819224 100644 --- a/crates/cpp/tests/README.md +++ b/crates/cpp/tests/README.md @@ -18,3 +18,6 @@ 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` folder experiments with directly linking +two components in a shared everything environment. From be3ac119041bcd4d500c073dc36beb7aa5ee518a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 23 Jun 2024 23:39:26 +0200 Subject: [PATCH 256/672] initial version of meshless strings, still wrong handling of malloc/free --- crates/cpp/helper-types/wit-guest.h | 4 + crates/cpp/tests/meshless_strings/Makefile | 1 + .../meshless_strings/component_a/Makefile | 12 ++ .../meshless_strings/component_a/main.cpp | 30 +++++ .../component_a/the_world.cpp | 123 ++++++++++++++++++ .../component_a/the_world_cpp.h | 33 +++++ .../meshless_strings/component_b/Makefile | 12 ++ .../meshless_strings/component_b/guest.cpp | 1 + .../component_b/the_world.cpp | 123 ++++++++++++++++++ .../component_b/the_world_cpp.h | 1 + 10 files changed, 340 insertions(+) create mode 120000 crates/cpp/tests/meshless_strings/Makefile create mode 100644 crates/cpp/tests/meshless_strings/component_a/Makefile create mode 100644 crates/cpp/tests/meshless_strings/component_a/main.cpp create mode 100644 crates/cpp/tests/meshless_strings/component_a/the_world.cpp create mode 100644 crates/cpp/tests/meshless_strings/component_a/the_world_cpp.h create mode 100644 crates/cpp/tests/meshless_strings/component_b/Makefile create mode 120000 crates/cpp/tests/meshless_strings/component_b/guest.cpp create mode 100644 crates/cpp/tests/meshless_strings/component_b/the_world.cpp create mode 120000 crates/cpp/tests/meshless_strings/component_b/the_world_cpp.h diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 59e08a55a..fa80e86c3 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -45,6 +45,10 @@ class string { std::string to_string() const { return std::string((const char *)data_, length); } + static string from_view(std::string_view v) { + char* addr = (char*)malloc(v.size()); + return string(addr, v.size()); + } }; /// A vector in linear memory, freed unconditionally using free 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..054e3ca37 --- /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 -w a --wasm64 --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..50081b063 --- /dev/null +++ b/crates/cpp/tests/meshless_strings/component_a/main.cpp @@ -0,0 +1,30 @@ + +#include "the_world_cpp.h" +#include + +void comp_a::exports::foo::foo::strings::A(wit::string &&x) { + std::cout << x.get_view() << 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(wit::string &&a, wit::string &&b) { + std::cout << a.get_view() << '|' << b.get_view() << 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..9bba403ff --- /dev/null +++ b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp @@ -0,0 +1,123 @@ +// 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 // 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() { + size_t ret_area[2]; + uint8_t *ptr0 = (uint8_t *)(&ret_area); + fooX3AfooX2FstringsX00b(ptr0); + auto len1 = *((size_t *)(ptr0 + sizeof(size_t))); + + 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()); + size_t ret_area[2]; + uint8_t *ptr2 = (uint8_t *)(&ret_area); + fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2); + auto len3 = *((size_t *)(ptr2 + sizeof(size_t))); + + 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; + + exports::foo::foo::strings::A(wit::string((char const *)(arg0), len0)); +} +extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) uint8_t * +a_fooX3AfooX2FstringsX00b() { + auto result0 = exports::foo::foo::strings::B(); + static size_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 + sizeof(size_t))) = len2; + *((uint8_t **)(ptr1 + 0)) = ptr2; + return ptr1; +} +extern "C" + __attribute__((__weak__, + __export_name__("cabi_post_fooX3AfooX2FstringsX23b"))) void + a_cabi_post_fooX3AfooX2FstringsX00b(uint8_t *arg0) { + if ((*((size_t *)(arg0 + sizeof(size_t)))) > 0) { + wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); + } +} +extern "C" __attribute__((__export_name__("foo:foo/strings#c"))) uint8_t * +a_fooX3AfooX2FstringsX00c(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 size_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 + sizeof(size_t))) = len4; + *((uint8_t **)(ptr3 + 0)) = ptr4; + return ptr3; +} +extern "C" + __attribute__((__weak__, + __export_name__("cabi_post_fooX3AfooX2FstringsX23c"))) void + a_cabi_post_fooX3AfooX2FstringsX00c(uint8_t *arg0) { + if ((*((size_t *)(arg0 + sizeof(size_t)))) > 0) { + wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); + } +} + +// 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..aa065e85d --- /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 +#include +#include +#include +#include +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(wit::string &&x); +wit::string B(); +wit::string C(wit::string &&a, wit::string &&b); +} // namespace strings +} // namespace foo +} // namespace foo +} // namespace exports +} +using namespace comp_a; +//using namespace A::foo; +#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..13439bc8e --- /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 -w b --wasm64 --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 120000 index 000000000..f9b4d9b20 --- /dev/null +++ b/crates/cpp/tests/meshless_strings/component_b/guest.cpp @@ -0,0 +1 @@ +../../native_strings/guest.cpp \ No newline at end of file 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..aadd5d775 --- /dev/null +++ b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp @@ -0,0 +1,123 @@ +// 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 // 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() { + size_t ret_area[2]; + uint8_t *ptr0 = (uint8_t *)(&ret_area); + a_fooX3AfooX2FstringsX00b(ptr0); + auto len1 = *((size_t *)(ptr0 + sizeof(size_t))); + + 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()); + size_t ret_area[2]; + uint8_t *ptr2 = (uint8_t *)(&ret_area); + a_fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2); + auto len3 = *((size_t *)(ptr2 + sizeof(size_t))); + + 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(wit::string((char const *)(arg0), len0)); +} +extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) uint8_t * +fooX3AfooX2FstringsX00b() { + auto result0 = exports::foo::foo::strings::B(); + static size_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 + sizeof(size_t))) = len2; + *((uint8_t **)(ptr1 + 0)) = ptr2; + return ptr1; +} +extern "C" + __attribute__((__weak__, + __export_name__("cabi_post_fooX3AfooX2FstringsX23b"))) void + cabi_post_fooX3AfooX2FstringsX00b(uint8_t *arg0) { + if ((*((size_t *)(arg0 + sizeof(size_t)))) > 0) { + wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); + } +} +extern "C" __attribute__((__export_name__("foo:foo/strings#c"))) uint8_t * +fooX3AfooX2FstringsX00c(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 size_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 + sizeof(size_t))) = len4; + *((uint8_t **)(ptr3 + 0)) = ptr4; + return ptr3; +} +extern "C" + __attribute__((__weak__, + __export_name__("cabi_post_fooX3AfooX2FstringsX23c"))) void + cabi_post_fooX3AfooX2FstringsX00c(uint8_t *arg0) { + if ((*((size_t *)(arg0 + sizeof(size_t)))) > 0) { + wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); + } +} + +// 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 120000 index 000000000..b8347c33c --- /dev/null +++ b/crates/cpp/tests/meshless_strings/component_b/the_world_cpp.h @@ -0,0 +1 @@ +../../native_strings/the_world_cpp.h \ No newline at end of file From 76bafd92e244bd145a517f345cc1ad6108a1280f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 25 Jun 2024 23:13:43 +0200 Subject: [PATCH 257/672] fully working meshless strings --- crates/cpp/helper-types/wit-guest.h | 2 + .../meshless_strings/component_a/main.cpp | 34 +++++++------- .../component_a/the_world.cpp | 44 ++++++++++--------- .../component_a/the_world_cpp.h | 4 +- .../component_b/the_world.cpp | 39 ++++++++-------- crates/cpp/tests/meshless_strings/wit | 1 + 6 files changed, 68 insertions(+), 56 deletions(-) create mode 120000 crates/cpp/tests/meshless_strings/wit diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index fa80e86c3..ba5fc0862 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -4,6 +4,7 @@ #include #include #include +#include // memcpy namespace wit { /// A string in linear memory, freed unconditionally using free @@ -47,6 +48,7 @@ class string { } static string from_view(std::string_view v) { char* addr = (char*)malloc(v.size()); + memcpy(addr, v.data(), v.size()); return string(addr, v.size()); } }; diff --git a/crates/cpp/tests/meshless_strings/component_a/main.cpp b/crates/cpp/tests/meshless_strings/component_a/main.cpp index 50081b063..19d56bd26 100644 --- a/crates/cpp/tests/meshless_strings/component_a/main.cpp +++ b/crates/cpp/tests/meshless_strings/component_a/main.cpp @@ -3,28 +3,30 @@ #include void comp_a::exports::foo::foo::strings::A(wit::string &&x) { - std::cout << x.get_view() << std::endl; + std::cout << x.get_view() << 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 b = wit::string::from_view(std::string_view("hello B")); + return b; } -wit::string comp_a::exports::foo::foo::strings::C(wit::string &&a, wit::string &&b) { - std::cout << a.get_view() << '|' << b.get_view() << std::endl; - wit::string c = wit::string::from_view(std::string_view("hello C")); - return c; +wit::string comp_a::exports::foo::foo::strings::C(wit::string &&a, + wit::string &&b) { + std::cout << a.get_view() << '|' << b.get_view() << 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")); + 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 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; + 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 index 9bba403ff..840b8e7a6 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp @@ -29,11 +29,13 @@ 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 *); +__attribute__((import_name("b"))) uint8_t * +fooX3AfooX2FstringsX00b(); extern "C" __attribute__((import_module("foo:foo/strings"))) -__attribute__((import_name("c"))) void -fooX3AfooX2FstringsX00c(uint8_t *, size_t, uint8_t *, size_t, uint8_t *); +__attribute__((import_name("c"))) uint8_t * +fooX3AfooX2FstringsX00c(uint8_t *, size_t, uint8_t *, size_t); +extern "C" void cabi_post_fooX3AfooX2FstringsX00b(uint8_t *); +extern "C" void cabi_post_fooX3AfooX2FstringsX00c(uint8_t *); void comp_a::foo::foo::strings::A(std::string_view x) { auto const &vec0 = x; auto ptr0 = (uint8_t *)(vec0.data()); @@ -41,32 +43,34 @@ void comp_a::foo::foo::strings::A(std::string_view x) { fooX3AfooX2FstringsX00a(ptr0, len0); } wit::string comp_a::foo::foo::strings::B() { - size_t ret_area[2]; - uint8_t *ptr0 = (uint8_t *)(&ret_area); - fooX3AfooX2FstringsX00b(ptr0); + uint8_t *ptr0 = fooX3AfooX2FstringsX00b(); auto len1 = *((size_t *)(ptr0 + sizeof(size_t))); - - return wit::string((char const *)(*((uint8_t **)(ptr0 + 0))), len1); + auto res = wit::string::from_view( + std::string_view((char const *)(*((uint8_t **)(ptr0 + 0))), len1)); + cabi_post_fooX3AfooX2FstringsX00b(ptr0); + return res; } -wit::string comp_a::foo::foo::strings::C(std::string_view a, std::string_view b) { +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()); - size_t ret_area[2]; - uint8_t *ptr2 = (uint8_t *)(&ret_area); - fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2); + uint8_t *ptr2 = fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1); auto len3 = *((size_t *)(ptr2 + sizeof(size_t))); - - return wit::string((char const *)(*((uint8_t **)(ptr2 + 0))), len3); + auto res = wit::string::from_view( + std::string_view((char const *)(*((uint8_t **)(ptr2 + 0))), len3)); + cabi_post_fooX3AfooX2FstringsX00c(ptr2); + return res; } extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void a_fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { auto len0 = arg1; - exports::foo::foo::strings::A(wit::string((char const *)(arg0), len0)); + exports::foo::foo::strings::A( + wit::string::from_view(std::string_view((char const *)(arg0), len0))); } extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) uint8_t * a_fooX3AfooX2FstringsX00b() { @@ -92,14 +96,14 @@ extern "C" } extern "C" __attribute__((__export_name__("foo:foo/strings#c"))) uint8_t * a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, - size_t arg3) { + 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)); + auto result2 = exports::foo::foo::strings::C( + wit::string::from_view(std::string_view((char const *)(arg0), len0)), + wit::string::from_view(std::string_view((char const *)(arg2), len1))); static size_t ret_area[2]; uint8_t *ptr3 = (uint8_t *)(&ret_area); auto const &vec4 = result2; 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 index aa065e85d..32ef96a2e 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world_cpp.h +++ b/crates/cpp/tests/meshless_strings/component_a/the_world_cpp.h @@ -27,7 +27,7 @@ wit::string C(wit::string &&a, wit::string &&b); } // namespace foo } // namespace foo } // namespace exports -} +} // namespace comp_a using namespace comp_a; -//using namespace A::foo; +// using namespace A::foo; #endif diff --git a/crates/cpp/tests/meshless_strings/component_b/the_world.cpp b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp index aadd5d775..4e4b76ccd 100644 --- a/crates/cpp/tests/meshless_strings/component_b/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp @@ -29,11 +29,13 @@ 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 *); +__attribute__((import_name("b"))) uint8_t * +a_fooX3AfooX2FstringsX00b(); 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 *); +__attribute__((import_name("c"))) uint8_t * +a_fooX3AfooX2FstringsX00c(uint8_t *, size_t, uint8_t *, size_t); +extern "C" void a_cabi_post_fooX3AfooX2FstringsX00b(uint8_t *); +extern "C" void a_cabi_post_fooX3AfooX2FstringsX00c(uint8_t *); void foo::foo::strings::A(std::string_view x) { auto const &vec0 = x; auto ptr0 = (uint8_t *)(vec0.data()); @@ -41,12 +43,12 @@ void foo::foo::strings::A(std::string_view x) { a_fooX3AfooX2FstringsX00a(ptr0, len0); } wit::string foo::foo::strings::B() { - size_t ret_area[2]; - uint8_t *ptr0 = (uint8_t *)(&ret_area); - a_fooX3AfooX2FstringsX00b(ptr0); + uint8_t *ptr0 = a_fooX3AfooX2FstringsX00b(); auto len1 = *((size_t *)(ptr0 + sizeof(size_t))); - - return wit::string((char const *)(*((uint8_t **)(ptr0 + 0))), len1); + auto res = wit::string::from_view( + std::string_view((char const *)(*((uint8_t **)(ptr0 + 0))), len1)); + a_cabi_post_fooX3AfooX2FstringsX00b(ptr0); + return res; } wit::string foo::foo::strings::C(std::string_view a, std::string_view b) { auto const &vec0 = a; @@ -55,18 +57,19 @@ wit::string foo::foo::strings::C(std::string_view a, std::string_view b) { auto const &vec1 = b; auto ptr1 = (uint8_t *)(vec1.data()); auto len1 = (size_t)(vec1.size()); - size_t ret_area[2]; - uint8_t *ptr2 = (uint8_t *)(&ret_area); - a_fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2); + uint8_t *ptr2 = a_fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1); auto len3 = *((size_t *)(ptr2 + sizeof(size_t))); - - return wit::string((char const *)(*((uint8_t **)(ptr2 + 0))), len3); + auto res = wit::string::from_view( + std::string_view((char const *)(*((uint8_t **)(ptr2 + 0))), len3)); + a_cabi_post_fooX3AfooX2FstringsX00c(ptr2); + return res; } 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(wit::string((char const *)(arg0), len0)); + exports::foo::foo::strings::A( + wit::string::from_view(std::string_view((char const *)(arg0), len0))); } extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) uint8_t * fooX3AfooX2FstringsX00b() { @@ -97,9 +100,9 @@ fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, auto len1 = arg3; - auto result2 = - exports::foo::foo::strings::C(wit::string((char const *)(arg0), len0), - wit::string((char const *)(arg2), len1)); + auto result2 = exports::foo::foo::strings::C( + wit::string::from_view(std::string_view((char const *)(arg0), len0)), + wit::string::from_view(std::string_view((char const *)(arg2), len1))); static size_t ret_area[2]; uint8_t *ptr3 = (uint8_t *)(&ret_area); auto const &vec4 = result2; 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 From 79511433644fc3e428e4e5619dab1da160cf6fb6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 25 Jun 2024 23:15:02 +0200 Subject: [PATCH 258/672] readme update --- crates/cpp/tests/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/cpp/tests/README.md b/crates/cpp/tests/README.md index e30819224..27ef59264 100644 --- a/crates/cpp/tests/README.md +++ b/crates/cpp/tests/README.md @@ -1,14 +1,14 @@ -This folder contains examples on how to use the caninical ABI without +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 +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 +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. @@ -19,5 +19,5 @@ 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` folder experiments with directly linking -two components in a shared everything environment. +The `meshless_resources` and `meshless_strings` folders experiment +with directly linking two components in a shared everything environment. From 143ac3ce68b82089d4ee4bfd5f4f7608203e9fe5 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 2 Jul 2024 23:44:49 +0200 Subject: [PATCH 259/672] meshless: easy part with imported resources in Rust --- .../meshless_resources/rust_comp_a/.gitignore | 1 + .../meshless_resources/rust_comp_a/Cargo.lock | 7 + .../meshless_resources/rust_comp_a/Cargo.toml | 8 + .../meshless_resources/rust_comp_a/Makefile | 3 + .../meshless_resources/rust_comp_a/build.rs | 8 + .../meshless_resources/rust_comp_a/src/a.rs | 304 ++++++++++++++++++ .../rust_comp_a/src/main.rs | 12 + 7 files changed, 343 insertions(+) create mode 100644 crates/cpp/tests/meshless_resources/rust_comp_a/.gitignore create mode 100644 crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.lock create mode 100644 crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.toml create mode 100644 crates/cpp/tests/meshless_resources/rust_comp_a/Makefile create mode 100644 crates/cpp/tests/meshless_resources/rust_comp_a/build.rs create mode 100644 crates/cpp/tests/meshless_resources/rust_comp_a/src/a.rs create mode 100644 crates/cpp/tests/meshless_resources/rust_comp_a/src/main.rs 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..b4bb8f1be --- /dev/null +++ b/crates/cpp/tests/meshless_resources/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_resources/rust_comp_a/Cargo.toml b/crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.toml new file mode 100644 index 000000000..dd669d8a6 --- /dev/null +++ b/crates/cpp/tests/meshless_resources/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_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..e1076e6bf --- /dev/null +++ b/crates/cpp/tests/meshless_resources/rust_comp_a/build.rs @@ -0,0 +1,8 @@ +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_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..cc95ffc35 --- /dev/null +++ b/crates/cpp/tests/meshless_resources/rust_comp_a/src/a.rs @@ -0,0 +1,304 @@ +// Generated by `wit-bindgen` 0.27.0. DO NOT EDIT! +// Options used: +// * with "foo:foo/resources" = generate +#[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, + } + + 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)] + /// borrows: func(o: borrow); + 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); + } + } + + } + + } +} +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 { + // 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, + } + + /// 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: u32); + } + + impl Resource { + #[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` 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` 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) -> u32 { + resource.handle.swap(u32::MAX, Relaxed) + } + + #[doc(hidden)] + pub fn handle(resource: &Resource) -> u32 { + resource.handle.load(Relaxed) + } + } + + impl fmt::Debug for Resource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Resource") + .field("handle", &self.handle) + .finish() + } + } + + impl Drop for Resource { + 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: 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.27.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.212.0\x10wit-bindgen-rust\x060.27.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); +} From 58e67f6e7f3a739f2b752ccbf2a92cbb801f2346 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 2 Jul 2024 23:45:14 +0200 Subject: [PATCH 260/672] update --- Cargo.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 55094d170..e83f0db7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2410,7 +2410,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.211.0", + "wasm-encoder 0.212.0", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", From 845b2844d54ec355a8840152f7f6af4174b6c417 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 3 Jul 2024 00:38:03 +0200 Subject: [PATCH 261/672] meshless/symmetric strings with Rust --- .../meshless_strings/rust_comp_a/.gitignore | 1 + .../meshless_strings/rust_comp_a/Cargo.lock | 7 + .../meshless_strings/rust_comp_a/Cargo.toml | 8 + .../meshless_strings/rust_comp_a/build.rs | 8 + .../meshless_strings/rust_comp_a/src/main.rs | 33 +++ .../rust_comp_a/src/the_world.rs | 273 ++++++++++++++++++ 6 files changed, 330 insertions(+) create mode 100644 crates/cpp/tests/meshless_strings/rust_comp_a/.gitignore create mode 100644 crates/cpp/tests/meshless_strings/rust_comp_a/Cargo.lock create mode 100644 crates/cpp/tests/meshless_strings/rust_comp_a/Cargo.toml create mode 100644 crates/cpp/tests/meshless_strings/rust_comp_a/build.rs create mode 100644 crates/cpp/tests/meshless_strings/rust_comp_a/src/main.rs create mode 100644 crates/cpp/tests/meshless_strings/rust_comp_a/src/the_world.rs 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..e1076e6bf --- /dev/null +++ b/crates/cpp/tests/meshless_strings/rust_comp_a/build.rs @@ -0,0 +1,8 @@ +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..5dbc31c74 --- /dev/null +++ b/crates/cpp/tests/meshless_strings/rust_comp_a/src/main.rs @@ -0,0 +1,33 @@ +use the_world::foo::foo::strings; +use the_world::exports::foo::foo::strings::Guest; + +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..d9aba158a --- /dev/null +++ b/crates/cpp/tests/meshless_strings/rust_comp_a/src/the_world.rs @@ -0,0 +1,273 @@ +// Generated by `wit-bindgen` 0.27.0. DO NOT EDIT! +// Options used: +// * with "foo:foo/strings" = generate +#[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::(); + 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 { + #[link(wasm_import_module = "foo:foo/strings")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "b")] + fn fooX3AfooX2FstringsX00b() -> *mut u8; + #[cfg_attr(target_arch = "wasm32", link_name = "cabi_post_b")] + fn cabi_post_fooX3AfooX2FstringsX00b(_: *mut u8); + } + let ptr0 = fooX3AfooX2FstringsX00b(); + let l1 = *ptr0.add(0).cast::<*mut u8>(); + let l2 = *ptr0.add(8).cast::(); + let len3 = l2; + // let bytes3 = _rt::Vec::from_raw_parts(l1.cast(), len3, len3); + // _rt::string_lift(bytes3) + let res = String::from(std::str::from_utf8(std::slice::from_raw_parts(l1, len3)).unwrap()); + cabi_post_fooX3AfooX2FstringsX00b(ptr0); + res + } + } + #[allow(unused_unsafe, clippy::all)] + pub fn c(a: &str,b: &str,) -> _rt::String{ + unsafe { + // #[repr(align(4))] + // struct RetArea([::core::mem::MaybeUninit::; 8]); + // let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + let vec0 = a; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + let vec1 = b; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + // let ptr2 = ret_area.0.as_mut_ptr().cast::(); + #[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; + #[cfg_attr(target_arch = "wasm32", link_name = "cabi_post_c")] + fn cabi_post_fooX3AfooX2FstringsX00c(_: *mut u8); + } + let ptr2 = fooX3AfooX2FstringsX00c(ptr0.cast_mut(), len0, ptr1.cast_mut(), len1); + let l3 = *ptr2.add(0).cast::<*mut u8>(); + let l4 = *ptr2.add(8).cast::(); + let len5 = l4; + let res = String::from(std::str::from_utf8(std::slice::from_raw_parts(l3, len5)).unwrap()); + // let bytes5 = _rt::Vec::from_raw_parts(l3.cast(), len5, len5); + // _rt::string_lift(bytes5) + cabi_post_fooX3AfooX2FstringsX00c(ptr2); + res + } + } + + } + + } +} +#[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(arg0: *mut u8,arg1: usize,) {#[cfg(target_arch="wasm32")] + _rt::run_ctors_once();let len0 = arg1; + let s = String::from(std::str::from_utf8(std::slice::from_raw_parts(arg0, len0)).unwrap()); + // let bytes0 = _rt::Vec::from_raw_parts(arg0.cast(), len0, len0); + T::a(s); + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_b_cabi() -> *mut u8 {#[cfg(target_arch="wasm32")] + _rt::run_ctors_once();let result0 = T::b(); + let ptr1 = _RET_AREA.0.as_mut_ptr().cast::(); + let vec2 = (result0.into_bytes()).into_boxed_slice(); + let ptr2 = vec2.as_ptr().cast::(); + let len2 = vec2.len(); + ::core::mem::forget(vec2); + *ptr1.add(8).cast::() = len2; + *ptr1.add(0).cast::<*mut u8>() = ptr2.cast_mut(); + ptr1 + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __post_return_b(arg0: *mut u8,) { + let l0 = *arg0.add(0).cast::<*mut u8>(); + let l1 = *arg0.add(8).cast::(); + _rt::cabi_dealloc(l0, l1, 1); + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_c_cabi(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 = String::from(std::str::from_utf8(std::slice::from_raw_parts(arg0, len0)).unwrap()); + //_rt::Vec::from_raw_parts(arg0.cast(), len0, len0); + let len1 = arg3; + let bytes1 = String::from(std::str::from_utf8(std::slice::from_raw_parts(arg2, len1)).unwrap()); + let result2 = T::c(bytes0, bytes1); + let ptr3 = _RET_AREA.0.as_mut_ptr().cast::(); + let vec4 = (result2.into_bytes()).into_boxed_slice(); + let ptr4 = vec4.as_ptr().cast::(); + let len4 = vec4.len(); + ::core::mem::forget(vec4); + *ptr3.add(8).cast::() = len4; + *ptr3.add(0).cast::<*mut u8>() = ptr4.cast_mut(); + ptr3 + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __post_return_c(arg0: *mut u8,) { + let l0 = *arg0.add(0).cast::<*mut u8>(); + let l1 = *arg0.add(8).cast::(); + _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 a_fooX3AfooX2FstringsX00a(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 a_fooX3AfooX2FstringsX00b() -> *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 a_cabi_post_fooX3AfooX2FstringsX00b(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 a_fooX3AfooX2FstringsX00c(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 a_cabi_post_fooX3AfooX2FstringsX00c(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::; 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) -> 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 as *mut u8, 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.27.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.212.0\x10wit-bindgen-rust\x060.27.0"; + +#[inline(never)] +#[doc(hidden)] +#[cfg(target_arch = "wasm32")] +pub fn __link_custom_section_describing_imports() { + wit_bindgen::rt::maybe_link_cabi_realloc(); +} + From 397b8ed863da969212aebf6596a3b682b10b38cf Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 6 Jul 2024 14:06:32 +0200 Subject: [PATCH 262/672] initial prototype for exported resource in Rust --- .../meshless_resources/rust_comp_a/Cargo.lock | 7 + .../meshless_resources/rust_comp_a/Cargo.toml | 1 + .../meshless_resources/rust_comp_a/build.rs | 7 +- .../meshless_resources/rust_comp_b/.gitignore | 1 + .../meshless_resources/rust_comp_b/Cargo.lock | 7 + .../meshless_resources/rust_comp_b/Cargo.toml | 13 + .../meshless_resources/rust_comp_b/Makefile | 3 + .../meshless_resources/rust_comp_b/src/b.rs | 416 ++++++++++++++++++ .../meshless_resources/rust_comp_b/src/lib.rs | 37 ++ 9 files changed, 488 insertions(+), 4 deletions(-) create mode 100644 crates/cpp/tests/meshless_resources/rust_comp_b/.gitignore create mode 100644 crates/cpp/tests/meshless_resources/rust_comp_b/Cargo.lock create mode 100644 crates/cpp/tests/meshless_resources/rust_comp_b/Cargo.toml create mode 100644 crates/cpp/tests/meshless_resources/rust_comp_b/Makefile create mode 100644 crates/cpp/tests/meshless_resources/rust_comp_b/src/b.rs create mode 100644 crates/cpp/tests/meshless_resources/rust_comp_b/src/lib.rs diff --git a/crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.lock b/crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.lock index b4bb8f1be..2578eb661 100644 --- a/crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.lock +++ b/crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.lock @@ -5,3 +5,10 @@ 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 index dd669d8a6..c36e84d15 100644 --- a/crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.toml +++ b/crates/cpp/tests/meshless_resources/rust_comp_a/Cargo.toml @@ -6,3 +6,4 @@ 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/build.rs b/crates/cpp/tests/meshless_resources/rust_comp_a/build.rs index e1076e6bf..6f2a100df 100644 --- a/crates/cpp/tests/meshless_resources/rust_comp_a/build.rs +++ b/crates/cpp/tests/meshless_resources/rust_comp_a/build.rs @@ -1,8 +1,7 @@ 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++"); + 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_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..ceddc1819 --- /dev/null +++ b/crates/cpp/tests/meshless_resources/rust_comp_b/src/b.rs @@ -0,0 +1,416 @@ +// Generated by `wit-bindgen` 0.27.0. DO NOT EDIT! +// Options used: +// * with "foo:foo/resources" = generate +#[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 std::{collections::BTreeMap, sync::Mutex}; + + use super::super::super::super::_rt; + + struct Representation(*mut u8); + unsafe impl Send for Representation {} + + static RESOURCE_MAP: Mutex> = Mutex::new(BTreeMap::new()); + //Default::default(); + + #[derive(Debug)] + #[repr(transparent)] + pub struct R { + handle: _rt::Resource, + } + + type _RRep = Option; + + 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(val: T) -> Self { + Self::type_guard::(); + let val: _RRep = Some(val); + let ptr: *mut _RRep = _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(&self) -> &T { + let ptr = unsafe { &*self.as_ptr::() }; + ptr.as_ref().unwrap() + } + + /// Gets mutable access to the underlying `T` which represents this + /// resource. + pub fn get_mut(&mut self) -> &mut T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.as_mut().unwrap() + } + + /// Consumes this resource and returns the underlying `T`. + pub fn into_inner(self) -> T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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() { + use core::any::TypeId; + static mut LAST_TYPE: Option = None; + unsafe { + //assert!(!cfg!(target_feature = "threads")); + let id = TypeId::of::(); + 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(handle: *mut u8) { + Self::type_guard::(); + let _ = _rt::Box::from_raw(handle as *mut _RRep); + } + + fn as_ptr(&self) -> *mut _RRep { + R::type_guard::(); + 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(&self) -> &T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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(&self) -> *mut _RRep { + R::type_guard::(); + self.rep.cast() + } + } + + unsafe impl _rt::WasmResource for R { + #[inline] + unsafe fn drop(handle: u32) { + let rep = RESOURCE_MAP.lock().unwrap().remove(&handle); + assert!(rep.is_some()); + R::dtor::(rep.unwrap().0); + } + } + + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_constructor_r_cabi(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(arg0: i32, arg1: i32) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + let a = R::from_handle(arg0 as u32).as_ptr::(); + (*a).as_ref().unwrap().add(arg1 as u32); +// T::add(RESOURCE_MAP.lock().unwrap().get(&arg0), arg1 as u32); + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_create_cabi() -> 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_consume_cabi(arg0: i32) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + T::consume(R::from_handle(arg0 as u32)); + } + + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_drop_cabi(arg0: i32) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + drop(R::from_handle(arg0 as u32)); + } + + pub trait Guest { + type R: GuestR; + fn create() -> R; + /// borrows: func(o: borrow); + fn consume(o: R); + } + pub trait GuestR: 'static { + #[doc(hidden)] + unsafe fn _resource_new(val: *mut u8) -> u32 + where + Self: Sized, + { + let mut lock = RESOURCE_MAP.lock().unwrap(); + let new_index = lock.iter().next_back().map_or(1, |elem| elem.0+1); + let old = lock.insert(new_index, Representation(val)); + assert!(old.is_none()); + new_index + } + + #[doc(hidden)] + fn _resource_rep(handle: u32) -> *mut u8 + where + Self: Sized, + { + RESOURCE_MAP.lock().unwrap().get(&handle).unwrap().0 + } + + 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 fooX3AfooX2FresourcesX00X5BconstructorX5Dr(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 fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(arg0: i32,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 fooX3AfooX2FresourcesX00create() -> i32 { + $($path_to_types)*::_export_create_cabi::<$ty>() + } + #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/resources#consume")] + #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] + unsafe extern "C" fn fooX3AfooX2FresourcesX00consume(arg0: i32,) { + $($path_to_types)*::_export_consume_cabi::<$ty>(arg0) + } + + #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] + unsafe extern "C" fn fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(arg0: i32) { + $($path_to_types)*::_export_drop_cabi::<$ty>(arg0) + } + };); +} + #[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 { + // 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, + } + + /// 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: u32); + } + + impl Resource { + #[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` 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` 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) -> u32 { + resource.handle.swap(u32::MAX, Relaxed) + } + + #[doc(hidden)] + pub fn handle(resource: &Resource) -> u32 { + resource.handle.load(Relaxed) + } + } + + impl fmt::Debug for Resource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Resource") + .field("handle", &self.handle) + .finish() + } + } + + impl Drop for Resource { + 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 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.27.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.212.0\x10wit-bindgen-rust\x060.27.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..b0558e284 --- /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); + +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 consume(o: resources::R) { + println!( + "resource consumed with {:?}", + o.get::().0.lock().unwrap() + ); + } +} From 164aec04c58b9a2dbf5650e797c3bfcafa009e4d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 6 Jul 2024 23:27:41 +0200 Subject: [PATCH 263/672] fully working rust example --- .../tests/meshless_resources/rust_comp_b/src/b.rs | 13 +++++++------ .../tests/meshless_resources/rust_comp_b/src/lib.rs | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) 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 index ceddc1819..162d8a9cb 100644 --- a/crates/cpp/tests/meshless_resources/rust_comp_b/src/b.rs +++ b/crates/cpp/tests/meshless_resources/rust_comp_b/src/b.rs @@ -150,7 +150,6 @@ pub mod exports { unsafe fn drop(handle: u32) { let rep = RESOURCE_MAP.lock().unwrap().remove(&handle); assert!(rep.is_some()); - R::dtor::(rep.unwrap().0); } } @@ -167,9 +166,10 @@ pub mod exports { pub unsafe fn _export_method_r_add_cabi(arg0: i32, arg1: i32) { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); - let a = R::from_handle(arg0 as u32).as_ptr::(); + let r = R::from_handle(arg0 as u32); + let a = r.as_ptr::(); + r.take_handle(); (*a).as_ref().unwrap().add(arg1 as u32); -// T::add(RESOURCE_MAP.lock().unwrap().get(&arg0), arg1 as u32); } #[doc(hidden)] #[allow(non_snake_case)] @@ -189,10 +189,11 @@ pub mod exports { #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_drop_cabi(arg0: i32) { + pub unsafe fn _export_drop_cabi(arg0: i32) { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); - drop(R::from_handle(arg0 as u32)); + let rep = RESOURCE_MAP.lock().unwrap().remove(&(arg0 as u32)); + R::dtor::(rep.unwrap().0); } pub trait Guest { @@ -253,7 +254,7 @@ pub mod exports { #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] unsafe extern "C" fn fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(arg0: i32) { - $($path_to_types)*::_export_drop_cabi::<$ty>(arg0) + $($path_to_types)*::_export_drop_cabi::<<$ty as $($path_to_types)*::Guest>::R>(arg0) } };); } 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 index b0558e284..eb353f5ea 100644 --- a/crates/cpp/tests/meshless_resources/rust_comp_b/src/lib.rs +++ b/crates/cpp/tests/meshless_resources/rust_comp_b/src/lib.rs @@ -25,7 +25,7 @@ impl Guest for MyWorld { type R = MyResource; fn create() -> resources::R { - resources::R::new(MyResource::new(1)) + resources::R::new(MyResource::new(17)) } fn consume(o: resources::R) { From 5107e65754301e0cb332e3aa5860325e59aa63a9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 9 Jul 2024 20:52:02 +0200 Subject: [PATCH 264/672] introduce symmetric option, support empty with for Rust --- crates/cpp/src/lib.rs | 5 +++++ crates/rust/src/lib.rs | 44 +++++++++++++++++++++++++++--------------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 6fb8d316b..d5167bf69 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -213,6 +213,11 @@ pub struct Opts { /// 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, } impl Opts { diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 6c6d62fa8..09a4068e5 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -213,6 +213,11 @@ pub struct Opts { /// 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, } impl Opts { @@ -1223,25 +1228,32 @@ impl FromStr for WithGeneration { type Err = String; fn from_str(s: &str) -> std::prelude::v1::Result { - let with = s - .trim() - .split(',') - .map(|s| { - let (k, v) = s.trim().split_once('=').ok_or_else(|| { + if s.is_empty() { + Ok(WithGeneration { + with: Default::default(), + generate_by_default: true, + }) + } else { + let with = + s.trim() + .split(',') + .map(|s| { + let (k, v) = s.trim().split_once('=').ok_or_else(|| { format!("expected string of form `=[,=...]`; got `{s}`") })?; - let k = k.trim().to_string(); - let v = match v.trim() { - "generate" => WithOption::Generate, - v => WithOption::Path(v.to_string()), - }; - Ok((k, v)) + let k = k.trim().to_string(); + let v = match v.trim() { + "generate" => WithOption::Generate, + v => WithOption::Path(v.to_string()), + }; + Ok((k, v)) + }) + .collect::, Self::Err>>()?; + Ok(WithGeneration { + with, + generate_by_default: false, }) - .collect::, Self::Err>>()?; - Ok(WithGeneration { - with, - generate_by_default: false, - }) + } } } From 4aa0c4926e3b07515cdd854e91dedf276a583927 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 9 Jul 2024 22:28:00 +0200 Subject: [PATCH 265/672] first steps towards symmetric calling convention --- crates/core/src/abi.rs | 351 +++++++++--------- crates/cpp/src/lib.rs | 10 +- .../meshless_strings/component_a/Makefile | 2 +- .../component_a/the_world_cpp.h | 3 +- crates/rust/src/bindgen.rs | 1 + 5 files changed, 192 insertions(+), 175 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 2ec249b1d..b179ece61 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -586,6 +586,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 @@ -786,193 +788,206 @@ impl<'a, B: Bindgen> Generator<'a, B> { fn call(&mut self, func: &Function) { let sig = self.resolve.wasm_signature(self.variant, func); - match self.lift_lower { - LiftLower::LowerArgsLiftResults => { - if !sig.indirect_params { - // If the parameters for this function aren't indirect - // (there aren't too many) then we simply do a normal lower - // operation for them all. - for (nth, (_, ty)) in func.params.iter().enumerate() { - self.emit(&Instruction::GetArg { nth }); - self.lower(ty); - } - } else { - // ... otherwise if parameters are indirect space is - // allocated from them and each argument is lowered - // individually into memory. - let (size, align) = self - .bindgen - .sizes() - .record(func.params.iter().map(|t| &t.1)); - let ptr = match self.variant { - // When a wasm module calls an import it will provide - // space that isn't explicitly deallocated. - AbiVariant::GuestImport => self.bindgen.return_pointer(size, align), - // When calling a wasm module from the outside, though, - // malloc needs to be called. - AbiVariant::GuestExport => { - self.emit(&Instruction::Malloc { - realloc: "cabi_realloc", - size, - align, - }); - self.stack.pop().unwrap() - } - }; - let mut offset = 0usize; - for (nth, (_, ty)) in func.params.iter().enumerate() { - self.emit(&Instruction::GetArg { nth }); - offset = align_to(offset, self.bindgen.sizes().align(ty)); - self.write_to_memory(ty, ptr.clone(), offset as i32); - offset += self.bindgen.sizes().size(ty); + let language_to_abi = matches!(self.lift_lower, LiftLower::LowerArgsLiftResults) + || (matches!(self.lift_lower, LiftLower::Symmetric) + && matches!(self.variant, AbiVariant::GuestImport)); + if language_to_abi { + // bad symmetric hack + let sig = if sig.retptr && matches!(self.lift_lower, LiftLower::Symmetric) { + WasmSignature { + params: Vec::from(&sig.params[0..sig.params.len() - 1]), + results: Vec::from(&sig.params[sig.params.len() - 1..]), + indirect_params: sig.indirect_params, + retptr: false, + } + } else { + sig + }; + if !sig.indirect_params { + // If the parameters for this function aren't indirect + // (there aren't too many) then we simply do a normal lower + // operation for them all. + for (nth, (_, ty)) in func.params.iter().enumerate() { + self.emit(&Instruction::GetArg { nth }); + self.lower(ty); + } + } else { + // ... otherwise if parameters are indirect space is + // allocated from them and each argument is lowered + // individually into memory. + let (size, align) = self + .bindgen + .sizes() + .record(func.params.iter().map(|t| &t.1)); + let ptr = match self.variant { + // When a wasm module calls an import it will provide + // space that isn't explicitly deallocated. + AbiVariant::GuestImport => self.bindgen.return_pointer(size, align), + // When calling a wasm module from the outside, though, + // malloc needs to be called. + AbiVariant::GuestExport => { + self.emit(&Instruction::Malloc { + realloc: "cabi_realloc", + size, + align, + }); + self.stack.pop().unwrap() } - - self.stack.push(ptr); + }; + let mut offset = 0usize; + for (nth, (_, ty)) in func.params.iter().enumerate() { + self.emit(&Instruction::GetArg { nth }); + offset = align_to(offset, self.bindgen.sizes().align(ty)); + self.write_to_memory(ty, ptr.clone(), offset as i32); + offset += self.bindgen.sizes().size(ty); } - // If necessary we may need to prepare a return pointer for - // this ABI. - if self.variant == AbiVariant::GuestImport && sig.retptr { - let (size, align) = self.bindgen.sizes().params(func.results.iter_types()); - let ptr = self.bindgen.return_pointer(size, align); - self.return_pointer = Some(ptr.clone()); - self.stack.push(ptr); - } + self.stack.push(ptr); + } - // Now that all the wasm args are prepared we can call the - // actual wasm function. - assert_eq!(self.stack.len(), sig.params.len()); - self.emit(&Instruction::CallWasm { - name: &func.name, - sig: &sig, - }); + // If necessary we may need to prepare a return pointer for + // this ABI. + if self.variant == AbiVariant::GuestImport && sig.retptr { + let (size, align) = self.bindgen.sizes().params(func.results.iter_types()); + let ptr = self.bindgen.return_pointer(size, align); + self.return_pointer = Some(ptr.clone()); + self.stack.push(ptr); + } - if !sig.retptr { - // With no return pointer in use we can simply lift the - // result(s) of the function from the result of the core - // wasm function. - for ty in func.results.iter_types() { - self.lift(ty) + // Now that all the wasm args are prepared we can call the + // actual wasm function. + assert_eq!(self.stack.len(), sig.params.len()); + self.emit(&Instruction::CallWasm { + name: &func.name, + sig: &sig, + }); + + if matches!(self.lift_lower, LiftLower::Symmetric) && func.results.len()!=0 { + let ptr = self.stack.pop().unwrap(); + self.read_results_from_memory(&func.results, ptr, 0); + } else if !sig.retptr { + // With no return pointer in use we can simply lift the + // result(s) of the function from the result of the core + // wasm function. + for ty in func.results.iter_types() { + self.lift(ty) + } + } else { + let ptr = match self.variant { + // imports into guests means it's a wasm module + // calling an imported function. We supplied the + // return poitner as the last argument (saved in + // `self.return_pointer`) so we use that to read + // the result of the function from memory. + AbiVariant::GuestImport => { + assert!(sig.results.is_empty()); + self.return_pointer.take().unwrap() } - } else { - let ptr = match self.variant { - // imports into guests means it's a wasm module - // calling an imported function. We supplied the - // return poitner as the last argument (saved in - // `self.return_pointer`) so we use that to read - // the result of the function from memory. - AbiVariant::GuestImport => { - assert!(sig.results.is_empty()); - self.return_pointer.take().unwrap() - } - - // guest exports means that this is a host - // calling wasm so wasm returned a pointer to where - // the result is stored - AbiVariant::GuestExport => self.stack.pop().unwrap(), - }; - self.read_results_from_memory(&func.results, ptr, 0); - } + // guest exports means that this is a host + // calling wasm so wasm returned a pointer to where + // the result is stored + AbiVariant::GuestExport => self.stack.pop().unwrap(), + }; - self.emit(&Instruction::Return { - func, - amt: func.results.len(), - }); + self.read_results_from_memory(&func.results, ptr, 0); } - LiftLower::LiftArgsLowerResults => { - if !sig.indirect_params { - // If parameters are not passed indirectly then we lift each - // argument in succession from the component wasm types that - // make-up the type. - let mut offset = 0; - let mut temp = Vec::new(); - for (_, ty) in func.params.iter() { - temp.truncate(0); - self.resolve.push_flat(ty, &mut temp); - for _ in 0..temp.len() { - self.emit(&Instruction::GetArg { nth: offset }); - offset += 1; - } - self.lift(ty); - } - } else { - // ... otherwise argument is read in succession from memory - // where the pointer to the arguments is the first argument - // to the function. - let mut offset = 0usize; - self.emit(&Instruction::GetArg { nth: 0 }); - let ptr = self.stack.pop().unwrap(); - for (_, ty) in func.params.iter() { - offset = align_to(offset, self.bindgen.sizes().align(ty)); - self.read_from_memory(ty, ptr.clone(), offset as i32); - offset += self.bindgen.sizes().size(ty); + + self.emit(&Instruction::Return { + func, + amt: func.results.len(), + }); + } else { + if !sig.indirect_params { + // If parameters are not passed indirectly then we lift each + // argument in succession from the component wasm types that + // make-up the type. + let mut offset = 0; + let mut temp = Vec::new(); + for (_, ty) in func.params.iter() { + temp.truncate(0); + self.resolve.push_flat(ty, &mut temp); + for _ in 0..temp.len() { + self.emit(&Instruction::GetArg { nth: offset }); + offset += 1; } + self.lift(ty); + } + } else { + // ... otherwise argument is read in succession from memory + // where the pointer to the arguments is the first argument + // to the function. + let mut offset = 0usize; + self.emit(&Instruction::GetArg { nth: 0 }); + let ptr = self.stack.pop().unwrap(); + for (_, ty) in func.params.iter() { + offset = align_to(offset, self.bindgen.sizes().align(ty)); + self.read_from_memory(ty, ptr.clone(), offset as i32); + offset += self.bindgen.sizes().size(ty); } + } - // ... and that allows us to call the interface types function - self.emit(&Instruction::CallInterface { func }); - - // This was dynamically allocated by the caller so after - // it's been read by the guest we need to deallocate it. - if let AbiVariant::GuestExport = self.variant { - if sig.indirect_params { - let (size, align) = self - .bindgen - .sizes() - .record(func.params.iter().map(|t| &t.1)); - self.emit(&Instruction::GetArg { nth: 0 }); - self.emit(&Instruction::GuestDeallocate { size, align }); - } + // ... and that allows us to call the interface types function + self.emit(&Instruction::CallInterface { func }); + + // This was dynamically allocated by the caller so after + // it's been read by the guest we need to deallocate it. + if let AbiVariant::GuestExport = self.variant { + if sig.indirect_params { + let (size, align) = self + .bindgen + .sizes() + .record(func.params.iter().map(|t| &t.1)); + self.emit(&Instruction::GetArg { nth: 0 }); + self.emit(&Instruction::GuestDeallocate { size, align }); } + } - if !sig.retptr { - // With no return pointer in use we simply lower the - // result(s) and return that directly from the function. - let results = self - .stack - .drain(self.stack.len() - func.results.len()..) - .collect::>(); - for (ty, result) in func.results.iter_types().zip(results) { - self.stack.push(result); - self.lower(ty); + if !sig.retptr { + // With no return pointer in use we simply lower the + // result(s) and return that directly from the function. + let results = self + .stack + .drain(self.stack.len() - func.results.len()..) + .collect::>(); + for (ty, result) in func.results.iter_types().zip(results) { + self.stack.push(result); + self.lower(ty); + } + } else { + match self.variant { + // 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 => { + self.emit(&Instruction::GetArg { + nth: sig.params.len() - 1, + }); + let ptr = self.stack.pop().unwrap(); + self.write_params_to_memory(func.results.iter_types(), ptr, 0); } - } else { - match self.variant { - // 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 => { - self.emit(&Instruction::GetArg { - nth: sig.params.len() - 1, - }); - let ptr = self.stack.pop().unwrap(); - self.write_params_to_memory(func.results.iter_types(), ptr, 0); - } - // For a guest import this is a function defined in - // wasm, so we're returning a pointer where the - // 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 => { - let (size, align) = - self.bindgen.sizes().params(func.results.iter_types()); - let ptr = self.bindgen.return_pointer(size, align); - self.write_params_to_memory(func.results.iter_types(), ptr.clone(), 0); - self.stack.push(ptr); - } + // For a guest import this is a function defined in + // wasm, so we're returning a pointer where the + // 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 => { + let (size, align) = self.bindgen.sizes().params(func.results.iter_types()); + let ptr = self.bindgen.return_pointer(size, align); + self.write_params_to_memory(func.results.iter_types(), ptr.clone(), 0); + self.stack.push(ptr); } } - - self.emit(&Instruction::Return { - func, - amt: sig.results.len(), - }); } + + self.emit(&Instruction::Return { + func, + amt: sig.results.len(), + }); } assert!( diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index d5167bf69..a150401ff 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1418,7 +1418,9 @@ impl CppInterfaceGenerator<'_> { let special = is_special_method(func); if !matches!(special, SpecialMethod::Allocate) { self.gen.c_src.src.push_str("{\n"); - let lift_lower = if export { + let lift_lower = if self.gen.opts.symmetric { + LiftLower::Symmetric + } else if export { LiftLower::LiftArgsLowerResults } else { LiftLower::LowerArgsLiftResults @@ -1455,7 +1457,7 @@ impl CppInterfaceGenerator<'_> { ); } } - LiftLower::LowerArgsLiftResults => { + LiftLower::LowerArgsLiftResults | LiftLower::Symmetric => { if self.gen.opts.host_side() { let namespace = class_namespace(self, func, variant); self.gen.c_src.qualify(&namespace); @@ -2554,7 +2556,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if realloc.is_none() { results.push(ptr); } else { - if !self.gen.gen.opts.host { + if !self.gen.gen.opts.host_side() && !self.gen.gen.opts.symmetric { uwriteln!(self.src, "{}.leak();\n", operands[0]); } results.push(ptr); @@ -2583,7 +2585,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if realloc.is_none() { results.push(ptr); } else { - if !self.gen.gen.opts.host_side() { + if !self.gen.gen.opts.host_side() && !self.gen.gen.opts.symmetric { uwriteln!(self.src, "{}.leak();\n", operands[0]); } results.push(ptr); diff --git a/crates/cpp/tests/meshless_strings/component_a/Makefile b/crates/cpp/tests/meshless_strings/component_a/Makefile index 054e3ca37..d5c67dda3 100644 --- a/crates/cpp/tests/meshless_strings/component_a/Makefile +++ b/crates/cpp/tests/meshless_strings/component_a/Makefile @@ -6,7 +6,7 @@ component_a: the_world.cpp main.cpp $(CXX) $(CXXFLAGS) -o $@ $^ -L../component_b -lcomponent_b bindgen: - ../../../../../target/debug/wit-bindgen cpp ../wit -w a --wasm64 --format + ../../../../../target/debug/wit-bindgen cpp ../wit --symmetric --internal-prefix=comp_a --wasm64 --format clean: -rm *~ component_a *.o 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 index 32ef96a2e..f41bd95d4 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world_cpp.h +++ b/crates/cpp/tests/meshless_strings/component_a/the_world_cpp.h @@ -28,6 +28,5 @@ wit::string C(wit::string &&a, wit::string &&b); } // namespace foo } // namespace exports } // namespace comp_a -using namespace comp_a; -// using namespace A::foo; + #endif diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index ab2d46fd1..0517c78f5 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -188,6 +188,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { let owned = match self.lift_lower() { LiftLower::LowerArgsLiftResults => false, LiftLower::LiftArgsLowerResults => true, + LiftLower::Symmetric => todo!(), }; self.gen.type_path(id, owned) } From c9e5a2424447eadff103fa10520c0885bf9a9c6a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 10 Jul 2024 00:49:48 +0200 Subject: [PATCH 266/672] imperfect cabi_post call --- crates/core/src/abi.rs | 17 +++++++++++++++-- crates/cpp/src/lib.rs | 5 ++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index b179ece61..20553a05d 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -862,9 +862,22 @@ impl<'a, B: Bindgen> Generator<'a, B> { sig: &sig, }); - if matches!(self.lift_lower, LiftLower::Symmetric) && func.results.len()!=0 { + if matches!(self.lift_lower, LiftLower::Symmetric) && func.results.len() != 0 { let ptr = self.stack.pop().unwrap(); - self.read_results_from_memory(&func.results, ptr, 0); + self.read_results_from_memory(&func.results, ptr.clone(), 0); + let post_name = String::from("cabi_post_") + &func.name; + let post_sig = WasmSignature { + params: vec![WasmType::Pointer], + results: Vec::new(), + indirect_params: false, + retptr: false, + }; + // TODO: can we get this name from somewhere? + self.stack.push(ptr); + self.emit(&Instruction::CallWasm { + name: &post_name, + sig: &post_sig, + }); } else if !sig.retptr { // With no return pointer in use we can simply lift the // result(s) of the function from the result of the core diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index a150401ff..9fe4bebeb 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2649,7 +2649,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let tmp = self.tmp(); let len = format!("len{}", tmp); uwriteln!(self.src, "auto {} = {};\n", len, operands[1]); - let result = if self.gen.gen.opts.host { + let result = if self.gen.gen.opts.symmetric { + uwriteln!(self.src, "auto string{tmp} = wit::string::from_view(std::string_view((char const *)({}), {len}));\n", operands[0]); + format!("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 { From 08820f66c8e96529900f86ee43e4d2eb2abe0770 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 10 Jul 2024 00:55:57 +0200 Subject: [PATCH 267/672] proper cabi_post calls --- crates/core/src/abi.rs | 6 ++++-- crates/cpp/src/lib.rs | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 20553a05d..1a97dee06 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -462,6 +462,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 @@ -860,12 +861,12 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&Instruction::CallWasm { name: &func.name, sig: &sig, + module_prefix: "", }); if matches!(self.lift_lower, LiftLower::Symmetric) && func.results.len() != 0 { let ptr = self.stack.pop().unwrap(); self.read_results_from_memory(&func.results, ptr.clone(), 0); - let post_name = String::from("cabi_post_") + &func.name; let post_sig = WasmSignature { params: vec![WasmType::Pointer], results: Vec::new(), @@ -875,8 +876,9 @@ impl<'a, B: Bindgen> Generator<'a, B> { // TODO: can we get this name from somewhere? self.stack.push(ptr); self.emit(&Instruction::CallWasm { - name: &post_name, + name: &func.name, sig: &post_sig, + module_prefix: "cabi_post_", }); } else if !sig.retptr { // With no return pointer in use we can simply lift the diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 9fe4bebeb..070b7a8f5 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -3177,12 +3177,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { ); results.push(resultname); } - abi::Instruction::CallWasm { name, sig } => { + abi::Instruction::CallWasm { name, sig, module_prefix } => { let module_name = self .gen .wasm_import_module .as_ref() - .map(|e| e.clone()) + .map(|e| String::from(*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\ From 410cd32b650722b05a05333af6fb48b663bfc086 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 10 Jul 2024 23:00:15 +0200 Subject: [PATCH 268/672] quite accurate code generation for symmetric string --- crates/cpp/src/lib.rs | 2 +- .../component_a/the_world.cpp | 68 +++++++++++-------- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 070b7a8f5..6f597de08 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2585,7 +2585,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if realloc.is_none() { results.push(ptr); } else { - if !self.gen.gen.opts.host_side() && !self.gen.gen.opts.symmetric { + 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); diff --git a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp index 840b8e7a6..5bf39de0e 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp @@ -31,11 +31,15 @@ fooX3AfooX2FstringsX00a(uint8_t *, size_t); extern "C" __attribute__((import_module("foo:foo/strings"))) __attribute__((import_name("b"))) uint8_t * fooX3AfooX2FstringsX00b(); +extern "C" __attribute__((import_module("cabi_post_foo:foo/strings"))) +__attribute__((import_name("b"))) void +cabi_post_fooX3AfooX2FstringsX00b(uint8_t *); extern "C" __attribute__((import_module("foo:foo/strings"))) __attribute__((import_name("c"))) uint8_t * fooX3AfooX2FstringsX00c(uint8_t *, size_t, uint8_t *, size_t); -extern "C" void cabi_post_fooX3AfooX2FstringsX00b(uint8_t *); -extern "C" void cabi_post_fooX3AfooX2FstringsX00c(uint8_t *); +extern "C" __attribute__((import_module("cabi_post_foo:foo/strings"))) +__attribute__((import_name("c"))) void +cabi_post_fooX3AfooX2FstringsX00c(uint8_t *); void comp_a::foo::foo::strings::A(std::string_view x) { auto const &vec0 = x; auto ptr0 = (uint8_t *)(vec0.data()); @@ -43,12 +47,14 @@ void comp_a::foo::foo::strings::A(std::string_view x) { fooX3AfooX2FstringsX00a(ptr0, len0); } wit::string comp_a::foo::foo::strings::B() { - uint8_t *ptr0 = fooX3AfooX2FstringsX00b(); - auto len1 = *((size_t *)(ptr0 + sizeof(size_t))); - auto res = wit::string::from_view( - std::string_view((char const *)(*((uint8_t **)(ptr0 + 0))), len1)); - cabi_post_fooX3AfooX2FstringsX00b(ptr0); - return res; + auto ret = fooX3AfooX2FstringsX00b(); + auto len0 = *((size_t *)(ret + 8)); + + auto string0 = wit::string::from_view( + std::string_view((char const *)(*((uint8_t **)(ret + 0))), len0)); + + cabi_post_fooX3AfooX2FstringsX00b(ret); + return string0; } wit::string comp_a::foo::foo::strings::C(std::string_view a, std::string_view b) { @@ -58,31 +64,35 @@ wit::string comp_a::foo::foo::strings::C(std::string_view a, auto const &vec1 = b; auto ptr1 = (uint8_t *)(vec1.data()); auto len1 = (size_t)(vec1.size()); - uint8_t *ptr2 = fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1); - auto len3 = *((size_t *)(ptr2 + sizeof(size_t))); - auto res = wit::string::from_view( - std::string_view((char const *)(*((uint8_t **)(ptr2 + 0))), len3)); - cabi_post_fooX3AfooX2FstringsX00c(ptr2); - return res; + auto ret = fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1); + auto len2 = *((size_t *)(ret + 8)); + + auto string2 = wit::string::from_view( + std::string_view((char const *)(*((uint8_t **)(ret + 0))), len2)); + + cabi_post_fooX3AfooX2FstringsX00c(ret); + return string2; } extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void a_fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { auto len0 = arg1; - exports::foo::foo::strings::A( - wit::string::from_view(std::string_view((char const *)(arg0), len0))); + auto string0 = + wit::string::from_view(std::string_view((char const *)(arg0), len0)); + + comp_a::exports::foo::foo::strings::A(std::move(string0)); } extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) uint8_t * a_fooX3AfooX2FstringsX00b() { - auto result0 = exports::foo::foo::strings::B(); - static size_t ret_area[2]; + auto result0 = comp_a::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 + sizeof(size_t))) = len2; + *((size_t *)(ptr1 + 8)) = len2; *((uint8_t **)(ptr1 + 0)) = ptr2; return ptr1; } @@ -90,28 +100,32 @@ extern "C" __attribute__((__weak__, __export_name__("cabi_post_fooX3AfooX2FstringsX23b"))) void a_cabi_post_fooX3AfooX2FstringsX00b(uint8_t *arg0) { - if ((*((size_t *)(arg0 + sizeof(size_t)))) > 0) { + 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 * a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, - size_t arg3) { + size_t arg3) { auto len0 = arg1; + auto string0 = + wit::string::from_view(std::string_view((char const *)(arg0), len0)); + auto len1 = arg3; - auto result2 = exports::foo::foo::strings::C( - wit::string::from_view(std::string_view((char const *)(arg0), len0)), - wit::string::from_view(std::string_view((char const *)(arg2), len1))); - static size_t ret_area[2]; + auto string1 = + wit::string::from_view(std::string_view((char const *)(arg2), len1)); + + auto result2 = comp_a::exports::foo::foo::strings::C(std::move(string0), std::move(string1)); + 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 + sizeof(size_t))) = len4; + *((size_t *)(ptr3 + 8)) = len4; *((uint8_t **)(ptr3 + 0)) = ptr4; return ptr3; } @@ -119,7 +133,7 @@ extern "C" __attribute__((__weak__, __export_name__("cabi_post_fooX3AfooX2FstringsX23c"))) void a_cabi_post_fooX3AfooX2FstringsX00c(uint8_t *arg0) { - if ((*((size_t *)(arg0 + sizeof(size_t)))) > 0) { + if ((*((size_t *)(arg0 + 8))) > 0) { wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); } } From ef0d4fd7b0d83480be434a77ff1d8efcea0d9557 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 10 Jul 2024 23:23:51 +0200 Subject: [PATCH 269/672] symmetric code generation for strings works (only prefix missing) --- crates/cpp/src/lib.rs | 42 +++++++++++++++---- .../component_a/the_world.cpp | 7 ++-- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 6f597de08..98285d1c2 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1021,6 +1021,10 @@ impl CppInterfaceGenerator<'_> { }; 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, @@ -1613,14 +1617,27 @@ impl CppInterfaceGenerator<'_> { 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) => make_external_symbol(module_name, &func.name, variant), + 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) + } 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, AbiVariant::GuestExport) - } + 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); @@ -2585,7 +2602,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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)) { + 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); @@ -2651,7 +2671,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { uwriteln!(self.src, "auto {} = {};\n", len, operands[1]); let result = if self.gen.gen.opts.symmetric { uwriteln!(self.src, "auto string{tmp} = wit::string::from_view(std::string_view((char const *)({}), {len}));\n", operands[0]); - format!("string{tmp}") + if matches!(self.variant, AbiVariant::GuestExport) { + format!("std::move(string{tmp})") + } else { + format!("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) @@ -3177,7 +3201,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { ); results.push(resultname); } - abi::Instruction::CallWasm { name, sig, module_prefix } => { + abi::Instruction::CallWasm { + name, + sig, + module_prefix, + } => { let module_name = self .gen .wasm_import_module diff --git a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp index 5bf39de0e..540f7501f 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp @@ -98,7 +98,7 @@ a_fooX3AfooX2FstringsX00b() { } extern "C" __attribute__((__weak__, - __export_name__("cabi_post_fooX3AfooX2FstringsX23b"))) void + __export_name__("cabi_post_fooX3AfooX2FstringsX00b"))) void a_cabi_post_fooX3AfooX2FstringsX00b(uint8_t *arg0) { if ((*((size_t *)(arg0 + 8))) > 0) { wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); @@ -117,7 +117,8 @@ a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, auto string1 = wit::string::from_view(std::string_view((char const *)(arg2), len1)); - auto result2 = comp_a::exports::foo::foo::strings::C(std::move(string0), std::move(string1)); + auto result2 = comp_a::exports::foo::foo::strings::C(std::move(string0), + std::move(string1)); static uint64_t ret_area[2]; uint8_t *ptr3 = (uint8_t *)(&ret_area); auto const &vec4 = result2; @@ -131,7 +132,7 @@ a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, } extern "C" __attribute__((__weak__, - __export_name__("cabi_post_fooX3AfooX2FstringsX23c"))) void + __export_name__("cabi_post_fooX3AfooX2FstringsX00c"))) void a_cabi_post_fooX3AfooX2FstringsX00c(uint8_t *arg0) { if ((*((size_t *)(arg0 + 8))) > 0) { wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); From 33dbde73f9b802dd8ef4548f8e198ac6e2b9e978 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 10 Jul 2024 23:28:25 +0200 Subject: [PATCH 270/672] regenerated code --- .../meshless_strings/component_b/Makefile | 2 +- .../component_b/the_world.cpp | 69 +++++++++++-------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/crates/cpp/tests/meshless_strings/component_b/Makefile b/crates/cpp/tests/meshless_strings/component_b/Makefile index 13439bc8e..9d9dea504 100644 --- a/crates/cpp/tests/meshless_strings/component_b/Makefile +++ b/crates/cpp/tests/meshless_strings/component_b/Makefile @@ -6,7 +6,7 @@ libcomponent_b.a: the_world.o guest.o ar rcvs $@ $^ bindgen: - ../../../../../target/debug/wit-bindgen cpp ../wit -w b --wasm64 --format + ../../../../../target/debug/wit-bindgen cpp ../wit --symmetric --wasm64 --format clean: -rm *~ *.a *.o diff --git a/crates/cpp/tests/meshless_strings/component_b/the_world.cpp b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp index 4e4b76ccd..83de2e361 100644 --- a/crates/cpp/tests/meshless_strings/component_b/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp @@ -31,11 +31,15 @@ a_fooX3AfooX2FstringsX00a(uint8_t *, size_t); extern "C" __attribute__((import_module("foo:foo/strings"))) __attribute__((import_name("b"))) uint8_t * a_fooX3AfooX2FstringsX00b(); +extern "C" __attribute__((import_module("cabi_post_foo:foo/strings"))) +__attribute__((import_name("b"))) void +a_cabi_post_fooX3AfooX2FstringsX00b(uint8_t *); extern "C" __attribute__((import_module("foo:foo/strings"))) __attribute__((import_name("c"))) uint8_t * a_fooX3AfooX2FstringsX00c(uint8_t *, size_t, uint8_t *, size_t); -extern "C" void a_cabi_post_fooX3AfooX2FstringsX00b(uint8_t *); -extern "C" void a_cabi_post_fooX3AfooX2FstringsX00c(uint8_t *); +extern "C" __attribute__((import_module("cabi_post_foo:foo/strings"))) +__attribute__((import_name("c"))) void +a_cabi_post_fooX3AfooX2FstringsX00c(uint8_t *); void foo::foo::strings::A(std::string_view x) { auto const &vec0 = x; auto ptr0 = (uint8_t *)(vec0.data()); @@ -43,12 +47,14 @@ void foo::foo::strings::A(std::string_view x) { a_fooX3AfooX2FstringsX00a(ptr0, len0); } wit::string foo::foo::strings::B() { - uint8_t *ptr0 = a_fooX3AfooX2FstringsX00b(); - auto len1 = *((size_t *)(ptr0 + sizeof(size_t))); - auto res = wit::string::from_view( - std::string_view((char const *)(*((uint8_t **)(ptr0 + 0))), len1)); - a_cabi_post_fooX3AfooX2FstringsX00b(ptr0); - return res; + auto ret = a_fooX3AfooX2FstringsX00b(); + auto len0 = *((size_t *)(ret + 8)); + + auto string0 = wit::string::from_view( + std::string_view((char const *)(*((uint8_t **)(ret + 0))), len0)); + + a_cabi_post_fooX3AfooX2FstringsX00b(ret); + return string0; } wit::string foo::foo::strings::C(std::string_view a, std::string_view b) { auto const &vec0 = a; @@ -57,39 +63,43 @@ wit::string foo::foo::strings::C(std::string_view a, std::string_view b) { auto const &vec1 = b; auto ptr1 = (uint8_t *)(vec1.data()); auto len1 = (size_t)(vec1.size()); - uint8_t *ptr2 = a_fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1); - auto len3 = *((size_t *)(ptr2 + sizeof(size_t))); - auto res = wit::string::from_view( - std::string_view((char const *)(*((uint8_t **)(ptr2 + 0))), len3)); - a_cabi_post_fooX3AfooX2FstringsX00c(ptr2); - return res; + auto ret = a_fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1); + auto len2 = *((size_t *)(ret + 8)); + + auto string2 = wit::string::from_view( + std::string_view((char const *)(*((uint8_t **)(ret + 0))), len2)); + + a_cabi_post_fooX3AfooX2FstringsX00c(ret); + return string2; } 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( - wit::string::from_view(std::string_view((char const *)(arg0), len0))); + auto string0 = + wit::string::from_view(std::string_view((char const *)(arg0), len0)); + + exports::foo::foo::strings::A(std::move(string0)); } extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) uint8_t * fooX3AfooX2FstringsX00b() { auto result0 = exports::foo::foo::strings::B(); - static size_t ret_area[2]; + 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 + sizeof(size_t))) = len2; + *((size_t *)(ptr1 + 8)) = len2; *((uint8_t **)(ptr1 + 0)) = ptr2; return ptr1; } extern "C" __attribute__((__weak__, - __export_name__("cabi_post_fooX3AfooX2FstringsX23b"))) void + __export_name__("cabi_post_fooX3AfooX2FstringsX00b"))) void cabi_post_fooX3AfooX2FstringsX00b(uint8_t *arg0) { - if ((*((size_t *)(arg0 + sizeof(size_t)))) > 0) { + if ((*((size_t *)(arg0 + 8))) > 0) { wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); } } @@ -98,27 +108,32 @@ fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3) { auto len0 = arg1; + auto string0 = + wit::string::from_view(std::string_view((char const *)(arg0), len0)); + auto len1 = arg3; - auto result2 = exports::foo::foo::strings::C( - wit::string::from_view(std::string_view((char const *)(arg0), len0)), - wit::string::from_view(std::string_view((char const *)(arg2), len1))); - static size_t ret_area[2]; + auto string1 = + wit::string::from_view(std::string_view((char const *)(arg2), len1)); + + auto result2 = + exports::foo::foo::strings::C(std::move(string0), std::move(string1)); + 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 + sizeof(size_t))) = len4; + *((size_t *)(ptr3 + 8)) = len4; *((uint8_t **)(ptr3 + 0)) = ptr4; return ptr3; } extern "C" __attribute__((__weak__, - __export_name__("cabi_post_fooX3AfooX2FstringsX23c"))) void + __export_name__("cabi_post_fooX3AfooX2FstringsX00c"))) void cabi_post_fooX3AfooX2FstringsX00c(uint8_t *arg0) { - if ((*((size_t *)(arg0 + sizeof(size_t)))) > 0) { + if ((*((size_t *)(arg0 + 8))) > 0) { wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); } } From a7b79bf6444c6e149ddd743aa7d47cb90962e6c1 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 11 Jul 2024 21:09:12 +0200 Subject: [PATCH 271/672] fix resource import --- crates/core/src/abi.rs | 4 +++- crates/cpp/tests/meshless_resources/component_a/Makefile | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 1a97dee06..09d2ee5e7 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -864,7 +864,9 @@ impl<'a, B: Bindgen> Generator<'a, B> { module_prefix: "", }); - if matches!(self.lift_lower, LiftLower::Symmetric) && func.results.len() != 0 { + if matches!(self.lift_lower, LiftLower::Symmetric) + && guest_export_needs_post_return(self.resolve, func) + { let ptr = self.stack.pop().unwrap(); self.read_results_from_memory(&func.results, ptr.clone(), 0); let post_sig = WasmSignature { diff --git a/crates/cpp/tests/meshless_resources/component_a/Makefile b/crates/cpp/tests/meshless_resources/component_a/Makefile index ae570a356..bf15a3c65 100644 --- a/crates/cpp/tests/meshless_resources/component_a/Makefile +++ b/crates/cpp/tests/meshless_resources/component_a/Makefile @@ -6,7 +6,7 @@ component_a: a.cpp main.cpp $(CXX) $(CXXFLAGS) -o $@ $^ -L../component_b -lcomponent_b bindgen: - ../../../../../target/debug/wit-bindgen cpp ../wit -w a --wasm64 --format + ../../../../../target/debug/wit-bindgen cpp ../wit -w a --symmetric --wasm64 --format clean: -rm *~ component_a *.o From 77bfa5e45aa9188a773f39bf66f3a598a4b97859 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 11 Jul 2024 22:13:28 +0200 Subject: [PATCH 272/672] using pointers in import probably works to omit a resource table --- crates/core/src/abi.rs | 75 ++++++++++++++++++- crates/cpp/helper-types/wit-guest.h | 22 ++++-- crates/cpp/src/lib.rs | 68 +++++++++++------ .../cpp/tests/native_mesh/component_a/a.cpp | 12 +-- .../cpp/tests/native_mesh/component_a/a_cpp.h | 1 + 5 files changed, 140 insertions(+), 38 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 09d2ee5e7..dde7fd704 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -687,7 +687,12 @@ pub fn call( func: &Function, bindgen: &mut impl Bindgen, ) { - Generator::new(resolve, variant, lift_lower, bindgen).call(func); + if matches!(lift_lower, LiftLower::Symmetric) { + let sig = wasm_signature_symmetric(resolve, variant, func); + Generator::new(resolve, variant, lift_lower, bindgen).call_with_signature(func, sig); + } else { + Generator::new(resolve, variant, lift_lower, bindgen).call(func); + } } /// Used in a similar manner as the `Interface::call` function except is @@ -788,7 +793,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { fn call(&mut self, func: &Function) { 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) { let language_to_abi = matches!(self.lift_lower, LiftLower::LowerArgsLiftResults) || (matches!(self.lift_lower, LiftLower::Symmetric) && matches!(self.variant, AbiVariant::GuestImport)); @@ -1999,3 +2007,68 @@ fn cast(from: WasmType, to: WasmType) -> Bitcast { fn align_to(val: usize, align: usize) -> usize { (val + align - 1) & !(align - 1) } + +fn push_flat_symmetric(resolve: &Resolve, ty: &Type, vec: &mut Vec) { + if let Type::Id(id) = ty { + if matches!(&resolve.types[*id].kind, TypeDefKind::Handle(_)) { + 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, +) -> WasmSignature { + 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.results.iter_types() { + 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); + match variant { + AbiVariant::GuestImport => { + params.push(WasmType::Pointer); + } + AbiVariant::GuestExport => { + results.push(WasmType::Pointer); + } + } + } + + WasmSignature { + params, + indirect_params, + results, + retptr, + } +} diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index ba5fc0862..8e313dbe4 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -136,26 +136,32 @@ template class ResourceExportBase { /// Wraps the identifier and can be forwarded but not duplicated class ResourceImportBase { public: - static const int32_t invalid = -1; +#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: - int32_t handle; + handle_t handle; public: - ResourceImportBase(int32_t h = invalid) : handle(h) {} + ResourceImportBase(handle_t h = invalid) : handle(h) {} ResourceImportBase(ResourceImportBase &&r) : handle(r.handle) { r.handle = invalid; } ResourceImportBase(ResourceImportBase const &) = delete; - void set_handle(int32_t h) { handle = h; } - int32_t get_handle() const { return handle; } - int32_t into_handle() { - int32_t h = handle; + 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 < 0); + assert(handle == invalid); handle = r.handle; r.handle = invalid; return *this; diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 98285d1c2..1da7b3461 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -631,6 +631,8 @@ impl WorldGenerator for Cpp { 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}"); @@ -961,23 +963,26 @@ impl CppInterfaceGenerator<'_> { // local patching of borrows function needs more complex solution fn patched_wasm_signature(&self, variant: AbiVariant, func: &Function) -> WasmSignature { - let res = self.resolve.wasm_signature(variant, func); - // 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)); - // } - // } - // } - // } - // } - // } - res + if self.gen.opts.symmetric { + abi::wasm_signature_symmetric(self.resolve, variant, func) + } else { + self.resolve.wasm_signature(variant, func) + // 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) @@ -985,7 +990,11 @@ impl CppInterfaceGenerator<'_> { let is_drop = is_special_method(func); let signature = match is_drop { SpecialMethod::ResourceDrop => WasmSignature { - params: vec![WasmType::I32], + params: vec![if self.gen.opts.symmetric { + WasmType::Pointer + } else { + WasmType::I32 + }], results: Vec::new(), indirect_params: false, retptr: false, @@ -1472,15 +1481,28 @@ impl CppInterfaceGenerator<'_> { let name = self.declare_import( &module_name, &func.name, - &[WasmType::I32], + &[if self.gen.opts.symmetric { + WasmType::Pointer + } else { + WasmType::I32 + }], &[], ); - uwriteln!( - self.gen.c_src.src, - " if (handle>=0) {{ + if self.gen.opts.symmetric { + uwriteln!( + self.gen.c_src.src, + " if (handle!=nullptr) {{ {name}(handle); }}" - ); + ); + } else { + uwriteln!( + self.gen.c_src.src, + " if (handle>=0) {{ + {name}(handle); + }}" + ); + } } } }, diff --git a/crates/cpp/tests/native_mesh/component_a/a.cpp b/crates/cpp/tests/native_mesh/component_a/a.cpp index 5a02f8030..2698cd8d9 100644 --- a/crates/cpp/tests/native_mesh/component_a/a.cpp +++ b/crates/cpp/tests/native_mesh/component_a/a.cpp @@ -27,21 +27,21 @@ cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { extern "C" __attribute__((import_module("foo:foo/resources"))) __attribute__((import_name("[resource-drop]r"))) void - fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t); +fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(uint8_t *); extern "C" __attribute__((import_module("foo:foo/resources"))) __attribute__((import_name("[constructor]r"))) -int32_t fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t); +uint8_t *fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t); extern "C" __attribute__((import_module("foo:foo/resources"))) __attribute__((import_name("[method]r.add"))) void - fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(int32_t, int32_t); +fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(uint8_t *, int32_t); extern "C" __attribute__((import_module("foo:foo/resources"))) -__attribute__((import_name("create"))) int32_t +__attribute__((import_name("create"))) uint8_t * fooX3AfooX2FresourcesX00create(); extern "C" __attribute__((import_module("foo:foo/resources"))) __attribute__((import_name("consume"))) void - fooX3AfooX2FresourcesX00consume(int32_t); +fooX3AfooX2FresourcesX00consume(uint8_t *); foo::foo::resources::R::~R() { - if (handle >= 0) { + if (handle != nullptr) { fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(handle); } } diff --git a/crates/cpp/tests/native_mesh/component_a/a_cpp.h b/crates/cpp/tests/native_mesh/component_a/a_cpp.h index 4b588eca6..01899e67e 100644 --- a/crates/cpp/tests/native_mesh/component_a/a_cpp.h +++ b/crates/cpp/tests/native_mesh/component_a/a_cpp.h @@ -1,6 +1,7 @@ // 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 #include #include From c93ae05bd19134cfcd2678a26dd79d7bcc432683 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 11 Jul 2024 22:35:28 +0200 Subject: [PATCH 273/672] symmetric with address seems to work just fine --- crates/cpp/helper-types/wit-guest.h | 23 +++++++++---- .../meshless_resources/component_b/b.cpp | 30 +++++++++-------- .../meshless_resources/component_b/b_cpp.h | 21 +++++++++++- .../component_b/exports-foo-foo-resources-R.h | 33 ++++++++++++++++++- 4 files changed, 85 insertions(+), 22 deletions(-) mode change 120000 => 100644 crates/cpp/tests/meshless_resources/component_b/b_cpp.h mode change 120000 => 100644 crates/cpp/tests/meshless_resources/component_b/exports-foo-foo-resources-R.h diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 8e313dbe4..b6d696ce5 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -104,7 +104,12 @@ template class ResourceExportBase { struct Deregister { void operator()(R *ptr) const { // probably always true because of unique_ptr wrapping, TODO: check - if (ptr->handle >= 0) { +#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); } @@ -112,9 +117,15 @@ template class ResourceExportBase { }; typedef std::unique_ptr Owned; - static const int32_t invalid = -1; +#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 - int32_t handle; + handle_t handle; ResourceExportBase() : handle(R::ResourceNew((R *)this)) {} // because this function is called by the host via Dtor we must not deregister @@ -123,9 +134,9 @@ template class ResourceExportBase { ResourceExportBase(ResourceExportBase &&) = delete; ResourceExportBase &operator=(ResourceExportBase &&b) = delete; ResourceExportBase &operator=(ResourceExportBase const &) = delete; - int32_t get_handle() const { return handle; } - int32_t into_handle() { - int32_t result = handle; + handle_t get_handle() const { return handle; } + handle_t into_handle() { + handle_t result = handle; handle = invalid; return result; } diff --git a/crates/cpp/tests/meshless_resources/component_b/b.cpp b/crates/cpp/tests/meshless_resources/component_b/b.cpp index 87a596408..983506576 100644 --- a/crates/cpp/tests/meshless_resources/component_b/b.cpp +++ b/crates/cpp/tests/meshless_resources/component_b/b.cpp @@ -26,7 +26,7 @@ cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { } //static wit::ResourceTable r_table; -template std::map wit::ResourceTable::resources; +//template std::map wit::ResourceTable::resources; // extern "C" __attribute__((import_module("[export]foo:foo/resources"))) // __attribute__((import_name("[resource-new]r"))) int32_t @@ -44,36 +44,38 @@ template std::map wit::ResourceTable::resources; // (exports::foo::foo::resources::R *)arg0); // } extern "C" - int32_t + uint8_t* fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t arg0) { auto result0 = exports::foo::foo::resources::R::New((uint32_t(arg0))); return result0.release()->handle; } extern "C" void - fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(int32_t arg0, int32_t arg1) { + fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(uint8_t* arg0, int32_t arg1) { exports::foo::foo::resources::R::ResourceRep(arg0) ->Add((uint32_t(arg1))); } -int32_t exports::foo::foo::resources::R::ResourceNew(R *self) { - return wit::ResourceTable::store_resource(std::move(self)); +uint8_t* exports::foo::foo::resources::R::ResourceNew(R *self) { + return (uint8_t*)self; + //wit::ResourceTable::store_resource(std::move(self)); } exports::foo::foo::resources::R * -exports::foo::foo::resources::R::ResourceRep(int32_t id) { - return *wit::ResourceTable::lookup_resource(id); +exports::foo::foo::resources::R::ResourceRep(uint8_t* id) { + return (exports::foo::foo::resources::R *)id; +// *wit::ResourceTable::lookup_resource(id); } -void exports::foo::foo::resources::R::ResourceDrop(int32_t id) { - auto obj = wit::ResourceTable::remove_resource(id); - assert(obj.has_value()); - exports::foo::foo::resources::R::Dtor(*obj); +void exports::foo::foo::resources::R::ResourceDrop(uint8_t* id) { + //auto obj = wit::ResourceTable::remove_resource(id); + //assert(obj.has_value()); + exports::foo::foo::resources::R::Dtor((exports::foo::foo::resources::R*)id); } -extern "C" int32_t +extern "C" uint8_t* fooX3AfooX2FresourcesX00create() { auto result0 = exports::foo::foo::resources::Create(); return result0.release()->handle; } extern "C" void -fooX3AfooX2FresourcesX00consume(int32_t arg0) { +fooX3AfooX2FresourcesX00consume(uint8_t* arg0) { auto obj0 = exports::foo::foo::resources::R::Owned( exports::foo::foo::resources::R::ResourceRep(arg0)); //obj0->into_handle(); @@ -81,7 +83,7 @@ fooX3AfooX2FresourcesX00consume(int32_t arg0) { } extern "C" void -fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(int32_t arg0) { +fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(uint8_t* arg0) { exports::foo::foo::resources::R::ResourceDrop(arg0); } // 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 deleted file mode 120000 index 6a787422e..000000000 --- a/crates/cpp/tests/meshless_resources/component_b/b_cpp.h +++ /dev/null @@ -1 +0,0 @@ -../../native_mesh/component_b/b_cpp.h \ No newline at end of file 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 +#include +// 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 deleted file mode 120000 index de6072793..000000000 --- a/crates/cpp/tests/meshless_resources/component_b/exports-foo-foo-resources-R.h +++ /dev/null @@ -1 +0,0 @@ -../../native_mesh/component_b/exports-foo-foo-resources-R.h \ No newline at end of file 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 +#include +#include +#include +/* 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 { + 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 From f1b8ef8359d291fd3d4fff5e35a5e753b26a0888 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 12 Jul 2024 00:21:32 +0200 Subject: [PATCH 274/672] correct the types of the resource functions --- crates/cpp/src/lib.rs | 42 ++++++++++++++----- .../meshless_resources/component_b/Makefile | 2 +- 2 files changed, 32 insertions(+), 12 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 1da7b3461..d9832a93a 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -420,6 +420,9 @@ impl Cpp { 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}"); } @@ -988,19 +991,20 @@ impl CppInterfaceGenerator<'_> { // print the signature of the guest export (lowered (wasm) function calling into highlevel) fn print_export_signature(&mut self, func: &Function, variant: AbiVariant) -> Vec { 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![if self.gen.opts.symmetric { - WasmType::Pointer - } else { - WasmType::I32 - }], + params: vec![id_type], results: Vec::new(), indirect_params: false, retptr: false, }, SpecialMethod::ResourceRep => WasmSignature { - params: vec![WasmType::I32], + params: vec![id_type], results: vec![WasmType::Pointer], indirect_params: false, retptr: false, @@ -1013,7 +1017,7 @@ impl CppInterfaceGenerator<'_> { }, SpecialMethod::ResourceNew => WasmSignature { params: vec![WasmType::Pointer], - results: vec![WasmType::I32], + results: vec![id_type], indirect_params: false, retptr: false, }, @@ -1146,6 +1150,9 @@ impl CppInterfaceGenerator<'_> { 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) @@ -1219,7 +1226,15 @@ impl CppInterfaceGenerator<'_> { res.implicit_self = true; continue; } - if matches!( + 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, _) @@ -2111,11 +2126,16 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> ); } 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))], - results: Results::Anon(Type::S32), + results: Results::Anon(id_type), docs: Docs::default(), stability: Stability::Unknown, }; @@ -2124,7 +2144,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> let func1 = Function { name: "[resource-rep]".to_string() + &name, kind: FunctionKind::Static(id), - params: vec![("id".into(), Type::S32)], + params: vec![("id".into(), id_type)], results: Results::Anon(Type::Id(id)), docs: Docs::default(), stability: Stability::Unknown, @@ -2134,7 +2154,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> let func2 = Function { name: "[resource-drop]".to_string() + &name, kind: FunctionKind::Static(id), - params: vec![("id".into(), Type::S32)], + params: vec![("id".into(), id_type)], results: Results::Named(vec![]), docs: Docs::default(), stability: Stability::Unknown, diff --git a/crates/cpp/tests/meshless_resources/component_b/Makefile b/crates/cpp/tests/meshless_resources/component_b/Makefile index 34b4630f7..c871371a6 100644 --- a/crates/cpp/tests/meshless_resources/component_b/Makefile +++ b/crates/cpp/tests/meshless_resources/component_b/Makefile @@ -6,7 +6,7 @@ libcomponent_b.a: b.o impl.o ar rcvs $@ $^ bindgen: - ../../../../../target/debug/wit-bindgen cpp ../wit -w b --wasm64 --format + ../../../../../target/debug/wit-bindgen cpp ../wit -w b --symmetric --wasm64 --format clean: -rm *~ *.a *.o From eb09bf17ca2ad10551703c1570d10908f3549122 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 12 Jul 2024 00:22:32 +0200 Subject: [PATCH 275/672] cargo fmt --- crates/cpp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index d9832a93a..77f64fe31 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1151,7 +1151,7 @@ impl CppInterfaceGenerator<'_> { // 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(); + res.result = "uint8_t*".into(); } else // not for ctor nor imported dtor on guest if !matches!(&func.kind, FunctionKind::Constructor(_)) From 3658850c916cf46ed7b89baf0784b6bbcb9a11af Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 12 Jul 2024 09:43:16 +0200 Subject: [PATCH 276/672] replace dtor by res-drop (still wrong contents) --- crates/cpp/src/lib.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 77f64fe31..f18e11e22 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1051,12 +1051,17 @@ impl CppInterfaceGenerator<'_> { 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 func_name = &func.name; let module_prefix = module_name.as_ref().map_or(String::default(), |name| { let mut res = name.clone(); res.push('#'); @@ -1078,8 +1083,8 @@ impl CppInterfaceGenerator<'_> { }); 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), + Some(ref module_name) => make_external_symbol(&module_name, &func_name, symbol_variant), + None => make_external_component(&func_name), }; self.gen.c_src.src.push_str(&export_name); self.gen.c_src.src.push_str("("); @@ -1116,7 +1121,7 @@ impl CppInterfaceGenerator<'_> { if self.gen.opts.host_side() { let signature = wamr::wamr_signature(self.resolve, func); let remember = HostFunction { - wasm_name: func.name.clone(), + wasm_name: func_name.clone(), wamr_signature: signature.to_string(), host_name: export_name.clone(), }; From dae55ed1f2f5a8716bdb2ed468b23413f011638d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 12 Jul 2024 23:56:39 +0200 Subject: [PATCH 277/672] work in progress --- crates/cpp/src/lib.rs | 74 +++++++++++++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index f18e11e22..96e902906 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1490,7 +1490,7 @@ impl CppInterfaceGenerator<'_> { ); } } - LiftLower::LowerArgsLiftResults | LiftLower::Symmetric => { + LiftLower::LowerArgsLiftResults => { if self.gen.opts.host_side() { let namespace = class_namespace(self, func, variant); self.gen.c_src.qualify(&namespace); @@ -1508,21 +1508,40 @@ impl CppInterfaceGenerator<'_> { }], &[], ); - if self.gen.opts.symmetric { - uwriteln!( - self.gen.c_src.src, - " if (handle!=nullptr) {{ + uwriteln!( + self.gen.c_src.src, + " if (handle>=0) {{ {name}(handle); }}" - ); - } else { - uwriteln!( - self.gen.c_src.src, - " if (handle>=0) {{ + ); + } + } + LiftLower::Symmetric => { + let module_name = + self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + if matches!(variant, AbiVariant::GuestExport) { + let namespace = class_namespace(self, func, variant); + self.gen.c_src.qualify(&namespace); + self.gen.c_src.src.push_str("Dtor(("); + self.gen.c_src.qualify(&namespace); + uwriteln!(self.gen.c_src.src, "*){});", func.params.get(0).unwrap().0); + } else { + let name = self.declare_import( + &module_name, + &func.name, + &[if self.gen.opts.symmetric { + WasmType::Pointer + } else { + WasmType::I32 + }], + &[], + ); + uwriteln!( + self.gen.c_src.src, + " if (handle!=nullptr) {{ {name}(handle); }}" - ); - } + ); } } }, @@ -1542,12 +1561,27 @@ impl CppInterfaceGenerator<'_> { ); } else { let classname = class_namespace(self, func, variant).join("::"); - uwriteln!(self.gen.c_src.src, "(({classname}*)arg0)->handle=-1;"); - uwriteln!(self.gen.c_src.src, "{0}::Dtor(({0}*)arg0);", classname); + if self.gen.opts.symmetric { + uwriteln!( + self.gen.c_src.src, + "{0}::ResourceDrop(({0}*)arg0);", + classname + ); + } 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.host_side() { + 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( @@ -1570,7 +1604,15 @@ impl CppInterfaceGenerator<'_> { } } SpecialMethod::ResourceRep => { - if !self.gen.opts.host_side() { + 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( From a166ddb6855e0c18f0d4426de436e1d21281d75e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 13 Jul 2024 00:19:14 +0200 Subject: [PATCH 278/672] fully working symmetric C++ codegen --- crates/cpp/src/lib.rs | 32 +++++----- .../meshless_resources/component_b/b.cpp | 62 +++++++------------ 2 files changed, 37 insertions(+), 57 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 96e902906..7147f1a55 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1501,11 +1501,7 @@ impl CppInterfaceGenerator<'_> { let name = self.declare_import( &module_name, &func.name, - &[if self.gen.opts.symmetric { - WasmType::Pointer - } else { - WasmType::I32 - }], + &[WasmType::I32], &[], ); uwriteln!( @@ -1520,20 +1516,21 @@ impl CppInterfaceGenerator<'_> { let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); if matches!(variant, AbiVariant::GuestExport) { - let namespace = class_namespace(self, func, variant); + 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, "*){});", func.params.get(0).unwrap().0); + uwriteln!( + self.gen.c_src.src, + "{classname}*){});", + func.params.get(0).unwrap().0 + ); } else { let name = self.declare_import( &module_name, &func.name, - &[if self.gen.opts.symmetric { - WasmType::Pointer - } else { - WasmType::I32 - }], + &[WasmType::Pointer], &[], ); uwriteln!( @@ -1564,8 +1561,9 @@ impl CppInterfaceGenerator<'_> { if self.gen.opts.symmetric { uwriteln!( self.gen.c_src.src, - "{0}::ResourceDrop(({0}*)arg0);", - classname + "{}::ResourceDrop(({})arg0);", + classname, + self.gen.opts.ptr_type() ); } else { uwriteln!(self.gen.c_src.src, "(({classname}*)arg0)->handle=-1;"); @@ -2912,9 +2910,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { ); uwriteln!( self.src, - "auto {var} = {tname}::Owned({tname}::ResourceRep({op})); - {var}->into_handle();" + "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})")) } }, diff --git a/crates/cpp/tests/meshless_resources/component_b/b.cpp b/crates/cpp/tests/meshless_resources/component_b/b.cpp index 983506576..501cd2ece 100644 --- a/crates/cpp/tests/meshless_resources/component_b/b.cpp +++ b/crates/cpp/tests/meshless_resources/component_b/b.cpp @@ -25,65 +25,45 @@ cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { return ret; } -//static wit::ResourceTable r_table; -//template std::map wit::ResourceTable::resources; - -// 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" - uint8_t* - fooX3AfooX2FresourcesX00X5BconstructorX5Dr(int32_t arg0) { + __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" - void - fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(uint8_t* arg0, int32_t arg1) { - exports::foo::foo::resources::R::ResourceRep(arg0) - ->Add((uint32_t(arg1))); + __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; - //wit::ResourceTable::store_resource(std::move(self)); +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) { +exports::foo::foo::resources::R::ResourceRep(uint8_t *id) { return (exports::foo::foo::resources::R *)id; -// *wit::ResourceTable::lookup_resource(id); } -void exports::foo::foo::resources::R::ResourceDrop(uint8_t* id) { - //auto obj = wit::ResourceTable::remove_resource(id); - //assert(obj.has_value()); - exports::foo::foo::resources::R::Dtor((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" uint8_t* +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" void -fooX3AfooX2FresourcesX00consume(uint8_t* arg0) { +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)); - //obj0->into_handle(); exports::foo::foo::resources::Consume(std::move(obj0)); } -extern "C" void -fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(uint8_t* arg0) { - exports::foo::foo::resources::R::ResourceDrop(arg0); -} // Component Adapters From e05bcdd03cdf3f58cd9f308760bdb1d54b351a53 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 13 Jul 2024 00:26:52 +0200 Subject: [PATCH 279/672] prepare for pointers as id --- .../meshless_resources/rust_comp_a/src/a.rs | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) 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 index cc95ffc35..aad24d720 100644 --- a/crates/cpp/tests/meshless_resources/rust_comp_a/src/a.rs +++ b/crates/cpp/tests/meshless_resources/rust_comp_a/src/a.rs @@ -21,19 +21,19 @@ pub mod foo { impl R{ #[doc(hidden)] - pub unsafe fn from_handle(handle: u32) -> Self { + pub unsafe fn from_handle(handle: usize) -> Self { Self { handle: _rt::Resource::from_handle(handle), } } #[doc(hidden)] - pub fn take_handle(&self) -> u32 { + pub fn take_handle(&self) -> usize { _rt::Resource::take_handle(&self.handle) } #[doc(hidden)] - pub fn handle(&self) -> u32 { + pub fn handle(&self) -> usize { _rt::Resource::handle(&self.handle) } } @@ -41,12 +41,12 @@ pub mod foo { unsafe impl _rt::WasmResource for R{ #[inline] - unsafe fn drop(_handle: u32) { + 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(_: u32); + fn fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(_: usize); } fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(_handle); @@ -62,10 +62,10 @@ pub mod foo { #[link(wasm_import_module = "foo:foo/resources")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "[constructor]r")] - fn fooX3AfooX2FresourcesX00X5BconstructorX5Dr(_: i32, ) -> i32; + fn fooX3AfooX2FresourcesX00X5BconstructorX5Dr(_: i32, ) -> usize; } let ret = fooX3AfooX2FresourcesX00X5BconstructorX5Dr(_rt::as_i32(&a)); - R::from_handle(ret as u32) + R::from_handle(ret) } } } @@ -77,9 +77,9 @@ pub mod foo { #[link(wasm_import_module = "foo:foo/resources")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "[method]r.add")] - fn fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(_: i32, _: i32, ); + fn fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(_: usize, _: i32, ); } - fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd((self).handle() as i32, _rt::as_i32(&b)); + fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd((self).handle(), _rt::as_i32(&b)); } } } @@ -90,10 +90,10 @@ pub mod foo { #[link(wasm_import_module = "foo:foo/resources")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "create")] - fn fooX3AfooX2FresourcesX00create() -> i32; + fn fooX3AfooX2FresourcesX00create() -> usize; } let ret = fooX3AfooX2FresourcesX00create(); - R::from_handle(ret as u32) + R::from_handle(ret) } } #[allow(unused_unsafe, clippy::all)] @@ -104,9 +104,9 @@ pub mod foo { #[link(wasm_import_module = "foo:foo/resources")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "consume")] - fn fooX3AfooX2FresourcesX00consume(_: i32, ); + fn fooX3AfooX2FresourcesX00consume(_: usize, ); } - fooX3AfooX2FresourcesX00consume((&o).take_handle() as i32); + fooX3AfooX2FresourcesX00consume((&o).take_handle()); } } @@ -119,7 +119,7 @@ mod _rt { use core::fmt; use core::marker; - use core::sync::atomic::{AtomicU32, Ordering::Relaxed}; + use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; /// A type which represents a component model resource, either imported or /// exported into this component. @@ -141,7 +141,7 @@ mod _rt { // // This represents, almost all the time, a valid handle value. When it's // invalid it's stored as `u32::MAX`. - handle: AtomicU32, + handle: AtomicUsize, _marker: marker::PhantomData, } @@ -152,15 +152,15 @@ mod _rt { #[allow(clippy::missing_safety_doc)] pub unsafe trait WasmResource { /// Invokes the `[resource-drop]...` intrinsic. - unsafe fn drop(handle: u32); + unsafe fn drop(handle: usize); } impl Resource { #[doc(hidden)] - pub unsafe fn from_handle(handle: u32) -> Self { - debug_assert!(handle != u32::MAX); + pub unsafe fn from_handle(handle: usize) -> Self { + debug_assert!(handle != 0); Self { - handle: AtomicU32::new(handle), + handle: AtomicUsize::new(handle), _marker: marker::PhantomData, } } @@ -178,12 +178,12 @@ mod _rt { /// `take_handle` should only be exposed internally to generated code, not /// to user code. #[doc(hidden)] - pub fn take_handle(resource: &Resource) -> u32 { - resource.handle.swap(u32::MAX, Relaxed) + pub fn take_handle(resource: &Resource) -> usize { + resource.handle.swap(0, Relaxed) } #[doc(hidden)] - pub fn handle(resource: &Resource) -> u32 { + pub fn handle(resource: &Resource) -> usize { resource.handle.load(Relaxed) } } @@ -202,7 +202,7 @@ mod _rt { match self.handle.load(Relaxed) { // If this handle was "taken" then don't do anything in the // destructor. - u32::MAX => {} + 0 => {} // ... but otherwise do actually destroy it with the imported // component model intrinsic as defined through `T`. From a2c77a83771367e59aeb3dc17ca31cd5137f7856 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 13 Jul 2024 00:55:02 +0200 Subject: [PATCH 280/672] restructure for using pointers, still has a leak in R::drop --- .../meshless_resources/rust_comp_b/src/b.rs | 90 ++++++++++--------- 1 file changed, 47 insertions(+), 43 deletions(-) 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 index 162d8a9cb..c7a73bd84 100644 --- a/crates/cpp/tests/meshless_resources/rust_comp_b/src/b.rs +++ b/crates/cpp/tests/meshless_resources/rust_comp_b/src/b.rs @@ -18,10 +18,10 @@ pub mod exports { use super::super::super::super::_rt; - struct Representation(*mut u8); - unsafe impl Send for Representation {} + // struct Representation(*mut u8); + // unsafe impl Send for Representation {} - static RESOURCE_MAP: Mutex> = Mutex::new(BTreeMap::new()); + // static RESOURCE_MAP: Mutex> = Mutex::new(BTreeMap::new()); //Default::default(); #[derive(Debug)] @@ -65,19 +65,19 @@ pub mod exports { } #[doc(hidden)] - pub unsafe fn from_handle(handle: u32) -> Self { + pub unsafe fn from_handle(handle: usize) -> Self { Self { handle: _rt::Resource::from_handle(handle), } } #[doc(hidden)] - pub fn take_handle(&self) -> u32 { + pub fn take_handle(&self) -> usize { _rt::Resource::take_handle(&self.handle) } #[doc(hidden)] - pub fn handle(&self) -> u32 { + pub fn handle(&self) -> usize { _rt::Resource::handle(&self.handle) } @@ -147,53 +147,55 @@ pub mod exports { unsafe impl _rt::WasmResource for R { #[inline] - unsafe fn drop(handle: u32) { - let rep = RESOURCE_MAP.lock().unwrap().remove(&handle); - assert!(rep.is_some()); + unsafe fn drop(handle: usize) { + // let rep = RESOURCE_MAP.lock().unwrap().remove(&handle); + // assert!(rep.is_some()); + //R::dtor::(handle as *mut u8); + //let _ = _rt::Box::from_raw(handle as *mut _RRep); } } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_constructor_r_cabi(arg0: i32) -> i32 { + pub unsafe fn _export_constructor_r_cabi(arg0: i32) -> usize { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); let result0 = R::new(T::new(arg0 as u32)); - (result0).take_handle() as i32 + (result0).take_handle() } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_method_r_add_cabi(arg0: i32, arg1: i32) { + pub unsafe fn _export_method_r_add_cabi(arg0: usize, arg1: i32) { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); - let r = R::from_handle(arg0 as u32); + let r = R::from_handle(arg0); let a = r.as_ptr::(); r.take_handle(); (*a).as_ref().unwrap().add(arg1 as u32); } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_create_cabi() -> i32 { + pub unsafe fn _export_create_cabi() -> usize { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); let result0 = T::create(); - (result0).take_handle() as i32 + (result0).take_handle() } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_consume_cabi(arg0: i32) { + pub unsafe fn _export_consume_cabi(arg0: usize) { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); - T::consume(R::from_handle(arg0 as u32)); + T::consume(R::from_handle(arg0)); } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_drop_cabi(arg0: i32) { + pub unsafe fn _export_drop_cabi(arg0: usize) { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); - let rep = RESOURCE_MAP.lock().unwrap().remove(&(arg0 as u32)); - R::dtor::(rep.unwrap().0); + //let rep = RESOURCE_MAP.lock().unwrap().remove(&(arg0 as u32)); + R::dtor::(arg0 as *mut u8); } pub trait Guest { @@ -204,23 +206,25 @@ pub mod exports { } pub trait GuestR: 'static { #[doc(hidden)] - unsafe fn _resource_new(val: *mut u8) -> u32 + unsafe fn _resource_new(val: *mut u8) -> usize where Self: Sized, { - let mut lock = RESOURCE_MAP.lock().unwrap(); - let new_index = lock.iter().next_back().map_or(1, |elem| elem.0+1); - let old = lock.insert(new_index, Representation(val)); - assert!(old.is_none()); - new_index + // let mut lock = RESOURCE_MAP.lock().unwrap(); + // let new_index = lock.iter().next_back().map_or(1, |elem| elem.0+1); + // let old = lock.insert(new_index, Representation(val)); + // assert!(old.is_none()); + // new_index + val as usize } #[doc(hidden)] - fn _resource_rep(handle: u32) -> *mut u8 + fn _resource_rep(handle: usize) -> *mut u8 where Self: Sized, { - RESOURCE_MAP.lock().unwrap().get(&handle).unwrap().0 + //RESOURCE_MAP.lock().unwrap().get(&handle).unwrap().0 + handle as *mut u8 } fn new(a: u32) -> Self; @@ -233,27 +237,27 @@ pub mod exports { #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/resources#[constructor]r")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn fooX3AfooX2FresourcesX00X5BconstructorX5Dr(arg0: i32,) -> i32 { + unsafe extern "C" fn fooX3AfooX2FresourcesX00X5BconstructorX5Dr(arg0: i32,) -> usize { $($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 fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(arg0: i32,arg1: i32,) { + unsafe extern "C" fn fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(arg0: usize,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 fooX3AfooX2FresourcesX00create() -> i32 { + unsafe extern "C" fn fooX3AfooX2FresourcesX00create() -> usize { $($path_to_types)*::_export_create_cabi::<$ty>() } #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/resources#consume")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn fooX3AfooX2FresourcesX00consume(arg0: i32,) { + unsafe extern "C" fn fooX3AfooX2FresourcesX00consume(arg0: usize,) { $($path_to_types)*::_export_consume_cabi::<$ty>(arg0) } #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(arg0: i32) { + unsafe extern "C" fn fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(arg0: usize) { $($path_to_types)*::_export_drop_cabi::<<$ty as $($path_to_types)*::Guest>::R>(arg0) } };); @@ -268,7 +272,7 @@ mod _rt { use core::fmt; use core::marker; - use core::sync::atomic::{AtomicU32, Ordering::Relaxed}; + use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; /// A type which represents a component model resource, either imported or /// exported into this component. @@ -290,7 +294,7 @@ mod _rt { // // This represents, almost all the time, a valid handle value. When it's // invalid it's stored as `u32::MAX`. - handle: AtomicU32, + handle: AtomicUsize, _marker: marker::PhantomData, } @@ -301,15 +305,15 @@ mod _rt { #[allow(clippy::missing_safety_doc)] pub unsafe trait WasmResource { /// Invokes the `[resource-drop]...` intrinsic. - unsafe fn drop(handle: u32); + unsafe fn drop(handle: usize); } impl Resource { #[doc(hidden)] - pub unsafe fn from_handle(handle: u32) -> Self { - debug_assert!(handle != u32::MAX); + pub unsafe fn from_handle(handle: usize) -> Self { + debug_assert!(handle != 0); Self { - handle: AtomicU32::new(handle), + handle: AtomicUsize::new(handle), _marker: marker::PhantomData, } } @@ -327,12 +331,12 @@ mod _rt { /// `take_handle` should only be exposed internally to generated code, not /// to user code. #[doc(hidden)] - pub fn take_handle(resource: &Resource) -> u32 { - resource.handle.swap(u32::MAX, Relaxed) + pub fn take_handle(resource: &Resource) -> usize { + resource.handle.swap(0, Relaxed) } #[doc(hidden)] - pub fn handle(resource: &Resource) -> u32 { + pub fn handle(resource: &Resource) -> usize { resource.handle.load(Relaxed) } } @@ -351,7 +355,7 @@ mod _rt { match self.handle.load(Relaxed) { // If this handle was "taken" then don't do anything in the // destructor. - u32::MAX => {} + 0 => {} // ... but otherwise do actually destroy it with the imported // component model intrinsic as defined through `T`. From 7330c92e58a290c46722d1ed747d5531f15e6022 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 13 Jul 2024 11:03:49 +0200 Subject: [PATCH 281/672] this implementation of the dtor feels inelegant but it works fine --- .../meshless_resources/rust_comp_b/src/b.rs | 25 ++++--------------- 1 file changed, 5 insertions(+), 20 deletions(-) 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 index c7a73bd84..9583a1e1c 100644 --- a/crates/cpp/tests/meshless_resources/rust_comp_b/src/b.rs +++ b/crates/cpp/tests/meshless_resources/rust_comp_b/src/b.rs @@ -14,16 +14,8 @@ pub mod exports { #[cfg(target_arch = "wasm32")] static __FORCE_SECTION_REF: fn() = super::super::super::super::__link_custom_section_describing_imports; - use std::{collections::BTreeMap, sync::Mutex}; use super::super::super::super::_rt; - - // struct Representation(*mut u8); - // unsafe impl Send for Representation {} - - // static RESOURCE_MAP: Mutex> = Mutex::new(BTreeMap::new()); - //Default::default(); - #[derive(Debug)] #[repr(transparent)] pub struct R { @@ -148,10 +140,10 @@ pub mod exports { unsafe impl _rt::WasmResource for R { #[inline] unsafe fn drop(handle: usize) { - // let rep = RESOURCE_MAP.lock().unwrap().remove(&handle); - // assert!(rep.is_some()); - //R::dtor::(handle as *mut u8); - //let _ = _rt::Box::from_raw(handle as *mut _RRep); + extern "C" { + fn fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(arg0: usize); + } + unsafe { fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(handle) }; } } @@ -194,7 +186,6 @@ pub mod exports { pub unsafe fn _export_drop_cabi(arg0: usize) { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); - //let rep = RESOURCE_MAP.lock().unwrap().remove(&(arg0 as u32)); R::dtor::(arg0 as *mut u8); } @@ -210,11 +201,6 @@ pub mod exports { where Self: Sized, { - // let mut lock = RESOURCE_MAP.lock().unwrap(); - // let new_index = lock.iter().next_back().map_or(1, |elem| elem.0+1); - // let old = lock.insert(new_index, Representation(val)); - // assert!(old.is_none()); - // new_index val as usize } @@ -223,8 +209,7 @@ pub mod exports { where Self: Sized, { - //RESOURCE_MAP.lock().unwrap().get(&handle).unwrap().0 - handle as *mut u8 + handle as *mut u8 } fn new(a: u32) -> Self; From 7618224451cc5c8f316a5b337e7678fa6acfea2d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 17 Jul 2024 23:46:42 +0200 Subject: [PATCH 282/672] fix crash generating fusion --- crates/core/src/abi.rs | 59 ++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index dde7fd704..bb789ab21 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -801,17 +801,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { || (matches!(self.lift_lower, LiftLower::Symmetric) && matches!(self.variant, AbiVariant::GuestImport)); if language_to_abi { - // bad symmetric hack - let sig = if sig.retptr && matches!(self.lift_lower, LiftLower::Symmetric) { - WasmSignature { - params: Vec::from(&sig.params[0..sig.params.len() - 1]), - results: Vec::from(&sig.params[sig.params.len() - 1..]), - indirect_params: sig.indirect_params, - retptr: false, - } - } else { - sig - }; if !sig.indirect_params { // If the parameters for this function aren't indirect // (there aren't too many) then we simply do a normal lower @@ -856,7 +845,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { // If necessary we may need to prepare a return pointer for // this ABI. - if self.variant == AbiVariant::GuestImport && sig.retptr { + if self.variant == AbiVariant::GuestImport + && sig.retptr + && !matches!(self.lift_lower, LiftLower::Symmetric) + { let (size, align) = self.bindgen.sizes().params(func.results.iter_types()); let ptr = self.bindgen.return_pointer(size, align); self.return_pointer = Some(ptr.clone()); @@ -872,24 +864,24 @@ impl<'a, B: Bindgen> Generator<'a, B> { module_prefix: "", }); - if matches!(self.lift_lower, LiftLower::Symmetric) - && guest_export_needs_post_return(self.resolve, func) - { + if matches!(self.lift_lower, LiftLower::Symmetric) && sig.retptr { let ptr = self.stack.pop().unwrap(); self.read_results_from_memory(&func.results, ptr.clone(), 0); - let post_sig = WasmSignature { - params: vec![WasmType::Pointer], - results: Vec::new(), - indirect_params: false, - retptr: false, - }; - // TODO: can we get this name from somewhere? - self.stack.push(ptr); - self.emit(&Instruction::CallWasm { - name: &func.name, - sig: &post_sig, - module_prefix: "cabi_post_", - }); + if guest_export_needs_post_return(self.resolve, func) { + let post_sig = WasmSignature { + params: vec![WasmType::Pointer], + results: Vec::new(), + indirect_params: false, + retptr: false, + }; + // TODO: can we get this name from somewhere? + self.stack.push(ptr); + self.emit(&Instruction::CallWasm { + name: &func.name, + sig: &post_sig, + module_prefix: "cabi_post_", + }); + } } else if !sig.retptr { // With no return pointer in use we can simply lift the // result(s) of the function from the result of the core @@ -2023,7 +2015,7 @@ fn push_flat_symmetric(resolve: &Resolve, ty: &Type, vec: &mut Vec) { // another hack pub fn wasm_signature_symmetric( resolve: &Resolve, - variant: AbiVariant, + _variant: AbiVariant, func: &Function, ) -> WasmSignature { const MAX_FLAT_PARAMS: usize = 16; @@ -2055,14 +2047,7 @@ pub fn wasm_signature_symmetric( if results.len() > MAX_FLAT_RESULTS { retptr = true; results.truncate(0); - match variant { - AbiVariant::GuestImport => { - params.push(WasmType::Pointer); - } - AbiVariant::GuestExport => { - results.push(WasmType::Pointer); - } - } + results.push(WasmType::Pointer); } WasmSignature { From 5887d5064c48748c4954f2ef1b0f063eac3b1ca2 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 18 Jul 2024 20:35:37 +0200 Subject: [PATCH 283/672] second thought: Allocate ret_buf by the caller, first half --- crates/cpp/DESIGN.md | 31 +++++++++++ .../component_a/the_world.cpp | 30 +++++------ .../component_a/the_world_cpp.h | 1 + .../component_b/the_world.cpp | 30 +++++------ .../rust_comp_a/src/the_world.rs | 52 +++++++------------ 5 files changed, 78 insertions(+), 66 deletions(-) diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md index f114ab4e4..da1ecce29 100644 --- a/crates/cpp/DESIGN.md +++ b/crates/cpp/DESIGN.md @@ -51,3 +51,34 @@ Complex (non-POD) struct elements on the host will need exec_env to decode or co [^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. + +# Symmetric ABI + +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 following 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 + + - (change) Returned strings and lists become views, valid until a + cabi_post on the ret_area ptr which was passed to the first call. + This is mostly a concession to functional safety, avoiding all + allocations in the hot path. Caller provided buffers or custom realloc + would solve this in a different way. + + - The imported resource ABI is used also for exporting + with one modification: + + Resource IDs are usize, so you can optimize the resource table away. diff --git a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp index 540f7501f..8e96d24e6 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp @@ -29,14 +29,14 @@ 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"))) uint8_t * -fooX3AfooX2FstringsX00b(); +__attribute__((import_name("b"))) void +fooX3AfooX2FstringsX00b(uint8_t *); extern "C" __attribute__((import_module("cabi_post_foo:foo/strings"))) __attribute__((import_name("b"))) void cabi_post_fooX3AfooX2FstringsX00b(uint8_t *); extern "C" __attribute__((import_module("foo:foo/strings"))) -__attribute__((import_name("c"))) uint8_t * -fooX3AfooX2FstringsX00c(uint8_t *, size_t, uint8_t *, size_t); +__attribute__((import_name("c"))) void +fooX3AfooX2FstringsX00c(uint8_t *, size_t, uint8_t *, size_t, uint8_t *); extern "C" __attribute__((import_module("cabi_post_foo:foo/strings"))) __attribute__((import_name("c"))) void cabi_post_fooX3AfooX2FstringsX00c(uint8_t *); @@ -47,7 +47,9 @@ void comp_a::foo::foo::strings::A(std::string_view x) { fooX3AfooX2FstringsX00a(ptr0, len0); } wit::string comp_a::foo::foo::strings::B() { - auto ret = fooX3AfooX2FstringsX00b(); + size_t ret_area[2]; + uint8_t *ret = (uint8_t *)(&ret_area); + fooX3AfooX2FstringsX00b(ret); auto len0 = *((size_t *)(ret + 8)); auto string0 = wit::string::from_view( @@ -64,7 +66,9 @@ wit::string comp_a::foo::foo::strings::C(std::string_view a, auto const &vec1 = b; auto ptr1 = (uint8_t *)(vec1.data()); auto len1 = (size_t)(vec1.size()); - auto ret = fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1); + size_t ret_area[2]; + uint8_t *ret = (uint8_t *)(&ret_area); + fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ret); auto len2 = *((size_t *)(ret + 8)); auto string2 = wit::string::from_view( @@ -82,11 +86,9 @@ a_fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { comp_a::exports::foo::foo::strings::A(std::move(string0)); } -extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) uint8_t * -a_fooX3AfooX2FstringsX00b() { +extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) void +a_fooX3AfooX2FstringsX00b(uint8_t *ptr1) { auto result0 = comp_a::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()); @@ -94,7 +96,6 @@ a_fooX3AfooX2FstringsX00b() { *((size_t *)(ptr1 + 8)) = len2; *((uint8_t **)(ptr1 + 0)) = ptr2; - return ptr1; } extern "C" __attribute__((__weak__, @@ -104,9 +105,9 @@ extern "C" wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); } } -extern "C" __attribute__((__export_name__("foo:foo/strings#c"))) uint8_t * +extern "C" __attribute__((__export_name__("foo:foo/strings#c"))) void a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, - size_t arg3) { + size_t arg3, uint8_t* ptr3) { auto len0 = arg1; auto string0 = @@ -119,8 +120,6 @@ a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, auto result2 = comp_a::exports::foo::foo::strings::C(std::move(string0), std::move(string1)); - 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()); @@ -128,7 +127,6 @@ a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, *((size_t *)(ptr3 + 8)) = len4; *((uint8_t **)(ptr3 + 0)) = ptr4; - return ptr3; } extern "C" __attribute__((__weak__, 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 index f41bd95d4..0955df5f7 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world_cpp.h +++ b/crates/cpp/tests/meshless_strings/component_a/the_world_cpp.h @@ -1,6 +1,7 @@ // 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 #include #include diff --git a/crates/cpp/tests/meshless_strings/component_b/the_world.cpp b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp index 83de2e361..7fad04fc6 100644 --- a/crates/cpp/tests/meshless_strings/component_b/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp @@ -29,14 +29,14 @@ 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"))) uint8_t * -a_fooX3AfooX2FstringsX00b(); +__attribute__((import_name("b"))) void +a_fooX3AfooX2FstringsX00b(uint8_t *); extern "C" __attribute__((import_module("cabi_post_foo:foo/strings"))) __attribute__((import_name("b"))) void a_cabi_post_fooX3AfooX2FstringsX00b(uint8_t *); extern "C" __attribute__((import_module("foo:foo/strings"))) -__attribute__((import_name("c"))) uint8_t * -a_fooX3AfooX2FstringsX00c(uint8_t *, size_t, uint8_t *, size_t); +__attribute__((import_name("c"))) void +a_fooX3AfooX2FstringsX00c(uint8_t *, size_t, uint8_t *, size_t, uint8_t *); extern "C" __attribute__((import_module("cabi_post_foo:foo/strings"))) __attribute__((import_name("c"))) void a_cabi_post_fooX3AfooX2FstringsX00c(uint8_t *); @@ -47,7 +47,9 @@ void foo::foo::strings::A(std::string_view x) { a_fooX3AfooX2FstringsX00a(ptr0, len0); } wit::string foo::foo::strings::B() { - auto ret = a_fooX3AfooX2FstringsX00b(); + size_t ret_area[2]; + uint8_t *ret = (uint8_t *)(&ret_area); + a_fooX3AfooX2FstringsX00b(ret); auto len0 = *((size_t *)(ret + 8)); auto string0 = wit::string::from_view( @@ -63,7 +65,9 @@ wit::string foo::foo::strings::C(std::string_view a, std::string_view b) { auto const &vec1 = b; auto ptr1 = (uint8_t *)(vec1.data()); auto len1 = (size_t)(vec1.size()); - auto ret = a_fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1); + size_t ret_area[2]; + uint8_t *ret = (uint8_t *)(&ret_area); + a_fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ret); auto len2 = *((size_t *)(ret + 8)); auto string2 = wit::string::from_view( @@ -81,11 +85,9 @@ fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { exports::foo::foo::strings::A(std::move(string0)); } -extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) uint8_t * -fooX3AfooX2FstringsX00b() { +extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) void +fooX3AfooX2FstringsX00b(uint8_t *ptr1) { 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()); @@ -93,7 +95,6 @@ fooX3AfooX2FstringsX00b() { *((size_t *)(ptr1 + 8)) = len2; *((uint8_t **)(ptr1 + 0)) = ptr2; - return ptr1; } extern "C" __attribute__((__weak__, @@ -103,9 +104,9 @@ extern "C" wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); } } -extern "C" __attribute__((__export_name__("foo:foo/strings#c"))) uint8_t * +extern "C" __attribute__((__export_name__("foo:foo/strings#c"))) void fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, - size_t arg3) { + size_t arg3, uint8_t *ptr3) { auto len0 = arg1; auto string0 = @@ -118,8 +119,6 @@ fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, auto result2 = exports::foo::foo::strings::C(std::move(string0), std::move(string1)); - 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()); @@ -127,7 +126,6 @@ fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, *((size_t *)(ptr3 + 8)) = len4; *((uint8_t **)(ptr3 + 0)) = ptr4; - return ptr3; } extern "C" __attribute__((__weak__, 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 index d9aba158a..e9fa6cdc2 100644 --- 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 @@ -30,19 +30,21 @@ pub mod foo { #[allow(unused_unsafe, clippy::all)] pub fn b() -> _rt::String{ unsafe { + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit::; 16]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 16]); + let ptr0 = ret_area.0.as_mut_ptr().cast::(); #[link(wasm_import_module = "foo:foo/strings")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "b")] - fn fooX3AfooX2FstringsX00b() -> *mut u8; + fn fooX3AfooX2FstringsX00b(_: *mut u8); #[cfg_attr(target_arch = "wasm32", link_name = "cabi_post_b")] fn cabi_post_fooX3AfooX2FstringsX00b(_: *mut u8); } - let ptr0 = fooX3AfooX2FstringsX00b(); + fooX3AfooX2FstringsX00b(ptr0); let l1 = *ptr0.add(0).cast::<*mut u8>(); let l2 = *ptr0.add(8).cast::(); let len3 = l2; - // let bytes3 = _rt::Vec::from_raw_parts(l1.cast(), len3, len3); - // _rt::string_lift(bytes3) let res = String::from(std::str::from_utf8(std::slice::from_raw_parts(l1, len3)).unwrap()); cabi_post_fooX3AfooX2FstringsX00b(ptr0); res @@ -51,30 +53,28 @@ pub mod foo { #[allow(unused_unsafe, clippy::all)] pub fn c(a: &str,b: &str,) -> _rt::String{ unsafe { - // #[repr(align(4))] - // struct RetArea([::core::mem::MaybeUninit::; 8]); - // let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]); + #[repr(align(8))] + struct RetArea([::core::mem::MaybeUninit::; 16]); + let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 16]); let vec0 = a; let ptr0 = vec0.as_ptr().cast::(); let len0 = vec0.len(); let vec1 = b; let ptr1 = vec1.as_ptr().cast::(); let len1 = vec1.len(); - // let ptr2 = ret_area.0.as_mut_ptr().cast::(); + let ptr2 = ret_area.0.as_mut_ptr().cast::(); #[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; + fn fooX3AfooX2FstringsX00c(_: *mut u8, _: usize, _: *mut u8, _: usize, _: *mut u8); #[cfg_attr(target_arch = "wasm32", link_name = "cabi_post_c")] fn cabi_post_fooX3AfooX2FstringsX00c(_: *mut u8); } - let ptr2 = fooX3AfooX2FstringsX00c(ptr0.cast_mut(), len0, ptr1.cast_mut(), len1); + fooX3AfooX2FstringsX00c(ptr0.cast_mut(), len0, ptr1.cast_mut(), len1, ptr2); let l3 = *ptr2.add(0).cast::<*mut u8>(); let l4 = *ptr2.add(8).cast::(); let len5 = l4; let res = String::from(std::str::from_utf8(std::slice::from_raw_parts(l3, len5)).unwrap()); - // let bytes5 = _rt::Vec::from_raw_parts(l3.cast(), len5, len5); - // _rt::string_lift(bytes5) cabi_post_fooX3AfooX2FstringsX00c(ptr2); res } @@ -107,16 +107,14 @@ pub mod exports { } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_b_cabi() -> *mut u8 {#[cfg(target_arch="wasm32")] + pub unsafe fn _export_b_cabi(ptr1: *mut u8) {#[cfg(target_arch="wasm32")] _rt::run_ctors_once();let result0 = T::b(); - let ptr1 = _RET_AREA.0.as_mut_ptr().cast::(); let vec2 = (result0.into_bytes()).into_boxed_slice(); let ptr2 = vec2.as_ptr().cast::(); let len2 = vec2.len(); ::core::mem::forget(vec2); *ptr1.add(8).cast::() = len2; *ptr1.add(0).cast::<*mut u8>() = ptr2.cast_mut(); - ptr1 } #[doc(hidden)] #[allow(non_snake_case)] @@ -127,21 +125,18 @@ pub mod exports { } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_c_cabi(arg0: *mut u8,arg1: usize,arg2: *mut u8,arg3: usize,) -> *mut u8 {#[cfg(target_arch="wasm32")] + pub unsafe fn _export_c_cabi(arg0: *mut u8,arg1: usize,arg2: *mut u8,arg3: usize, ptr3: *mut u8) {#[cfg(target_arch="wasm32")] _rt::run_ctors_once();let len0 = arg1; let bytes0 = String::from(std::str::from_utf8(std::slice::from_raw_parts(arg0, len0)).unwrap()); - //_rt::Vec::from_raw_parts(arg0.cast(), len0, len0); let len1 = arg3; let bytes1 = String::from(std::str::from_utf8(std::slice::from_raw_parts(arg2, len1)).unwrap()); let result2 = T::c(bytes0, bytes1); - let ptr3 = _RET_AREA.0.as_mut_ptr().cast::(); let vec4 = (result2.into_bytes()).into_boxed_slice(); let ptr4 = vec4.as_ptr().cast::(); let len4 = vec4.len(); ::core::mem::forget(vec4); *ptr3.add(8).cast::() = len4; *ptr3.add(0).cast::<*mut u8>() = ptr4.cast_mut(); - ptr3 } #[doc(hidden)] #[allow(non_snake_case)] @@ -167,8 +162,8 @@ pub mod exports { } #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/strings#b")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn a_fooX3AfooX2FstringsX00b() -> *mut u8 { - $($path_to_types)*::_export_b_cabi::<$ty>() + unsafe extern "C" fn a_fooX3AfooX2FstringsX00b(ptr: *mut u8) { + $($path_to_types)*::_export_b_cabi::<$ty>(ptr) } #[cfg_attr(target_arch = "wasm32", export_name = "cabi_post_foo:foo/strings#b")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] @@ -177,8 +172,8 @@ pub mod exports { } #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/strings#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,) -> *mut u8 { - $($path_to_types)*::_export_c_cabi::<$ty>(arg0, arg1, arg2, arg3) + 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) } #[cfg_attr(target_arch = "wasm32", export_name = "cabi_post_foo:foo/strings#c")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] @@ -189,9 +184,6 @@ pub mod exports { } #[doc(hidden)] pub(crate) use __export_foo_foo_strings_cabi; - #[repr(align(8))] - struct _RetArea([::core::mem::MaybeUninit::; 16]); - static mut _RET_AREA: _RetArea = _RetArea([::core::mem::MaybeUninit::uninit(); 16]); } @@ -200,14 +192,6 @@ pub mod exports { } mod _rt { pub use alloc_crate::string::String; - // pub use alloc_crate::vec::Vec; - // pub unsafe fn string_lift(bytes: Vec) -> 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() { From fc24a0d47d3f509c136697b1dc55c7cdbfc2c3b9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 18 Jul 2024 23:24:37 +0200 Subject: [PATCH 284/672] revised symmetric code generation --- crates/core/src/abi.rs | 25 ++++++---- crates/cpp/src/lib.rs | 9 ++++ .../component_a/the_world.cpp | 48 +++++++++---------- 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index bb789ab21..74d53a0e7 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -845,15 +845,18 @@ impl<'a, B: Bindgen> Generator<'a, B> { // If necessary we may need to prepare a return pointer for // this ABI. - if self.variant == AbiVariant::GuestImport - && sig.retptr - && !matches!(self.lift_lower, LiftLower::Symmetric) + let retptr = if sig.retptr + && (matches!(self.lift_lower, LiftLower::Symmetric) + || self.variant == AbiVariant::GuestImport) { let (size, align) = self.bindgen.sizes().params(func.results.iter_types()); let ptr = self.bindgen.return_pointer(size, align); self.return_pointer = Some(ptr.clone()); - self.stack.push(ptr); - } + self.stack.push(ptr.clone()); + Some(ptr) + } else { + None + }; // Now that all the wasm args are prepared we can call the // actual wasm function. @@ -865,8 +868,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); if matches!(self.lift_lower, LiftLower::Symmetric) && sig.retptr { - let ptr = self.stack.pop().unwrap(); - self.read_results_from_memory(&func.results, ptr.clone(), 0); + //let ptr = self.stack.pop().unwrap(); + self.read_results_from_memory(&func.results, retptr.clone().unwrap(), 0); if guest_export_needs_post_return(self.resolve, func) { let post_sig = WasmSignature { params: vec![WasmType::Pointer], @@ -875,7 +878,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { retptr: false, }; // TODO: can we get this name from somewhere? - self.stack.push(ptr); + self.stack.push(retptr.unwrap()); self.emit(&Instruction::CallWasm { name: &func.name, sig: &post_sig, @@ -996,7 +999,9 @@ impl<'a, B: Bindgen> Generator<'a, B> { let (size, align) = self.bindgen.sizes().params(func.results.iter_types()); let ptr = self.bindgen.return_pointer(size, align); self.write_params_to_memory(func.results.iter_types(), ptr.clone(), 0); - self.stack.push(ptr); + if !matches!(self.lift_lower, LiftLower::Symmetric) { + self.stack.push(ptr); + } } } } @@ -2047,7 +2052,7 @@ pub fn wasm_signature_symmetric( if results.len() > MAX_FLAT_RESULTS { retptr = true; results.truncate(0); - results.push(WasmType::Pointer); + params.push(WasmType::Pointer); } WasmSignature { diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7147f1a55..cea6fff29 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1744,6 +1744,15 @@ impl CppInterfaceGenerator<'_> { ); 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()); diff --git a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp index 8e96d24e6..a37d87a89 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp @@ -47,16 +47,16 @@ void comp_a::foo::foo::strings::A(std::string_view x) { fooX3AfooX2FstringsX00a(ptr0, len0); } wit::string comp_a::foo::foo::strings::B() { - size_t ret_area[2]; - uint8_t *ret = (uint8_t *)(&ret_area); - fooX3AfooX2FstringsX00b(ret); - auto len0 = *((size_t *)(ret + 8)); + uint64_t ret_area[2]; + uint8_t *ptr0 = (uint8_t *)(&ret_area); + fooX3AfooX2FstringsX00b(ptr0); + auto len1 = *((size_t *)(ptr0 + 8)); - auto string0 = wit::string::from_view( - std::string_view((char const *)(*((uint8_t **)(ret + 0))), len0)); + auto string1 = wit::string::from_view( + std::string_view((char const *)(*((uint8_t **)(ptr0 + 0))), len1)); - cabi_post_fooX3AfooX2FstringsX00b(ret); - return string0; + cabi_post_fooX3AfooX2FstringsX00b(ptr0); + return string1; } wit::string comp_a::foo::foo::strings::C(std::string_view a, std::string_view b) { @@ -66,16 +66,16 @@ wit::string comp_a::foo::foo::strings::C(std::string_view a, auto const &vec1 = b; auto ptr1 = (uint8_t *)(vec1.data()); auto len1 = (size_t)(vec1.size()); - size_t ret_area[2]; - uint8_t *ret = (uint8_t *)(&ret_area); - fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ret); - auto len2 = *((size_t *)(ret + 8)); + 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 string2 = wit::string::from_view( - std::string_view((char const *)(*((uint8_t **)(ret + 0))), len2)); + auto string3 = wit::string::from_view( + std::string_view((char const *)(*((uint8_t **)(ptr2 + 0))), len3)); - cabi_post_fooX3AfooX2FstringsX00c(ret); - return string2; + cabi_post_fooX3AfooX2FstringsX00c(ptr2); + return string3; } extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void a_fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { @@ -100,14 +100,14 @@ a_fooX3AfooX2FstringsX00b(uint8_t *ptr1) { extern "C" __attribute__((__weak__, __export_name__("cabi_post_fooX3AfooX2FstringsX00b"))) void - a_cabi_post_fooX3AfooX2FstringsX00b(uint8_t *arg0) { - if ((*((size_t *)(arg0 + 8))) > 0) { - wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); + a_cabi_post_fooX3AfooX2FstringsX00b(uint8_t *retptr) { + if ((*((size_t *)(retptr + 8))) > 0) { + wit::string::drop_raw((void *)(*((uint8_t **)(retptr + 0)))); } } 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* ptr3) { +a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3, + uint8_t *ptr3) { auto len0 = arg1; auto string0 = @@ -131,9 +131,9 @@ a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, extern "C" __attribute__((__weak__, __export_name__("cabi_post_fooX3AfooX2FstringsX00c"))) void - a_cabi_post_fooX3AfooX2FstringsX00c(uint8_t *arg0) { - if ((*((size_t *)(arg0 + 8))) > 0) { - wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); + a_cabi_post_fooX3AfooX2FstringsX00c(uint8_t *retptr) { + if ((*((size_t *)(retptr + 8))) > 0) { + wit::string::drop_raw((void *)(*((uint8_t **)(retptr + 0)))); } } From b33aafea57191398573fbef10b9697a9bb1f5641 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 18 Jul 2024 23:37:25 +0200 Subject: [PATCH 285/672] generated code is now correct again --- crates/core/src/abi.rs | 30 +++++++++---------- .../component_a/the_world.cpp | 24 +++++++-------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 74d53a0e7..7d3c883e7 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -975,33 +975,31 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.lower(ty); } } else { - match self.variant { + if 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 => { - self.emit(&Instruction::GetArg { - nth: sig.params.len() - 1, - }); - let ptr = self.stack.pop().unwrap(); - self.write_params_to_memory(func.results.iter_types(), ptr, 0); - } - + self.emit(&Instruction::GetArg { + nth: sig.params.len() - 1, + }); + let ptr = self.stack.pop().unwrap(); + self.write_params_to_memory(func.results.iter_types(), ptr, 0); + } else { // For a guest import this is a function defined in // wasm, so we're returning a pointer where the // 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 => { - let (size, align) = self.bindgen.sizes().params(func.results.iter_types()); - let ptr = self.bindgen.return_pointer(size, align); - self.write_params_to_memory(func.results.iter_types(), ptr.clone(), 0); - if !matches!(self.lift_lower, LiftLower::Symmetric) { - self.stack.push(ptr); - } + let (size, align) = self.bindgen.sizes().params(func.results.iter_types()); + let ptr = self.bindgen.return_pointer(size, align); + self.write_params_to_memory(func.results.iter_types(), ptr.clone(), 0); + if !matches!(self.lift_lower, LiftLower::Symmetric) { + self.stack.push(ptr); } } } diff --git a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp index a37d87a89..e9c6df974 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp @@ -87,15 +87,15 @@ a_fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { comp_a::exports::foo::foo::strings::A(std::move(string0)); } extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) void -a_fooX3AfooX2FstringsX00b(uint8_t *ptr1) { +a_fooX3AfooX2FstringsX00b(uint8_t *arg0) { auto result0 = comp_a::exports::foo::foo::strings::B(); - auto const &vec2 = result0; - auto ptr2 = (uint8_t *)(vec2.data()); - auto len2 = (size_t)(vec2.size()); + auto const &vec1 = result0; + auto ptr1 = (uint8_t *)(vec1.data()); + auto len1 = (size_t)(vec1.size()); result0.leak(); - *((size_t *)(ptr1 + 8)) = len2; - *((uint8_t **)(ptr1 + 0)) = ptr2; + *((size_t *)(arg0 + 8)) = len1; + *((uint8_t **)(arg0 + 0)) = ptr1; } extern "C" __attribute__((__weak__, @@ -107,7 +107,7 @@ extern "C" } 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 *ptr3) { + uint8_t *arg4) { auto len0 = arg1; auto string0 = @@ -120,13 +120,13 @@ a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3 auto result2 = comp_a::exports::foo::foo::strings::C(std::move(string0), std::move(string1)); - auto const &vec4 = result2; - auto ptr4 = (uint8_t *)(vec4.data()); - auto len4 = (size_t)(vec4.size()); + auto const &vec3 = result2; + auto ptr3 = (uint8_t *)(vec3.data()); + auto len3 = (size_t)(vec3.size()); result2.leak(); - *((size_t *)(ptr3 + 8)) = len4; - *((uint8_t **)(ptr3 + 0)) = ptr4; + *((size_t *)(arg4 + 8)) = len3; + *((uint8_t **)(arg4 + 0)) = ptr3; } extern "C" __attribute__((__weak__, From f4ce621bfe976c4c28b7042b3765584e59f2f7c6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 20 Jul 2024 21:53:54 +0200 Subject: [PATCH 286/672] fix tests and return value --- crates/core/src/abi.rs | 5 +++++ crates/cpp/src/lib.rs | 35 +++++++++++++++++++---------------- crates/cpp/tests/codegen.rs | 11 +++++++++++ 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index e736bb03c..a9553d8b8 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -1028,6 +1028,11 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&Instruction::GuestDeallocate { size, align }); } } + + self.emit(&Instruction::Return { + func, + amt: func.results.len(), + }); } false => { if let (AbiVariant::GuestImport, true) = (self.variant, self.async_) { diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 28ecc763c..e1fb9d595 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -3614,22 +3614,25 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.store(ptr_type, *offset, operands) } abi::Instruction::LengthStore { offset } => self.store("size_t", *offset, operands), - abi::Instruction::FutureLower { payload, ty } => todo!(), - abi::Instruction::FutureLift { payload, ty } => todo!(), - abi::Instruction::StreamLower { payload, ty } => todo!(), - abi::Instruction::StreamLift { payload, ty } => todo!(), - abi::Instruction::ErrorLower { ty } => todo!(), - abi::Instruction::ErrorLift { ty } => todo!(), - abi::Instruction::AsyncMalloc { size, align } => todo!(), - abi::Instruction::AsyncCallWasm { name, size, align } => todo!(), - abi::Instruction::AsyncCallStart { - name, - params, - results, - } => todo!(), - abi::Instruction::AsyncPostCallInterface { func } => todo!(), - abi::Instruction::AsyncCallReturn { name, params } => todo!(), - abi::Instruction::Flush { amt } => todo!(), + abi::Instruction::FutureLower { .. } => todo!(), + abi::Instruction::FutureLift { .. } => todo!(), + abi::Instruction::StreamLower { .. } => todo!(), + abi::Instruction::StreamLift { .. } => todo!(), + abi::Instruction::ErrorLower { .. } => todo!(), + abi::Instruction::ErrorLift { .. } => todo!(), + abi::Instruction::AsyncMalloc { .. } => todo!(), + abi::Instruction::AsyncCallWasm { .. } => todo!(), + abi::Instruction::AsyncCallStart { .. } => 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); + } + } } } diff --git a/crates/cpp/tests/codegen.rs b/crates/cpp/tests/codegen.rs index d10cd291d..91f876738 100644 --- a/crates/cpp/tests/codegen.rs +++ b/crates/cpp/tests/codegen.rs @@ -10,6 +10,7 @@ macro_rules! codegen_test { if [ "go_params", "guest-name", + "import-func", "import-and-export-resource", "import-and-export-resource-alias", "interface-has-go-keyword", @@ -17,6 +18,16 @@ macro_rules! codegen_test { "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", From 6fb498d43ae302a4d033bceb71c2933c146923b9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 20 Jul 2024 22:07:28 +0200 Subject: [PATCH 287/672] make async argument optional --- crates/rust/src/lib.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 6c6789203..6e40a3cec 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -137,7 +137,15 @@ pub enum AsyncConfig { #[cfg(feature = "clap")] fn parse_async(s: &str) -> Result { _ = s; - Err("todo: parse `AsyncConfig`".into()) + //Err("todo: parse `AsyncConfig`".into()) + Ok(AsyncConfig::None) +} + +#[cfg(feature = "clap")] +impl std::fmt::Display for AsyncConfig { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } } #[derive(Default, Debug, Clone)] @@ -260,7 +268,7 @@ pub struct Opts { pub symmetric: bool, /// Determines which functions to lift or lower `async`, if any. - #[cfg_attr(feature = "clap", arg(long = "async", value_parser = parse_async))] + #[cfg_attr(feature = "clap", arg(long = "async", value_parser = parse_async, default_value_t = Default::default()))] pub async_: AsyncConfig, } From aa46b69e9fdaa617bf5380fae9011c80bc791307 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 21 Jul 2024 17:46:31 +0200 Subject: [PATCH 288/672] initial support for automatic pointer width code --- Cargo.lock | 34 +-- Cargo.toml | 20 +- crates/c/src/lib.rs | 47 ++-- crates/core/src/abi.rs | 253 ++++++++++-------- crates/cpp/src/lib.rs | 52 ++-- .../component_a/the_world.cpp | 10 +- .../native_strings/rust/src/the_world.rs | 18 +- crates/cpp/tests/native_strings/the_world.cpp | 26 +- .../tests/native_strings/the_world_native.cpp | 18 +- crates/csharp/src/lib.rs | 11 +- crates/rust/src/bindgen.rs | 10 +- crates/rust/src/interface.rs | 27 +- crates/rust/src/lib.rs | 10 +- crates/teavm-java/src/lib.rs | 12 +- 14 files changed, 311 insertions(+), 237 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 59cc7fdb9..4404c42bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1439,7 +1439,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-core", "wit-component", "wit-parser 0.214.0", @@ -1727,7 +1727,7 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=async#2323160c8d0b2054f95740e337d8ba2aaeccfe5b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#474e13a0666fdd43b374021582351f50b07a1eb2" dependencies = [ "leb128", "wasmparser 0.214.0", @@ -1736,7 +1736,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=async#2323160c8d0b2054f95740e337d8ba2aaeccfe5b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#474e13a0666fdd43b374021582351f50b07a1eb2" dependencies = [ "anyhow", "indexmap", @@ -1744,7 +1744,7 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasmparser 0.214.0", ] @@ -1765,7 +1765,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=async#2323160c8d0b2054f95740e337d8ba2aaeccfe5b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#474e13a0666fdd43b374021582351f50b07a1eb2" dependencies = [ "ahash", "bitflags", @@ -2094,13 +2094,13 @@ dependencies = [ [[package]] name = "wast" version = "214.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=async#2323160c8d0b2054f95740e337d8ba2aaeccfe5b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#474e13a0666fdd43b374021582351f50b07a1eb2" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2115,9 +2115,9 @@ dependencies = [ [[package]] name = "wat" version = "1.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=async#2323160c8d0b2054f95740e337d8ba2aaeccfe5b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#474e13a0666fdd43b374021582351f50b07a1eb2" dependencies = [ - "wast 214.0.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", + "wast 214.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2395,7 +2395,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -2410,7 +2410,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasmparser 0.214.0", "wasmtime", "wasmtime-wasi", @@ -2444,7 +2444,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", @@ -2460,7 +2460,7 @@ dependencies = [ "heck 0.5.0", "indexmap", "test-helpers", - "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wasmparser 0.214.0", "wit-bindgen-core", @@ -2547,7 +2547,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=async#2323160c8d0b2054f95740e337d8ba2aaeccfe5b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#474e13a0666fdd43b374021582351f50b07a1eb2" dependencies = [ "anyhow", "bitflags", @@ -2556,10 +2556,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", + "wasm-encoder 0.214.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wasmparser 0.214.0", - "wat 1.214.0 (git+https://github.com/cpetig/wasm-tools?branch=async)", + "wat 1.214.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-parser 0.214.0", ] @@ -2584,7 +2584,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=async#2323160c8d0b2054f95740e337d8ba2aaeccfe5b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#474e13a0666fdd43b374021582351f50b07a1eb2" dependencies = [ "anyhow", "id-arena", diff --git a/Cargo.toml b/Cargo.toml index cfd7327dd..81c1375a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,11 +32,11 @@ indexmap = "2.0.0" prettyplease = "0.2.20" syn = { version = "2.0", features = ["printing"] } -wasmparser = { git = "https://github.com/cpetig/wasm-tools", branch = "async" } -wasm-encoder = { git = "https://github.com/cpetig/wasm-tools", branch = "async" } -wasm-metadata = { git = "https://github.com/cpetig/wasm-tools", branch = "async" } -wit-parser = { git = "https://github.com/cpetig/wasm-tools", branch = "async" } -wit-component = { git = "https://github.com/cpetig/wasm-tools", branch = "async" } +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" } wit-bindgen-core = { path = 'crates/core', version = '0.28.0' } wit-bindgen-c = { path = 'crates/c', version = '0.28.0' } @@ -73,9 +73,9 @@ default = [ 'c', 'rust', 'markdown', - 'teavm-java', - 'go', - 'csharp', +# 'teavm-java', +# 'go', +# 'csharp', 'cpp', 'bridge', ] @@ -105,3 +105,7 @@ wasm-encoder = { workspace = true } #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/crates/c/src/lib.rs b/crates/c/src/lib.rs index f3d41cb6e..630561a7d 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -18,8 +18,8 @@ struct C { opts: Opts, h_includes: Vec, c_includes: Vec, - return_pointer_area_size: usize, - return_pointer_area_align: usize, + return_pointer_area_size: ArchitectureSize, + return_pointer_area_align: Alignment, names: Ns, needs_string: bool, needs_union_int32_float: bool, @@ -462,7 +462,7 @@ impl WorldGenerator for C { // Declare a statically-allocated return area, if needed. We only do // this for export bindings, because import bindings allocate their // return-area on the stack. - if self.return_pointer_area_size > 0 { + if !self.return_pointer_area_size.is_empty() { // Automatic indentation avoided due to `extern "C" {` declaration uwrite!( c_str, @@ -470,8 +470,8 @@ impl WorldGenerator for C { __attribute__((__aligned__({}))) static uint8_t RET_AREA[{}]; ", - self.return_pointer_area_align, - self.return_pointer_area_size, + self.return_pointer_area_align.align_wasm32(), + self.return_pointer_area_size.size_wasm32(), ); } c_str.push_str(&self.src.c_adapters); @@ -1767,7 +1767,7 @@ impl InterfaceGenerator<'_> { .. } = f; - if import_return_pointer_area_size > 0 { + if !import_return_pointer_area_size.is_empty() { self.src.c_adapters(&format!( "\ __attribute__((__aligned__({import_return_pointer_area_align}))) @@ -2118,8 +2118,8 @@ struct FunctionBindgen<'a, 'b> { params: Vec, wasm_return: Option, ret_store_cnt: usize, - import_return_pointer_area_size: usize, - import_return_pointer_area_align: usize, + import_return_pointer_area_size: ArchitectureSize, + import_return_pointer_area_align: Alignment, /// Borrows observed during lifting an export, that will need to be dropped when the guest /// function exits. @@ -2147,8 +2147,8 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { params: Vec::new(), wasm_return: None, ret_store_cnt: 0, - import_return_pointer_area_size: 0, - import_return_pointer_area_align: 0, + import_return_pointer_area_size: Default::default(), + import_return_pointer_area_align: Default::default(), borrow_decls: Default::default(), borrows: Vec::new(), } @@ -2161,17 +2161,34 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { self.src.push_str(";\n"); } - fn load(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec) { - results.push(format!("*(({}*) ({} + {}))", ty, operands[0], offset)); + fn load( + &mut self, + ty: &str, + offset: ArchitectureSize, + operands: &[String], + results: &mut Vec, + ) { + results.push(format!( + "*(({}*) ({} + {}))", + ty, + operands[0], + offset.format("sizeof(void*)") + )); } - fn load_ext(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec) { + fn load_ext( + &mut self, + ty: &str, + offset: ArchitectureSize, + operands: &[String], + results: &mut Vec, + ) { self.load(ty, offset, operands, results); let result = results.pop().unwrap(); results.push(format!("(int32_t) {}", result)); } - fn store(&mut self, ty: &str, offset: i32, operands: &[String]) { + fn store(&mut self, ty: &str, offset: ArchitectureSize, operands: &[String]) { uwriteln!( self.src, "*(({}*)({} + {})) = {};", @@ -2227,7 +2244,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.blocks.push((src.into(), mem::take(operands))); } - fn return_pointer(&mut self, size: usize, align: usize) -> String { + fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> String { let ptr = self.locals.tmp("ptr"); // Use a stack-based return area for imports, because exports need diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index a9553d8b8..2ca0da791 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -1,7 +1,8 @@ pub use wit_parser::abi::{AbiVariant, WasmSignature, WasmType}; use wit_parser::{ - Enum, Flags, FlagsRepr, Function, Handle, Int, Record, Resolve, Result_, Results, SizeAlign, - Tuple, Type, TypeDefKind, TypeId, Variant, + align_to_arch, Alignment, ArchitectureSize, ElementInfo, Enum, Flags, FlagsRepr, Function, + Handle, Int, Record, Resolve, Result_, Results, SizeAlign, Tuple, Type, TypeDefKind, TypeId, + Variant, }; // Helper macro for defining instructions without having to have tons of @@ -86,67 +87,67 @@ def_instruction! { /// Pops a pointer from the stack and loads a little-endian `i32` from /// it, using the specified constant offset. - I32Load { offset: i32 } : [1] => [1], + I32Load { offset: ArchitectureSize } : [1] => [1], /// Pops a pointer from the stack and loads a little-endian `i8` from /// it, using the specified constant offset. The value loaded is the /// zero-extended to 32-bits - I32Load8U { offset: i32 } : [1] => [1], + I32Load8U { offset: ArchitectureSize } : [1] => [1], /// Pops a pointer from the stack and loads a little-endian `i8` from /// it, using the specified constant offset. The value loaded is the /// sign-extended to 32-bits - I32Load8S { offset: i32 } : [1] => [1], + I32Load8S { offset: ArchitectureSize } : [1] => [1], /// Pops a pointer from the stack and loads a little-endian `i16` from /// it, using the specified constant offset. The value loaded is the /// zero-extended to 32-bits - I32Load16U { offset: i32 } : [1] => [1], + I32Load16U { offset: ArchitectureSize } : [1] => [1], /// Pops a pointer from the stack and loads a little-endian `i16` from /// it, using the specified constant offset. The value loaded is the /// sign-extended to 32-bits - I32Load16S { offset: i32 } : [1] => [1], + I32Load16S { offset: ArchitectureSize } : [1] => [1], /// Pops a pointer from the stack and loads a little-endian `i64` from /// it, using the specified constant offset. - I64Load { offset: i32 } : [1] => [1], + I64Load { offset: ArchitectureSize } : [1] => [1], /// Pops a pointer from the stack and loads a little-endian `f32` from /// it, using the specified constant offset. - F32Load { offset: i32 } : [1] => [1], + F32Load { offset: ArchitectureSize } : [1] => [1], /// Pops a pointer from the stack and loads a little-endian `f64` from /// it, using the specified constant offset. - F64Load { offset: i32 } : [1] => [1], + F64Load { offset: ArchitectureSize } : [1] => [1], /// Like `I32Load` or `I64Load`, but for loading pointer values. - PointerLoad { offset: i32 } : [1] => [1], + PointerLoad { offset: ArchitectureSize } : [1] => [1], /// Like `I32Load` or `I64Load`, but for loading array length values. - LengthLoad { offset: i32 } : [1] => [1], + LengthLoad { offset: ArchitectureSize } : [1] => [1], /// Pops a pointer from the stack and then an `i32` value. /// Stores the value in little-endian at the pointer specified plus the /// constant `offset`. - I32Store { offset: i32 } : [2] => [0], + I32Store { offset: ArchitectureSize } : [2] => [0], /// Pops a pointer from the stack and then an `i32` value. /// Stores the low 8 bits of the value in little-endian at the pointer /// specified plus the constant `offset`. - I32Store8 { offset: i32 } : [2] => [0], + I32Store8 { offset: ArchitectureSize } : [2] => [0], /// Pops a pointer from the stack and then an `i32` value. /// Stores the low 16 bits of the value in little-endian at the pointer /// specified plus the constant `offset`. - I32Store16 { offset: i32 } : [2] => [0], + I32Store16 { offset: ArchitectureSize } : [2] => [0], /// Pops a pointer from the stack and then an `i64` value. /// Stores the value in little-endian at the pointer specified plus the /// constant `offset`. - I64Store { offset: i32 } : [2] => [0], + I64Store { offset: ArchitectureSize } : [2] => [0], /// Pops a pointer from the stack and then an `f32` value. /// Stores the value in little-endian at the pointer specified plus the /// constant `offset`. - F32Store { offset: i32 } : [2] => [0], + F32Store { offset: ArchitectureSize } : [2] => [0], /// Pops a pointer from the stack and then an `f64` value. /// Stores the value in little-endian at the pointer specified plus the /// constant `offset`. - F64Store { offset: i32 } : [2] => [0], + F64Store { offset: ArchitectureSize } : [2] => [0], /// Like `I32Store` or `I64Store`, but for storing pointer values. - PointerStore { offset: i32 } : [2] => [0], + PointerStore { offset: ArchitectureSize } : [2] => [0], /// Like `I32Store` or `I64Store`, but for storing array length values. - LengthStore { offset: i32 } : [2] => [0], + LengthStore { offset: ArchitectureSize } : [2] => [0], // Scalar lifting/lowering @@ -518,8 +519,8 @@ def_instruction! { /// Pushes the returned pointer onto the stack. Malloc { realloc: &'static str, - size: usize, - align: usize, + size: ArchitectureSize, + align: Alignment, } : [0] => [1], /// Used exclusively for guest-code generation this indicates that @@ -528,8 +529,8 @@ def_instruction! { /// /// This will pop a pointer from the stack and push nothing. GuestDeallocate { - size: usize, - align: usize, + size: ArchitectureSize, + align: Alignment, } : [1] => [0], /// Used exclusively for guest-code generation this indicates that @@ -556,9 +557,9 @@ def_instruction! { blocks: usize, } : [1] => [0], - AsyncMalloc { size: usize, align: usize } : [0] => [1], + AsyncMalloc { size: ArchitectureSize, align: Alignment } : [0] => [1], - AsyncCallWasm { name: &'a str, size: usize, align: usize } : [3] => [0], + AsyncCallWasm { name: &'a str, size: ArchitectureSize, align: Alignment } : [3] => [0], AsyncCallStart { name: &'a str, @@ -679,7 +680,7 @@ pub trait Bindgen { /// Gets a operand reference to the return pointer area. /// /// The provided size and alignment is for the function's return type. - fn return_pointer(&mut self, size: usize, align: usize) -> Self::Operand; + fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> Self::Operand; /// Enters a new block of code to generate code for. /// @@ -869,11 +870,11 @@ impl<'a, B: Bindgen> Generator<'a, B> { } let lower_to_memory = |self_: &mut Self, ptr: B::Operand| { - let mut offset = 0usize; + let mut offset = ArchitectureSize::default(); for (nth, (_, ty)) in func.params.iter().enumerate() { self_.emit(&Instruction::GetArg { nth }); - offset = align_to(offset, self_.bindgen.sizes().align(ty)); - self_.write_to_memory(ty, ptr.clone(), offset as i32); + offset = align_to_arch(offset, self_.bindgen.sizes().align(ty)); + self_.write_to_memory(ty, ptr.clone(), offset); offset += self_.bindgen.sizes().size(ty); } @@ -881,7 +882,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { }; let params_size_align = if self.async_ { - let (size, align) = self + let ElementInfo { size, align } = self .bindgen .sizes() .record(func.params.iter().map(|(_, ty)| ty)); @@ -902,7 +903,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { // ... otherwise if parameters are indirect space is // allocated from them and each argument is lowered // individually into memory. - let (size, align) = self + let ElementInfo { size, align } = self .bindgen .sizes() .record(func.params.iter().map(|t| &t.1)); @@ -932,48 +933,56 @@ impl<'a, B: Bindgen> Generator<'a, B> { let mut retptr = None; // If necessary we may need to prepare a return pointer for // this ABI. - let dealloc_size_align = if let Some((params_size, params_align)) = - params_size_align - { - let (size, align) = self.bindgen.sizes().record(func.results.iter_types()); - self.emit(&Instruction::AsyncMalloc { size, align }); - let ptr = self.stack.pop().unwrap(); - self.return_pointer = Some(ptr.clone()); - self.stack.push(ptr); - // ... and another return pointer for the call handle - self.stack.push(self.bindgen.return_pointer(4, 4)); - - assert_eq!(self.stack.len(), 3); - self.emit(&Instruction::AsyncCallWasm { - name: &format!("[async]{}", func.name), - size: params_size, - align: params_align, - }); - Some((size, align)) - } else { - if (self.variant == AbiVariant::GuestImport - || matches!(self.lift_lower, LiftLower::Symmetric)) - && sig.retptr - { - let (size, align) = self.bindgen.sizes().params(func.results.iter_types()); - let ptr = self.bindgen.return_pointer(size, align); + let dealloc_size_align = + if let Some((params_size, params_align)) = params_size_align { + let ElementInfo { size, align } = + self.bindgen.sizes().record(func.results.iter_types()); + self.emit(&Instruction::AsyncMalloc { size, align }); + let ptr = self.stack.pop().unwrap(); self.return_pointer = Some(ptr.clone()); - self.stack.push(ptr.clone()); - retptr = Some(ptr); - } + self.stack.push(ptr); + // ... and another return pointer for the call handle + self.stack.push( + self.bindgen + .return_pointer(Alignment::Pointer.into(), Alignment::Pointer), + ); + + assert_eq!(self.stack.len(), 3); + self.emit(&Instruction::AsyncCallWasm { + name: &format!("[async]{}", func.name), + size: params_size, + align: params_align, + }); + Some((size, align)) + } else { + if (self.variant == AbiVariant::GuestImport + || matches!(self.lift_lower, LiftLower::Symmetric)) + && sig.retptr + { + let ElementInfo { size, align } = + self.bindgen.sizes().params(func.results.iter_types()); + let ptr = self.bindgen.return_pointer(size, align); + self.return_pointer = Some(ptr.clone()); + self.stack.push(ptr.clone()); + retptr = Some(ptr); + } - assert_eq!(self.stack.len(), sig.params.len()); - self.emit(&Instruction::CallWasm { - name: &func.name, - sig: &sig, - module_prefix: Default::default(), - }); - None - }; + assert_eq!(self.stack.len(), sig.params.len()); + self.emit(&Instruction::CallWasm { + name: &func.name, + sig: &sig, + module_prefix: Default::default(), + }); + None + }; if matches!(self.lift_lower, LiftLower::Symmetric) && sig.retptr { //let ptr = self.stack.pop().unwrap(); - self.read_results_from_memory(&func.results, retptr.clone().unwrap(), 0); + self.read_results_from_memory( + &func.results, + retptr.clone().unwrap(), + Default::default(), + ); if guest_export_needs_post_return(self.resolve, func) { let post_sig = WasmSignature { params: vec![WasmType::Pointer], @@ -1018,7 +1027,11 @@ impl<'a, B: Bindgen> Generator<'a, B> { } }; - self.read_results_from_memory(&func.results, ptr.clone(), 0); + self.read_results_from_memory( + &func.results, + ptr.clone(), + ArchitectureSize::default(), + ); self.emit(&Instruction::Flush { amt: func.results.len(), }); @@ -1040,11 +1053,11 @@ impl<'a, B: Bindgen> Generator<'a, B> { } let read_from_memory = |self_: &mut Self| { - let mut offset = 0usize; + let mut offset = ArchitectureSize::default(); let ptr = self_.stack.pop().unwrap(); for (_, ty) in func.params.iter() { - offset = align_to(offset, self_.bindgen.sizes().align(ty)); - self_.read_from_memory(ty, ptr.clone(), offset as i32); + offset = align_to_arch(offset, self_.bindgen.sizes().align(ty)); + self_.read_from_memory(ty, ptr.clone(), offset); offset += self_.bindgen.sizes().size(ty); } }; @@ -1058,7 +1071,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { let name = &format!("[async-start]{}", func.name); if params.len() > MAX_FLAT_RESULTS { - let (size, align) = self + let ElementInfo { size, align } = self .bindgen .sizes() .params(func.params.iter().map(|(_, ty)| ty)); @@ -1127,7 +1140,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { // deallocate it. if let AbiVariant::GuestExport = self.variant { if sig.indirect_params && !self.async_ { - let (size, align) = self + let ElementInfo { size, align } = self .bindgen .sizes() .record(func.params.iter().map(|t| &t.1)); @@ -1162,7 +1175,11 @@ impl<'a, B: Bindgen> Generator<'a, B> { nth: sig.params.len() - 1, }); let ptr = self.stack.pop().unwrap(); - self.write_params_to_memory(func.results.iter_types(), ptr, 0); + self.write_params_to_memory( + func.results.iter_types(), + ptr, + Default::default(), + ); } // For a guest import this is a function defined in @@ -1171,10 +1188,14 @@ impl<'a, B: Bindgen> Generator<'a, B> { // (statically) and then write the result into that // memory, returning the pointer at the end. false => { - let (size, align) = + let ElementInfo { size, align } = self.bindgen.sizes().params(func.results.iter_types()); let ptr = self.bindgen.return_pointer(size, align); - self.write_params_to_memory(func.results.iter_types(), ptr.clone(), 0); + self.write_params_to_memory( + func.results.iter_types(), + ptr.clone(), + Default::default(), + ); if !matches!(self.lift_lower, LiftLower::Symmetric) { self.stack.push(ptr); } @@ -1233,7 +1254,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { .sizes() .field_offsets(func.results.iter_types()) { - let offset = i32::try_from(offset).unwrap(); + //let offset = i32::try_from(offset).unwrap(); self.deallocate(ty, addr.clone(), offset); } self.emit(&Instruction::Return { func, amt: 0 }); @@ -1319,7 +1340,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&IterElem { element }); self.emit(&IterBasePointer); let addr = self.stack.pop().unwrap(); - self.write_to_memory(element, addr, 0); + self.write_to_memory(element, addr, Default::default()); self.finish_block(0); self.emit(&ListLower { element, realloc }); } @@ -1516,7 +1537,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.push_block(); self.emit(&IterBasePointer); let addr = self.stack.pop().unwrap(); - self.read_from_memory(element, addr, 0); + self.read_from_memory(element, addr, Default::default()); self.finish_block(1); self.emit(&ListLift { element, ty: id }); } @@ -1661,7 +1682,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - fn write_to_memory(&mut self, ty: &Type, addr: B::Operand, offset: i32) { + fn write_to_memory(&mut self, ty: &Type, addr: B::Operand, offset: ArchitectureSize) { use Instruction::*; match *ty { @@ -1721,7 +1742,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { for i in (0..n).rev() { self.stack.push(addr.clone()); self.emit(&I32Store { - offset: offset + (i as i32) * 4, + offset: offset.add_bytes(i * 4), }); } } @@ -1785,20 +1806,19 @@ impl<'a, B: Bindgen> Generator<'a, B> { &mut self, params: impl IntoIterator + ExactSizeIterator, addr: B::Operand, - offset: i32, + offset: ArchitectureSize, ) { self.write_fields_to_memory(params, addr, offset); } fn write_variant_arms_to_memory<'b>( &mut self, - offset: i32, + offset: ArchitectureSize, addr: B::Operand, tag: Int, cases: impl IntoIterator> + Clone, ) { - let payload_offset = - offset + (self.bindgen.sizes().payload_offset(tag, cases.clone()) as i32); + let payload_offset = offset + (self.bindgen.sizes().payload_offset(tag, cases.clone())); for (i, ty) in cases.into_iter().enumerate() { self.push_block(); self.emit(&Instruction::VariantPayloadName); @@ -1814,14 +1834,14 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - fn write_list_to_memory(&mut self, ty: &Type, addr: B::Operand, offset: i32) { + fn write_list_to_memory(&mut self, ty: &Type, addr: B::Operand, offset: ArchitectureSize) { // After lowering the list there's two i32 values on the stack // which we write into memory, writing the pointer into the low address // and the length into the high address. self.lower(ty); self.stack.push(addr.clone()); self.emit(&Instruction::LengthStore { - offset: offset + self.bindgen.sizes().align(ty) as i32, + offset: offset + self.bindgen.sizes().align(ty).into(), }); self.stack.push(addr); self.emit(&Instruction::PointerStore { offset }); @@ -1831,7 +1851,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { &mut self, tys: impl IntoIterator + ExactSizeIterator, addr: B::Operand, - offset: i32, + offset: ArchitectureSize, ) { let fields = self .stack @@ -1845,7 +1865,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { .zip(fields) { self.stack.push(op); - self.write_to_memory(ty, addr.clone(), offset + (field_offset as i32)); + self.write_to_memory(ty, addr.clone(), offset + (field_offset)); } } @@ -1855,7 +1875,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(instr); } - fn read_from_memory(&mut self, ty: &Type, addr: B::Operand, offset: i32) { + fn read_from_memory(&mut self, ty: &Type, addr: B::Operand, offset: ArchitectureSize) { use Instruction::*; match *ty { @@ -1915,7 +1935,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { for i in 0..n { self.stack.push(addr.clone()); self.emit(&I32Load { - offset: offset + (i as i32) * 4, + offset: offset.add_bytes(i * 4), }); } } @@ -1967,21 +1987,25 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - fn read_results_from_memory(&mut self, results: &Results, addr: B::Operand, offset: i32) { + fn read_results_from_memory( + &mut self, + results: &Results, + addr: B::Operand, + offset: ArchitectureSize, + ) { self.read_fields_from_memory(results.iter_types(), addr, offset) } fn read_variant_arms_from_memory<'b>( &mut self, - offset: i32, + offset: ArchitectureSize, addr: B::Operand, tag: Int, cases: impl IntoIterator> + Clone, ) { self.stack.push(addr.clone()); self.load_intrepr(offset, tag); - let payload_offset = - offset + (self.bindgen.sizes().payload_offset(tag, cases.clone()) as i32); + let payload_offset = offset + (self.bindgen.sizes().payload_offset(tag, cases.clone())); for ty in cases { self.push_block(); if let Some(ty) = ty { @@ -1991,14 +2015,14 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - fn read_list_from_memory(&mut self, ty: &Type, addr: B::Operand, offset: i32) { + fn read_list_from_memory(&mut self, ty: &Type, addr: B::Operand, offset: ArchitectureSize) { // Read the pointer/len and then perform the standard lifting // proceses. self.stack.push(addr.clone()); self.emit(&Instruction::PointerLoad { offset }); self.stack.push(addr); self.emit(&Instruction::LengthLoad { - offset: offset + self.bindgen.sizes().align(ty) as i32, + offset: offset + self.bindgen.sizes().align(ty).into(), }); self.lift(ty); } @@ -2007,10 +2031,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { &mut self, tys: impl IntoIterator, addr: B::Operand, - offset: i32, + offset: ArchitectureSize, ) { for (field_offset, ty) in self.bindgen.sizes().field_offsets(tys).iter() { - self.read_from_memory(ty, addr.clone(), offset + (*field_offset as i32)); + self.read_from_memory(ty, addr.clone(), offset + (*field_offset)); } } @@ -2020,7 +2044,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.lift(ty); } - fn load_intrepr(&mut self, offset: i32, repr: Int) { + fn load_intrepr(&mut self, offset: ArchitectureSize, repr: Int) { self.emit(&match repr { Int::U64 => Instruction::I64Load { offset }, Int::U32 => Instruction::I32Load { offset }, @@ -2029,7 +2053,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); } - fn store_intrepr(&mut self, offset: i32, repr: Int) { + fn store_intrepr(&mut self, offset: ArchitectureSize, repr: Int) { self.emit(&match repr { Int::U64 => Instruction::I64Store { offset }, Int::U32 => Instruction::I32Store { offset }, @@ -2038,7 +2062,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); } - fn deallocate(&mut self, ty: &Type, addr: B::Operand, offset: i32) { + fn deallocate(&mut self, ty: &Type, addr: B::Operand, offset: ArchitectureSize) { use Instruction::*; // No need to execute any instructions if this type itself doesn't @@ -2053,7 +2077,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&Instruction::PointerLoad { offset }); self.stack.push(addr); self.emit(&Instruction::LengthLoad { - offset: offset + self.bindgen.sizes().align(ty) as i32, + offset: offset + self.bindgen.sizes().align(ty).into(), }); self.emit(&Instruction::GuestDeallocateString); } @@ -2079,13 +2103,13 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&Instruction::PointerLoad { offset }); self.stack.push(addr); self.emit(&Instruction::LengthLoad { - offset: offset + self.bindgen.sizes().align(ty) as i32, + offset: offset + self.bindgen.sizes().align(ty).into(), }); self.push_block(); self.emit(&IterBasePointer); let elemaddr = self.stack.pop().unwrap(); - self.deallocate(element, elemaddr, 0); + self.deallocate(element, elemaddr, Default::default()); self.finish_block(0); self.emit(&Instruction::GuestDeallocateList { element }); @@ -2147,15 +2171,14 @@ impl<'a, B: Bindgen> Generator<'a, B> { fn deallocate_variant<'b>( &mut self, - offset: i32, + offset: ArchitectureSize, addr: B::Operand, tag: Int, cases: impl IntoIterator> + Clone, ) { self.stack.push(addr.clone()); self.load_intrepr(offset, tag); - let payload_offset = - offset + (self.bindgen.sizes().payload_offset(tag, cases.clone()) as i32); + let payload_offset = offset + (self.bindgen.sizes().payload_offset(tag, cases.clone())); for ty in cases { self.push_block(); if let Some(ty) = ty { @@ -2165,9 +2188,9 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - fn deallocate_fields(&mut self, tys: &[Type], addr: B::Operand, offset: i32) { + fn deallocate_fields(&mut self, tys: &[Type], addr: B::Operand, offset: ArchitectureSize) { for (field_offset, ty) in self.bindgen.sizes().field_offsets(tys) { - self.deallocate(ty, addr.clone(), offset + (field_offset as i32)); + self.deallocate(ty, addr.clone(), offset + (field_offset)); } } } @@ -2228,9 +2251,9 @@ fn cast(from: WasmType, to: WasmType) -> Bitcast { } } -fn align_to(val: usize, align: usize) -> usize { - (val + align - 1) & !(align - 1) -} +// fn align_to(val: usize, align: usize) -> usize { +// (val + align - 1) & !(align - 1) +// } fn push_flat_symmetric(resolve: &Resolve, ty: &Type, vec: &mut Vec) { if let Type::Id(id) = ty { diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index e1fb9d595..650498244 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -11,8 +11,9 @@ use wit_bindgen_core::{ abi::{self, AbiVariant, Bindgen, Bitcast, LiftLower, WasmSignature, WasmType}, make_external_component, make_external_symbol, uwrite, uwriteln, wit_parser::{ - AddressSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, Resolve, Results, - SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, + Alignment, ArchitectureSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, + Resolve, Results, SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, + WorldKey, }, Files, InterfaceGenerator, Source, WorldGenerator, }; @@ -280,11 +281,7 @@ impl Cpp { in_guest_import: bool, wasm_import_module: Option, ) -> CppInterfaceGenerator<'a> { - let mut sizes = SizeAlign::new(if self.opts.wasm64 { - AddressSize::Wasm64 - } else { - AddressSize::Wasm32 - }); + let mut sizes = SizeAlign::new(); sizes.fill(resolve); CppInterfaceGenerator { @@ -2494,7 +2491,13 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } - fn load(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec) { + fn load( + &mut self, + ty: &str, + offset: ArchitectureSize, + operands: &[String], + results: &mut Vec, + ) { 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)); } else { @@ -2502,13 +2505,19 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } - fn load_ext(&mut self, ty: &str, offset: i32, operands: &[String], results: &mut Vec) { + fn load_ext( + &mut self, + ty: &str, + offset: ArchitectureSize, + operands: &[String], + results: &mut Vec, + ) { self.load(ty, offset, operands, results); let result = results.pop().unwrap(); results.push(format!("(int32_t) ({})", result)); } - fn store(&mut self, ty: &str, offset: i32, operands: &[String]) { + fn store(&mut self, ty: &str, offset: ArchitectureSize, operands: &[String]) { if self.gen.gen.opts.host { uwriteln!( self.src, @@ -3636,22 +3645,29 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } - fn return_pointer(&mut self, size: usize, align: usize) -> Self::Operand { + fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> Self::Operand { let tmp = self.tmp(); - let elems = (size + (align - 1)) / align; + let size_string = size.format("sizeof(void*)"); + //let elems = (size + (align - 1)) / align; let tp = match align { - 1 => "uint8_t", - 2 => "uint16_t", - 4 => "uint32_t", - 8 => "uint64_t", - _ => todo!(), + 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[{elems}];"); + uwriteln!( + self.src, + "{static_var}{tp} ret_area[({size_string}+sizeof({tp})-1)/sizeof({tp})];" + ); uwriteln!( self.src, "{} ptr{tmp} = ({0})(&ret_area);", diff --git a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp index e9c6df974..f62c9e1f6 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp @@ -78,7 +78,7 @@ wit::string comp_a::foo::foo::strings::C(std::string_view a, return string3; } extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void -a_fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { +fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { auto len0 = arg1; auto string0 = @@ -87,7 +87,7 @@ a_fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { comp_a::exports::foo::foo::strings::A(std::move(string0)); } extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) void -a_fooX3AfooX2FstringsX00b(uint8_t *arg0) { +fooX3AfooX2FstringsX00b(uint8_t *arg0) { auto result0 = comp_a::exports::foo::foo::strings::B(); auto const &vec1 = result0; auto ptr1 = (uint8_t *)(vec1.data()); @@ -100,13 +100,13 @@ a_fooX3AfooX2FstringsX00b(uint8_t *arg0) { extern "C" __attribute__((__weak__, __export_name__("cabi_post_fooX3AfooX2FstringsX00b"))) void - a_cabi_post_fooX3AfooX2FstringsX00b(uint8_t *retptr) { + cabi_post_fooX3AfooX2FstringsX00b(uint8_t *retptr) { if ((*((size_t *)(retptr + 8))) > 0) { wit::string::drop_raw((void *)(*((uint8_t **)(retptr + 0)))); } } extern "C" __attribute__((__export_name__("foo:foo/strings#c"))) void -a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3, +fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3, uint8_t *arg4) { auto len0 = arg1; @@ -131,7 +131,7 @@ a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3 extern "C" __attribute__((__weak__, __export_name__("cabi_post_fooX3AfooX2FstringsX00c"))) void - a_cabi_post_fooX3AfooX2FstringsX00c(uint8_t *retptr) { + cabi_post_fooX3AfooX2FstringsX00c(uint8_t *retptr) { if ((*((size_t *)(retptr + 8))) > 0) { wit::string::drop_raw((void *)(*((uint8_t **)(retptr + 0)))); } diff --git a/crates/cpp/tests/native_strings/rust/src/the_world.rs b/crates/cpp/tests/native_strings/rust/src/the_world.rs index 5c05d404b..18fde4703 100644 --- a/crates/cpp/tests/native_strings/rust/src/the_world.rs +++ b/crates/cpp/tests/native_strings/rust/src/the_world.rs @@ -1,4 +1,4 @@ -// Generated by `wit-bindgen` 0.24.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.28.0. DO NOT EDIT! // Options used: #[allow(dead_code)] pub mod foo { @@ -12,7 +12,7 @@ pub mod foo { 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,){ + pub fn a(x: &str,) -> (){ unsafe { let vec0 = x; let ptr0 = vec0.as_ptr().cast::(); @@ -43,7 +43,8 @@ pub mod foo { let l2 = *ptr0.add(8).cast::(); let len3 = l2; let bytes3 = _rt::Vec::from_raw_parts(l1.cast(), len3, len3); - _rt::string_lift(bytes3) + let result4 = _rt::string_lift(bytes3); + result4 } } #[allow(unused_unsafe, clippy::all)] @@ -69,7 +70,8 @@ pub mod foo { let l4 = *ptr2.add(8).cast::(); let len5 = l4; let bytes5 = _rt::Vec::from_raw_parts(l3.cast(), len5, len5); - _rt::string_lift(bytes5) + let result6 = _rt::string_lift(bytes5); + result6 } } @@ -142,7 +144,7 @@ pub mod exports { _rt::cabi_dealloc(l0, l1, 1); } pub trait Guest { - fn a(x: _rt::String,); + fn a(x: _rt::String,) -> (); fn b() -> _rt::String; fn c(a: _rt::String,b: _rt::String,) -> _rt::String; } @@ -209,7 +211,7 @@ mod _rt { return; } let layout = alloc::Layout::from_size_align_unchecked(size, align); - alloc::dealloc(ptr as *mut u8, layout); + alloc::dealloc(ptr, layout); } extern crate alloc as alloc_crate; pub use alloc_crate::alloc; @@ -244,7 +246,7 @@ macro_rules! __export_the_world_impl { 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"] +#[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\ @@ -253,7 +255,7 @@ A\x04\x01B\x06\x01@\x01\x01xs\x01\0\x04\0\x01a\x01\0\x01@\0\0s\x04\0\x01b\x01\x0 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.207.0\x10wit-bindgen-rust\x060.24.0"; +processed-by\x02\x0dwit-component\x070.214.0\x10wit-bindgen-rust\x060.28.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/cpp/tests/native_strings/the_world.cpp b/crates/cpp/tests/native_strings/the_world.cpp index 9ab7b9c5c..03c455465 100644 --- a/crates/cpp/tests/native_strings/the_world.cpp +++ b/crates/cpp/tests/native_strings/the_world.cpp @@ -41,12 +41,13 @@ void foo::foo::strings::A(std::string_view x) { fooX3AfooX2FstringsX00a(ptr0, len0); } wit::string foo::foo::strings::B() { - size_t ret_area[2]; + uint64_t ret_area[2]; uint8_t *ptr0 = (uint8_t *)(&ret_area); fooX3AfooX2FstringsX00b(ptr0); - auto len1 = *((size_t *)(ptr0 + sizeof(size_t))); + auto len1 = *((size_t *)(ptr0 + 8)); - return wit::string((char const *)(*((uint8_t **)(ptr0 + 0))), len1); + 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; @@ -55,12 +56,13 @@ wit::string foo::foo::strings::C(std::string_view a, std::string_view b) { auto const &vec1 = b; auto ptr1 = (uint8_t *)(vec1.data()); auto len1 = (size_t)(vec1.size()); - size_t ret_area[2]; + uint64_t ret_area[2]; uint8_t *ptr2 = (uint8_t *)(&ret_area); fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2); - auto len3 = *((size_t *)(ptr2 + sizeof(size_t))); + auto len3 = *((size_t *)(ptr2 + 8)); - return wit::string((char const *)(*((uint8_t **)(ptr2 + 0))), len3); + 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) { @@ -71,14 +73,14 @@ fooX3AfooX2FstringsX23a(uint8_t *arg0, size_t arg1) { extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) uint8_t * fooX3AfooX2FstringsX23b() { auto result0 = exports::foo::foo::strings::B(); - static size_t ret_area[2]; + 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 + sizeof(size_t))) = len2; + *((size_t *)(ptr1 + 8)) = len2; *((uint8_t **)(ptr1 + 0)) = ptr2; return ptr1; } @@ -86,7 +88,7 @@ extern "C" __attribute__((__weak__, __export_name__("cabi_post_fooX3AfooX2FstringsX23b"))) void cabi_post_fooX3AfooX2FstringsX23b(uint8_t *arg0) { - if ((*((size_t *)(arg0 + sizeof(size_t)))) > 0) { + if ((*((size_t *)(arg0 + 8))) > 0) { wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); } } @@ -100,14 +102,14 @@ fooX3AfooX2FstringsX23c(uint8_t *arg0, size_t arg1, uint8_t *arg2, auto result2 = exports::foo::foo::strings::C(wit::string((char const *)(arg0), len0), wit::string((char const *)(arg2), len1)); - static size_t ret_area[2]; + 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 + sizeof(size_t))) = len4; + *((size_t *)(ptr3 + 8)) = len4; *((uint8_t **)(ptr3 + 0)) = ptr4; return ptr3; } @@ -115,7 +117,7 @@ extern "C" __attribute__((__weak__, __export_name__("cabi_post_fooX3AfooX2FstringsX23c"))) void cabi_post_fooX3AfooX2FstringsX23c(uint8_t *arg0) { - if ((*((size_t *)(arg0 + sizeof(size_t)))) > 0) { + if ((*((size_t *)(arg0 + 8))) > 0) { wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); } } diff --git a/crates/cpp/tests/native_strings/the_world_native.cpp b/crates/cpp/tests/native_strings/the_world_native.cpp index 2ba8a4083..9e5ad10ca 100644 --- a/crates/cpp/tests/native_strings/the_world_native.cpp +++ b/crates/cpp/tests/native_strings/the_world_native.cpp @@ -20,7 +20,7 @@ extern "C" void fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { foo::foo::strings::A(std::string_view((char const *)(arg0), len0)); } -extern "C" void fooX3AfooX2FstringsX00b(uint8_t *arg0) { +extern "C" void fooX3AfooX2FstringsX00b(uint8_t *arg0, uint8_t *resultptr) { auto result0 = foo::foo::strings::B(); auto const &vec1 = result0; auto ptr1 = vec1.data(); @@ -30,7 +30,7 @@ extern "C" void fooX3AfooX2FstringsX00b(uint8_t *arg0) { } extern "C" void fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3, - uint8_t *arg4) { + uint8_t *arg4, uint8_t *resultptr) { auto len0 = arg1; auto len1 = arg3; @@ -54,9 +54,10 @@ wit::guest_owned exports::foo::foo::strings::B() { auto ret = fooX3AfooX2FstringsX23b(); auto len0 = *((size_t *)(ret + 8)); - return wit::guest_owned( - std::string_view((char const *)(*((uint8_t **)(ret + 0))), len0), ret, - cabi_post_fooX3AfooX2FstringsX23b); + auto result1 = + std::string_view((char const *)(*((uint8_t **)(ret + 0))), len0); + return wit::guest_owned(result1, ret, + cabi_post_fooX3AfooX2FstringsX23b); } wit::guest_owned exports::foo::foo::strings::C(wit::string a, wit::string b) { @@ -69,9 +70,10 @@ exports::foo::foo::strings::C(wit::string a, wit::string b) { auto ret = fooX3AfooX2FstringsX23c(ptr0, len0, ptr1, len1); auto len2 = *((size_t *)(ret + 8)); - return wit::guest_owned( - std::string_view((char const *)(*((uint8_t **)(ret + 0))), len2), ret, - cabi_post_fooX3AfooX2FstringsX23c); + auto result3 = + std::string_view((char const *)(*((uint8_t **)(ret + 0))), len2); + return wit::guest_owned(result3, ret, + cabi_post_fooX3AfooX2FstringsX23c); } // Component Adapters diff --git a/crates/csharp/src/lib.rs b/crates/csharp/src/lib.rs index 4ea3348fc..3a9304d45 100644 --- a/crates/csharp/src/lib.rs +++ b/crates/csharp/src/lib.rs @@ -24,6 +24,7 @@ use wit_bindgen_core::{ Files, InterfaceGenerator as _, Ns, WorldGenerator, }; use wit_component::{StringEncoding, WitPrinter}; +use wit_parser::{Alignment, ArchitectureSize}; mod csproj; pub use csproj::CSProject; @@ -1990,8 +1991,8 @@ struct FunctionBindgen<'a, 'b> { payloads: Vec, needs_cleanup_list: bool, cleanup: Vec, - import_return_pointer_area_size: usize, - import_return_pointer_area_align: usize, + import_return_pointer_area_size: ArchitectureSize, + import_return_pointer_area_align: Alignment, fixed: usize, // Number of `fixed` blocks that need to be closed. resource_drops: Vec<(String, String)>, } @@ -3045,7 +3046,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } } - fn return_pointer(&mut self, size: usize, align: usize) -> String { + fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> String { let ptr = self.locals.tmp("ptr"); // Use a stack-based return area for imports, because exports need @@ -3057,8 +3058,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.import_return_pointer_area_align = self.import_return_pointer_area_align.max(align); let (array_size, element_type) = dotnet_aligned_array( - self.import_return_pointer_area_size, - self.import_return_pointer_area_align, + self.import_return_pointer_area_size.size_wasm32(), + self.import_return_pointer_area_align.align_wasm32(), ); let ret_area = self.locals.tmp("retArea"); let ret_area_byte0 = self.locals.tmp("retAreaByte0"); diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 87ba78ab2..96f8f9a14 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -16,8 +16,8 @@ pub(super) struct FunctionBindgen<'a, 'b> { tmp: usize, pub needs_cleanup_list: bool, cleanup: Vec<(String, String)>, - pub import_return_pointer_area_size: usize, - pub import_return_pointer_area_align: usize, + pub import_return_pointer_area_size: ArchitectureSize, + pub import_return_pointer_area_align: Alignment, pub handle_decls: Vec, } @@ -39,8 +39,8 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { tmp: 0, needs_cleanup_list: false, cleanup: Vec::new(), - import_return_pointer_area_size: 0, - import_return_pointer_area_align: 0, + import_return_pointer_area_size: Default::default(), + import_return_pointer_area_align: Default::default(), handle_decls: Vec::new(), } } @@ -257,7 +257,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } } - fn return_pointer(&mut self, size: usize, align: usize) -> String { + fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> String { let tmp = self.tmp(); // Imports get a per-function return area to facilitate using the diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index e39e7615a..93f7e3c34 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -22,8 +22,8 @@ pub struct InterfaceGenerator<'a> { pub(super) gen: &'a mut RustWasm, pub wasm_import_module: &'a str, pub resolve: &'a Resolve, - pub return_pointer_area_size: usize, - pub return_pointer_area_align: usize, + pub return_pointer_area_size: ArchitectureSize, + pub return_pointer_area_align: Alignment, pub(super) needs_runtime_module: bool, } @@ -379,15 +379,26 @@ macro_rules! {macro_name} {{ } pub fn finish(&mut self) -> String { - if self.return_pointer_area_align > 0 { + if !self.return_pointer_area_size.is_empty() { + match self.return_pointer_area_align { + Alignment::Pointer => uwriteln!( + self.src, + "\ + #[cgf_attr(target_pointer_width=\"64\", repr(align(8))] + #[cgf_attr(target_pointer_width=\"32\", repr(align(4))]" + ), + Alignment::Bytes(bytes) => uwriteln!( + self.src, + "\ + #[repr(align({align}))]", + align = bytes.get() + ), + } uwrite!( self.src, - "\ - #[repr(align({align}))] - struct _RetArea([::core::mem::MaybeUninit::; {size}]); + "struct _RetArea([::core::mem::MaybeUninit::; {size}]); static mut _RET_AREA: _RetArea = _RetArea([::core::mem::MaybeUninit::uninit(); {size}]); ", - align = self.return_pointer_area_align, size = self.return_pointer_area_size, ); } @@ -835,7 +846,7 @@ impl {async_support}::StreamPayload for {name} {{ uwriteln!(self.src, "let mut cleanup_list = {vec}::new();"); } assert!(handle_decls.is_empty()); - if import_return_pointer_area_size > 0 { + if !import_return_pointer_area_size.is_empty() { uwrite!( self.src, "\ diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 6e40a3cec..d4eed88f1 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -293,11 +293,7 @@ impl RustWasm { resolve: &'a Resolve, in_import: bool, ) -> InterfaceGenerator<'a> { - let mut sizes = SizeAlign::new(if self.opts.wasm64 { - AddressSize::Wasm64 - } else { - AddressSize::Wasm32 - }); + let mut sizes = SizeAlign::new(); sizes.fill(resolve); InterfaceGenerator { @@ -308,8 +304,8 @@ impl RustWasm { gen: self, sizes, resolve, - return_pointer_area_size: 0, - return_pointer_area_align: 0, + return_pointer_area_size: Default::default(), + return_pointer_area_align: Default::default(), needs_runtime_module: false, } } diff --git a/crates/teavm-java/src/lib.rs b/crates/teavm-java/src/lib.rs index 917516118..aa1a35ac4 100644 --- a/crates/teavm-java/src/lib.rs +++ b/crates/teavm-java/src/lib.rs @@ -10,9 +10,9 @@ use wit_bindgen_core::{ abi::{self, AbiVariant, Bindgen, Bitcast, Instruction, LiftLower, WasmType}, uwrite, uwriteln, wit_parser::{ - Docs, Enum, Flags, FlagsRepr, Function, FunctionKind, Int, InterfaceId, Record, Resolve, - Result_, SizeAlign, Tuple, Type, TypeDef, TypeDefKind, TypeId, TypeOwner, Variant, WorldId, - WorldKey, + Alignment, ArchitectureSize, Docs, Enum, Flags, FlagsRepr, Function, FunctionKind, Int, + InterfaceId, Record, Resolve, Result_, SizeAlign, Tuple, Type, TypeDef, TypeDefKind, + TypeId, TypeOwner, Variant, WorldId, WorldKey, }, Direction, Files, InterfaceGenerator as _, Ns, Source, WorldGenerator, }; @@ -1690,8 +1690,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { if realloc.is_none() { self.cleanup.push(Cleanup { address: address.clone(), - size: format!("({op}).size() * {size}"), - align, + size: format!("({op}).size() * {size}", size = size.size_wasm32()), + align: align.align_wasm32(), }); } @@ -2046,7 +2046,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } } - fn return_pointer(&mut self, size: usize, align: usize) -> String { + fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> String { self.gen.gen.return_area_size = self.gen.gen.return_area_size.max(size); self.gen.gen.return_area_align = self.gen.gen.return_area_align.max(align); format!("{}RETURN_AREA", self.gen.gen.qualifier()) From 1833302a7924216f5722621fdb82aac2927a4564 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 21 Jul 2024 18:27:24 +0200 Subject: [PATCH 289/672] properly implement portable size and align expressions --- crates/c/src/lib.rs | 14 +++-- crates/cpp/src/lib.rs | 31 ++++++++--- crates/rust/src/bindgen.rs | 104 ++++++++++++++++++++++++++--------- crates/rust/src/interface.rs | 7 ++- 4 files changed, 114 insertions(+), 42 deletions(-) diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 630561a7d..3543ac91b 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -1770,9 +1770,11 @@ impl InterfaceGenerator<'_> { if !import_return_pointer_area_size.is_empty() { self.src.c_adapters(&format!( "\ - __attribute__((__aligned__({import_return_pointer_area_align}))) - uint8_t ret_area[{import_return_pointer_area_size}]; + __attribute__((__aligned__({}))) + uint8_t ret_area[{}]; ", + import_return_pointer_area_align.align_wasm32(), + import_return_pointer_area_size.size_wasm32(), )); } @@ -2194,7 +2196,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { "*(({}*)({} + {})) = {};", ty, operands[1], - offset, + offset.format("sizeof(void*)"), operands[0] ); } @@ -3049,7 +3051,11 @@ impl Bindgen for FunctionBindgen<'_, '_> { let i = self.locals.tmp("i"); uwriteln!(self.src, "for (size_t {i} = 0; {i} < {len}; {i}++) {{"); let size = self.gen.gen.sizes.size(element); - uwriteln!(self.src, "uint8_t *base = {ptr} + {i} * {size};"); + uwriteln!( + self.src, + "uint8_t *base = {ptr} + {i} * {};", + size.format("sizeof(void*)") + ); uwriteln!(self.src, "(void) base;"); uwrite!(self.src, "{body}"); uwriteln!(self.src, "}}"); diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 650498244..d7256a955 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -24,6 +24,7 @@ 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; @@ -2499,9 +2500,14 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { results: &mut Vec, ) { 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)); + 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)); + results.push(format!( + "*(({}*) ({} + {}))", + ty, + operands[0], + offset.format(POINTER_SIZE_EXPRESSION) + )); } } @@ -2524,7 +2530,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { "*(({}*)wasm_runtime_addr_app_to_native(wasm_runtime_get_module_inst(exec_env), ({} + {}))) = {};", ty, operands[1], - offset, + offset.format(POINTER_SIZE_EXPRESSION), operands[0] ); } else { @@ -2533,7 +2539,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { "*(({}*)({} + {})) = {};", ty, operands[1], - offset, + offset.format(POINTER_SIZE_EXPRESSION), operands[0] ); } @@ -2614,7 +2620,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { uwriteln!( self.src, "int32_t l{tmp} = *((int32_t const*)({} + {offset}));", - operands[0] + operands[0], + offset = offset.format(POINTER_SIZE_EXPRESSION) ); results.push(format!("l{tmp}")); } @@ -2825,7 +2832,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { )); uwriteln!(self.src, "for (unsigned i=0; i<{len}; ++i) {{"); - uwriteln!(self.src, "auto base = {base} + i * {size};"); + uwriteln!( + self.src, + "auto base = {base} + i * {size};", + size = size.format(POINTER_SIZE_EXPRESSION) + ); uwriteln!(self.src, "auto e{tmp} = todo();"); // inplace construct uwriteln!(self.src, "{result}.push_back(e{tmp});"); @@ -3588,7 +3599,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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};"); + 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, "}}"); @@ -3647,7 +3662,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> Self::Operand { let tmp = self.tmp(); - let size_string = size.format("sizeof(void*)"); + let size_string = size.format(POINTER_SIZE_EXPRESSION); //let elems = (size + (align - 1)) / align; let tp = match align { Alignment::Bytes(bytes) => match bytes.get() { diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 96f8f9a14..0766eeca0 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -21,6 +21,8 @@ pub(super) struct FunctionBindgen<'a, 'b> { pub handle_decls: Vec, } +pub const POINTER_SIZE_EXPRESSION: &str = "std::sizeof(*const u8)"; + impl<'a, 'b> FunctionBindgen<'a, 'b> { pub(super) fn new( gen: &'b mut InterfaceGenerator<'a>, @@ -755,7 +757,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { let size = self.gen.sizes.size(element); let align = self.gen.sizes.align(element); self.push_str(&format!( - "let {layout} = {alloc}::Layout::from_size_align_unchecked({vec}.len() * {size}, {align});\n", + "let {layout} = {alloc}::Layout::from_size_align_unchecked({vec}.len() * {}, {});\n", + size.format(POINTER_SIZE_EXPRESSION), align.format(POINTER_SIZE_EXPRESSION), )); self.push_str(&format!("let {result} = if {layout}.size() != 0 {{\n")); self.push_str(&format!( @@ -766,7 +769,10 @@ impl Bindgen for FunctionBindgen<'_, '_> { )); self.push_str("else {{\n::core::ptr::null_mut()\n}};\n"); self.push_str(&format!("for (i, e) in {vec}.into_iter().enumerate() {{\n",)); - self.push_str(&format!("let base = {result}.add(i * {size});\n",)); + self.push_str(&format!( + "let base = {result}.add(i * {});\n", + size.format(POINTER_SIZE_EXPRESSION) + )); self.push_str(&body); self.push_str("\n}\n"); results.push(format!("{result}")); @@ -802,13 +808,21 @@ impl Bindgen for FunctionBindgen<'_, '_> { )); uwriteln!(self.src, "for i in 0..{len} {{"); - uwriteln!(self.src, "let base = {base}.add(i * {size});"); + uwriteln!( + self.src, + "let base = {base}.add(i * {size});", + size = size.format(POINTER_SIZE_EXPRESSION) + ); uwriteln!(self.src, "let e{tmp} = {body};"); uwriteln!(self.src, "{result}.push(e{tmp});"); uwriteln!(self.src, "}}"); results.push(result); let dealloc = self.gen.path_to_cabi_dealloc(); - self.push_str(&format!("{dealloc}({base}, {len} * {size}, {align});\n",)); + 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()), @@ -838,6 +852,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { let alloc = self.gen.path_to_std_alloc_module(); self.push_str(&format!( "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align});\n", + size = size.format(POINTER_SIZE_EXPRESSION), + align = align.format(POINTER_SIZE_EXPRESSION) )); let operands = operands.join(", "); uwriteln!( @@ -903,7 +919,9 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!( self.src, "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align}); - let {ptr} = {alloc}::alloc({layout});" + let {ptr} = {alloc}::alloc({layout});", + size = size.format(POINTER_SIZE_EXPRESSION), + align = align.format(POINTER_SIZE_EXPRESSION) ); results.push(ptr); } @@ -986,7 +1004,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!( self.src, "let l{tmp} = *{}.add({offset}).cast::();", - operands[0] + operands[0], + offset = offset.format(POINTER_SIZE_EXPRESSION) ); results.push(format!("l{tmp}")); } @@ -995,7 +1014,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!( self.src, "let l{tmp} = i32::from(*{0}.add({offset}).cast::());", - operands[0] + operands[0], + offset = offset.format(POINTER_SIZE_EXPRESSION) ); results.push(format!("l{tmp}")); } @@ -1004,7 +1024,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!( self.src, "let l{tmp} = i32::from(*{}.add({offset}).cast::());", - operands[0] + operands[0], + offset = offset.format(POINTER_SIZE_EXPRESSION) ); results.push(format!("l{tmp}")); } @@ -1013,7 +1034,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!( self.src, "let l{tmp} = i32::from(*{}.add({offset}).cast::());", - operands[0] + operands[0], + offset = offset.format(POINTER_SIZE_EXPRESSION) ); results.push(format!("l{tmp}")); } @@ -1022,7 +1044,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!( self.src, "let l{tmp} = i32::from(*{}.add({offset}).cast::());", - operands[0] + operands[0], + offset = offset.format(POINTER_SIZE_EXPRESSION) ); results.push(format!("l{tmp}")); } @@ -1031,7 +1054,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!( self.src, "let l{tmp} = *{}.add({offset}).cast::();", - operands[0] + operands[0], + offset = offset.format(POINTER_SIZE_EXPRESSION) ); results.push(format!("l{tmp}")); } @@ -1040,7 +1064,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!( self.src, "let l{tmp} = *{}.add({offset}).cast::();", - operands[0] + operands[0], + offset = offset.format(POINTER_SIZE_EXPRESSION) ); results.push(format!("l{tmp}")); } @@ -1049,7 +1074,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!( self.src, "let l{tmp} = *{}.add({offset}).cast::();", - operands[0] + operands[0], + offset = offset.format(POINTER_SIZE_EXPRESSION) ); results.push(format!("l{tmp}")); } @@ -1059,7 +1085,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!( self.src, "let l{tmp} = *{}.add({offset}).cast::<*mut u8>();", - operands[0] + operands[0], + offset = offset.format(POINTER_SIZE_EXPRESSION) ); results.push(format!("l{tmp}")); } @@ -1067,8 +1094,9 @@ impl Bindgen for FunctionBindgen<'_, '_> { let tmp = self.tmp(); uwriteln!( self.src, - "let l{tmp} = *{}.add({offset}).cast::();", - operands[0] + "let l{tmp} = *{}.add({}).cast::();", + operands[0], + offset.format(POINTER_SIZE_EXPRESSION) ); results.push(format!("l{tmp}")); } @@ -1076,50 +1104,66 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::I32Store { offset } => { self.push_str(&format!( "*{}.add({}).cast::() = {};\n", - operands[1], offset, operands[0] + operands[1], + offset.format(POINTER_SIZE_EXPRESSION), + operands[0] )); } Instruction::I32Store8 { offset } => { self.push_str(&format!( "*{}.add({}).cast::() = ({}) as u8;\n", - operands[1], offset, operands[0] + operands[1], + offset.format(POINTER_SIZE_EXPRESSION), + operands[0] )); } Instruction::I32Store16 { offset } => { self.push_str(&format!( "*{}.add({}).cast::() = ({}) as u16;\n", - operands[1], offset, operands[0] + operands[1], + offset.format(POINTER_SIZE_EXPRESSION), + operands[0] )); } Instruction::I64Store { offset } => { self.push_str(&format!( "*{}.add({}).cast::() = {};\n", - operands[1], offset, operands[0] + operands[1], + offset.format(POINTER_SIZE_EXPRESSION), + operands[0] )); } Instruction::F32Store { offset } => { self.push_str(&format!( "*{}.add({}).cast::() = {};\n", - operands[1], offset, operands[0] + operands[1], + offset.format(POINTER_SIZE_EXPRESSION), + operands[0] )); } Instruction::F64Store { offset } => { self.push_str(&format!( "*{}.add({}).cast::() = {};\n", - operands[1], offset, operands[0] + operands[1], + offset.format(POINTER_SIZE_EXPRESSION), + operands[0] )); } Instruction::PointerStore { offset } => { self.push_str(&format!( "*{}.add({}).cast::<*mut u8>() = {};\n", - operands[1], offset, operands[0] + operands[1], + offset.format(POINTER_SIZE_EXPRESSION), + operands[0] )); } Instruction::LengthStore { offset } => { self.push_str(&format!( "*{}.add({}).cast::() = {};\n", - operands[1], offset, operands[0] + operands[1], + offset.format(POINTER_SIZE_EXPRESSION), + operands[0] )); } @@ -1129,7 +1173,9 @@ impl Bindgen for FunctionBindgen<'_, '_> { let dealloc = self.gen.path_to_cabi_dealloc(); self.push_str(&format!( "{dealloc}({op}, {size}, {align});\n", - op = operands[0] + op = operands[0], + size = size.format(POINTER_SIZE_EXPRESSION), + align = align.format(POINTER_SIZE_EXPRESSION) )); } @@ -1184,13 +1230,17 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str("let base = "); self.push_str(&base); self.push_str(".add(i * "); - self.push_str(&size.to_string()); + self.push_str(&size.format(POINTER_SIZE_EXPRESSION)); self.push_str(");\n"); self.push_str(&body); self.push_str("\n}\n"); } let dealloc = self.gen.path_to_cabi_dealloc(); - self.push_str(&format!("{dealloc}({base}, {len} * {size}, {align});\n",)); + self.push_str(&format!( + "{dealloc}({base}, {len} * {size}, {align});\n", + size = size.format(POINTER_SIZE_EXPRESSION), + align = align.format(POINTER_SIZE_EXPRESSION) + )); } } } diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 93f7e3c34..5090f8712 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -1,4 +1,4 @@ -use crate::bindgen::FunctionBindgen; +use crate::bindgen::{FunctionBindgen, POINTER_SIZE_EXPRESSION}; use crate::{ int_repr, to_rust_ident, to_upper_camel_case, wasm_type, AsyncConfig, FnSig, Identifier, InterfaceName, Ownership, RuntimeItem, RustFlagsRepr, RustWasm, @@ -399,7 +399,7 @@ macro_rules! {macro_name} {{ "struct _RetArea([::core::mem::MaybeUninit::; {size}]); static mut _RET_AREA: _RetArea = _RetArea([::core::mem::MaybeUninit::uninit(); {size}]); ", - size = self.return_pointer_area_size, + size = self.return_pointer_area_size.format("std::sizeof(usize)"), ); } @@ -853,7 +853,8 @@ impl {async_support}::StreamPayload for {name} {{ #[repr(align({import_return_pointer_area_align}))] struct RetArea([::core::mem::MaybeUninit::; {import_return_pointer_area_size}]); let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); {import_return_pointer_area_size}]); -", +", import_return_pointer_area_size = import_return_pointer_area_size.format(POINTER_SIZE_EXPRESSION), +import_return_pointer_area_align = import_return_pointer_area_align.format(POINTER_SIZE_EXPRESSION) ); } self.src.push_str(&String::from(src)); From 68bb99cfa8734af003137f7769fb21398bc3dbb2 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 21 Jul 2024 18:54:15 +0200 Subject: [PATCH 290/672] fix Rust and C codegen --- crates/c/src/lib.rs | 24 +++++++++++++++++------- crates/rust/src/bindgen.rs | 2 +- crates/rust/src/interface.rs | 2 +- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 3543ac91b..759cc05a5 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -470,8 +470,10 @@ impl WorldGenerator for C { __attribute__((__aligned__({}))) static uint8_t RET_AREA[{}]; ", - self.return_pointer_area_align.align_wasm32(), - self.return_pointer_area_size.size_wasm32(), + self.return_pointer_area_align + .format(POINTER_SIZE_EXPRESSION), + self.return_pointer_area_size + .format(POINTER_SIZE_EXPRESSION), ); } c_str.push_str(&self.src.c_adapters); @@ -1773,8 +1775,8 @@ impl InterfaceGenerator<'_> { __attribute__((__aligned__({}))) uint8_t ret_area[{}]; ", - import_return_pointer_area_align.align_wasm32(), - import_return_pointer_area_size.size_wasm32(), + import_return_pointer_area_align.format(POINTER_SIZE_EXPRESSION), + import_return_pointer_area_size.format(POINTER_SIZE_EXPRESSION), )); } @@ -2174,7 +2176,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { "*(({}*) ({} + {}))", ty, operands[0], - offset.format("sizeof(void*)") + offset.format(POINTER_SIZE_EXPRESSION) )); } @@ -2196,7 +2198,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { "*(({}*)({} + {})) = {};", ty, operands[1], - offset.format("sizeof(void*)"), + offset.format(POINTER_SIZE_EXPRESSION), operands[0] ); } @@ -3054,7 +3056,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!( self.src, "uint8_t *base = {ptr} + {i} * {};", - size.format("sizeof(void*)") + size.format(POINTER_SIZE_EXPRESSION) ); uwriteln!(self.src, "(void) base;"); uwrite!(self.src, "{body}"); @@ -3062,6 +3064,12 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!(self.src, "free({ptr});"); uwriteln!(self.src, "}}"); } + Instruction::Flush { amt } => { + for i in 0..*amt { + // no easy way to create a temporary? + results.push(operands[i].clone()); + } + } i => unimplemented!("{:?}", i), } @@ -3289,3 +3297,5 @@ pub fn to_c_ident(name: &str) -> String { s => s.to_snake_case(), } } + +pub const POINTER_SIZE_EXPRESSION: &str = "sizeof(void*)"; diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 0766eeca0..cc8ea18af 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -21,7 +21,7 @@ pub(super) struct FunctionBindgen<'a, 'b> { pub handle_decls: Vec, } -pub const POINTER_SIZE_EXPRESSION: &str = "std::sizeof(*const u8)"; +pub const POINTER_SIZE_EXPRESSION: &str = "core::mem::size_of::<*const u8>()"; impl<'a, 'b> FunctionBindgen<'a, 'b> { pub(super) fn new( diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 5090f8712..e0795c558 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -399,7 +399,7 @@ macro_rules! {macro_name} {{ "struct _RetArea([::core::mem::MaybeUninit::; {size}]); static mut _RET_AREA: _RetArea = _RetArea([::core::mem::MaybeUninit::uninit(); {size}]); ", - size = self.return_pointer_area_size.format("std::sizeof(usize)"), + size = self.return_pointer_area_size.format(POINTER_SIZE_EXPRESSION), ); } From 4067e8f971576ffbb46a93a2661162407a4f776e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 21 Jul 2024 22:14:18 +0200 Subject: [PATCH 291/672] regenerate symmetric strings, enable symmetric mode --- crates/cpp/src/lib.rs | 6 +- .../component_a/the_world.cpp | 28 ++++---- .../component_b/the_world.cpp | 72 ++++++++++--------- 3 files changed, 57 insertions(+), 49 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index d7256a955..b14f3f1f0 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -282,7 +282,11 @@ impl Cpp { in_guest_import: bool, wasm_import_module: Option, ) -> CppInterfaceGenerator<'a> { - let mut sizes = SizeAlign::new(); + let mut sizes = if self.opts.symmetric { + SizeAlign::new_symmetric() + } else { + SizeAlign::new() + }; sizes.fill(resolve); CppInterfaceGenerator { diff --git a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp index f62c9e1f6..716f97cf5 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp @@ -47,10 +47,11 @@ void comp_a::foo::foo::strings::A(std::string_view x) { fooX3AfooX2FstringsX00a(ptr0, len0); } wit::string comp_a::foo::foo::strings::B() { - uint64_t ret_area[2]; + 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 + 8)); + auto len1 = *((size_t *)(ptr0 + sizeof(void *))); auto string1 = wit::string::from_view( std::string_view((char const *)(*((uint8_t **)(ptr0 + 0))), len1)); @@ -66,10 +67,11 @@ wit::string comp_a::foo::foo::strings::C(std::string_view a, auto const &vec1 = b; auto ptr1 = (uint8_t *)(vec1.data()); auto len1 = (size_t)(vec1.size()); - uint64_t ret_area[2]; + 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 + 8)); + auto len3 = *((size_t *)(ptr2 + sizeof(void *))); auto string3 = wit::string::from_view( std::string_view((char const *)(*((uint8_t **)(ptr2 + 0))), len3)); @@ -78,7 +80,7 @@ wit::string comp_a::foo::foo::strings::C(std::string_view a, return string3; } extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void -fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { +a_fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { auto len0 = arg1; auto string0 = @@ -87,26 +89,26 @@ fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { comp_a::exports::foo::foo::strings::A(std::move(string0)); } extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) void -fooX3AfooX2FstringsX00b(uint8_t *arg0) { +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 + 8)) = len1; + *((size_t *)(arg0 + sizeof(void *))) = len1; *((uint8_t **)(arg0 + 0)) = ptr1; } extern "C" __attribute__((__weak__, __export_name__("cabi_post_fooX3AfooX2FstringsX00b"))) void - cabi_post_fooX3AfooX2FstringsX00b(uint8_t *retptr) { - if ((*((size_t *)(retptr + 8))) > 0) { + a_cabi_post_fooX3AfooX2FstringsX00b(uint8_t *retptr) { + if ((*((size_t *)(retptr + sizeof(void *)))) > 0) { wit::string::drop_raw((void *)(*((uint8_t **)(retptr + 0)))); } } extern "C" __attribute__((__export_name__("foo:foo/strings#c"))) void -fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3, +a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3, uint8_t *arg4) { auto len0 = arg1; @@ -125,14 +127,14 @@ fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3, auto len3 = (size_t)(vec3.size()); result2.leak(); - *((size_t *)(arg4 + 8)) = len3; + *((size_t *)(arg4 + sizeof(void *))) = len3; *((uint8_t **)(arg4 + 0)) = ptr3; } extern "C" __attribute__((__weak__, __export_name__("cabi_post_fooX3AfooX2FstringsX00c"))) void - cabi_post_fooX3AfooX2FstringsX00c(uint8_t *retptr) { - if ((*((size_t *)(retptr + 8))) > 0) { + a_cabi_post_fooX3AfooX2FstringsX00c(uint8_t *retptr) { + if ((*((size_t *)(retptr + sizeof(void *)))) > 0) { wit::string::drop_raw((void *)(*((uint8_t **)(retptr + 0)))); } } diff --git a/crates/cpp/tests/meshless_strings/component_b/the_world.cpp b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp index 7fad04fc6..6465c665f 100644 --- a/crates/cpp/tests/meshless_strings/component_b/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp @@ -47,16 +47,17 @@ void foo::foo::strings::A(std::string_view x) { a_fooX3AfooX2FstringsX00a(ptr0, len0); } wit::string foo::foo::strings::B() { - size_t ret_area[2]; - uint8_t *ret = (uint8_t *)(&ret_area); - a_fooX3AfooX2FstringsX00b(ret); - auto len0 = *((size_t *)(ret + 8)); + 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 *))); - auto string0 = wit::string::from_view( - std::string_view((char const *)(*((uint8_t **)(ret + 0))), len0)); + auto string1 = wit::string::from_view( + std::string_view((char const *)(*((uint8_t **)(ptr0 + 0))), len1)); - a_cabi_post_fooX3AfooX2FstringsX00b(ret); - return string0; + a_cabi_post_fooX3AfooX2FstringsX00b(ptr0); + return string1; } wit::string foo::foo::strings::C(std::string_view a, std::string_view b) { auto const &vec0 = a; @@ -65,16 +66,17 @@ wit::string foo::foo::strings::C(std::string_view a, std::string_view b) { auto const &vec1 = b; auto ptr1 = (uint8_t *)(vec1.data()); auto len1 = (size_t)(vec1.size()); - size_t ret_area[2]; - uint8_t *ret = (uint8_t *)(&ret_area); - a_fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ret); - auto len2 = *((size_t *)(ret + 8)); + 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 *))); - auto string2 = wit::string::from_view( - std::string_view((char const *)(*((uint8_t **)(ret + 0))), len2)); + auto string3 = wit::string::from_view( + std::string_view((char const *)(*((uint8_t **)(ptr2 + 0))), len3)); - a_cabi_post_fooX3AfooX2FstringsX00c(ret); - return string2; + a_cabi_post_fooX3AfooX2FstringsX00c(ptr2); + return string3; } extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { @@ -86,27 +88,27 @@ fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { exports::foo::foo::strings::A(std::move(string0)); } extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) void -fooX3AfooX2FstringsX00b(uint8_t *ptr1) { +fooX3AfooX2FstringsX00b(uint8_t *arg0) { auto result0 = exports::foo::foo::strings::B(); - auto const &vec2 = result0; - auto ptr2 = (uint8_t *)(vec2.data()); - auto len2 = (size_t)(vec2.size()); + auto const &vec1 = result0; + auto ptr1 = (uint8_t *)(vec1.data()); + auto len1 = (size_t)(vec1.size()); result0.leak(); - *((size_t *)(ptr1 + 8)) = len2; - *((uint8_t **)(ptr1 + 0)) = ptr2; + *((size_t *)(arg0 + sizeof(void *))) = len1; + *((uint8_t **)(arg0 + 0)) = ptr1; } extern "C" __attribute__((__weak__, __export_name__("cabi_post_fooX3AfooX2FstringsX00b"))) void - cabi_post_fooX3AfooX2FstringsX00b(uint8_t *arg0) { - if ((*((size_t *)(arg0 + 8))) > 0) { - wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); + cabi_post_fooX3AfooX2FstringsX00b(uint8_t *retptr) { + if ((*((size_t *)(retptr + sizeof(void *)))) > 0) { + wit::string::drop_raw((void *)(*((uint8_t **)(retptr + 0)))); } } 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 *ptr3) { +fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3, + uint8_t *arg4) { auto len0 = arg1; auto string0 = @@ -119,20 +121,20 @@ fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, auto result2 = exports::foo::foo::strings::C(std::move(string0), std::move(string1)); - auto const &vec4 = result2; - auto ptr4 = (uint8_t *)(vec4.data()); - auto len4 = (size_t)(vec4.size()); + auto const &vec3 = result2; + auto ptr3 = (uint8_t *)(vec3.data()); + auto len3 = (size_t)(vec3.size()); result2.leak(); - *((size_t *)(ptr3 + 8)) = len4; - *((uint8_t **)(ptr3 + 0)) = ptr4; + *((size_t *)(arg4 + sizeof(void *))) = len3; + *((uint8_t **)(arg4 + 0)) = ptr3; } extern "C" __attribute__((__weak__, __export_name__("cabi_post_fooX3AfooX2FstringsX00c"))) void - cabi_post_fooX3AfooX2FstringsX00c(uint8_t *arg0) { - if ((*((size_t *)(arg0 + 8))) > 0) { - wit::string::drop_raw((void *)(*((uint8_t **)(arg0 + 0)))); + cabi_post_fooX3AfooX2FstringsX00c(uint8_t *retptr) { + if ((*((size_t *)(retptr + sizeof(void *)))) > 0) { + wit::string::drop_raw((void *)(*((uint8_t **)(retptr + 0)))); } } From 01037f90df05a6c90429ce659faea9b886f59329 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 21 Jul 2024 22:28:13 +0200 Subject: [PATCH 292/672] dependency update --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4404c42bf..3d5aafd08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1727,7 +1727,7 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#474e13a0666fdd43b374021582351f50b07a1eb2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#fcbcd794d865bd52593a2cb6854c9b75b6ef3bbc" dependencies = [ "leb128", "wasmparser 0.214.0", @@ -1736,7 +1736,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#474e13a0666fdd43b374021582351f50b07a1eb2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#fcbcd794d865bd52593a2cb6854c9b75b6ef3bbc" dependencies = [ "anyhow", "indexmap", @@ -1765,7 +1765,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#474e13a0666fdd43b374021582351f50b07a1eb2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#fcbcd794d865bd52593a2cb6854c9b75b6ef3bbc" dependencies = [ "ahash", "bitflags", @@ -2094,7 +2094,7 @@ dependencies = [ [[package]] name = "wast" version = "214.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#474e13a0666fdd43b374021582351f50b07a1eb2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#fcbcd794d865bd52593a2cb6854c9b75b6ef3bbc" dependencies = [ "bumpalo", "leb128", @@ -2115,7 +2115,7 @@ dependencies = [ [[package]] name = "wat" version = "1.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#474e13a0666fdd43b374021582351f50b07a1eb2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#fcbcd794d865bd52593a2cb6854c9b75b6ef3bbc" dependencies = [ "wast 214.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] @@ -2547,7 +2547,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#474e13a0666fdd43b374021582351f50b07a1eb2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#fcbcd794d865bd52593a2cb6854c9b75b6ef3bbc" dependencies = [ "anyhow", "bitflags", @@ -2584,7 +2584,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#474e13a0666fdd43b374021582351f50b07a1eb2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#fcbcd794d865bd52593a2cb6854c9b75b6ef3bbc" dependencies = [ "anyhow", "id-arena", From 3def618c973985ab3209c4ecdf56e19020589b34 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 21 Jul 2024 23:45:12 +0200 Subject: [PATCH 293/672] proper support for option lifting --- crates/core/src/abi.rs | 8 +++++++- crates/cpp/tests/misc_wit/option_handle.wit | 11 +++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 crates/cpp/tests/misc_wit/option_handle.wit diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 2ca0da791..94073c42c 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -1898,7 +1898,13 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Error - | TypeDefKind::Handle(_) => self.emit_and_lift(ty, addr, &I32Load { offset }), + | TypeDefKind::Handle(_) => { + 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 => { todo!(); 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; +} + +world myworld { + import iface; +} From 7d12a44fe7f2f5c471a122fb7aa6ab96ff2d81cd Mon Sep 17 00:00:00 2001 From: Lachlan Heywood Date: Tue, 23 Jul 2024 17:49:33 -0400 Subject: [PATCH 294/672] fix(markdown): update primary link to match behavior from #818 (#1008) Signed-off-by: Lachlan Heywood --- crates/markdown/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/markdown/src/lib.rs b/crates/markdown/src/lib.rs index 5363cc682..d9752f941 100644 --- a/crates/markdown/src/lib.rs +++ b/crates/markdown/src/lib.rs @@ -43,7 +43,7 @@ impl WorldGenerator for Markdown { let world = &resolve.worlds[world]; uwriteln!( self.src, - "# World {}\n", + "# World {}\n", world.name.to_snake_case(), world.name ); From 4a57daf8af6afa46e21dfec83b70576b1cb25009 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 25 Jul 2024 14:01:47 +0200 Subject: [PATCH 295/672] beginning of symmetric support --- crates/rust/src/bindgen.rs | 29 +++++++++++++++++++---------- crates/rust/src/interface.rs | 6 +++++- crates/rust/src/lib.rs | 6 +++++- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index cc8ea18af..f5457c27a 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -685,7 +685,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } self.push_str(&format!("let {} = {}.as_ptr().cast::();\n", ptr, val)); self.push_str(&format!("let {} = {}.len();\n", len, val)); - if realloc.is_some() { + if realloc.is_some() && !self.gen.gen.opts.symmetric { self.push_str(&format!("::core::mem::forget({});\n", val)); } results.push(format!("{ptr}.cast_mut()")); @@ -717,7 +717,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } self.push_str(&format!("let {} = {}.as_ptr().cast::();\n", ptr, val)); self.push_str(&format!("let {} = {}.len();\n", len, val)); - if realloc.is_some() { + if realloc.is_some() && !self.gen.gen.opts.symmetric { self.push_str(&format!("::core::mem::forget({});\n", val)); } results.push(format!("{ptr}.cast_mut()")); @@ -729,15 +729,24 @@ 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.gen.gen.opts.raw_strings { - results.push(format!("bytes{tmp}")); + if self.gen.gen.opts.symmetric { + 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.gen.path_to_string_lift())); + uwriteln!( + self.src, + "let bytes{tmp} = {vec}::from_raw_parts({}.cast(), {len}, {len});", + operands[0], + ); + if self.gen.gen.opts.raw_strings { + results.push(format!("bytes{tmp}")); + } else { + results.push(format!("{}(bytes{tmp})", self.gen.path_to_string_lift())); + } } } diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index e0795c558..b71c43e89 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -827,7 +827,11 @@ impl {async_support}::StreamPayload for {name} {{ abi::call( f.gen.resolve, AbiVariant::GuestImport, - LiftLower::LowerArgsLiftResults, + if f.gen.gen.opts.symmetric { + LiftLower::Symmetric + } else { + LiftLower::LowerArgsLiftResults + }, func, &mut f, async_, diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index d4eed88f1..fefe8a240 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -293,7 +293,11 @@ impl RustWasm { resolve: &'a Resolve, in_import: bool, ) -> InterfaceGenerator<'a> { - let mut sizes = SizeAlign::new(); + let mut sizes = if self.opts.symmetric { + SizeAlign::new_symmetric() + } else { + SizeAlign::new() + }; sizes.fill(resolve); InterfaceGenerator { From 01b42163e9db43515b4939ec7d38de1e00a7e32e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 25 Jul 2024 14:23:57 +0200 Subject: [PATCH 296/672] proper cabi_post prefix --- crates/rust/src/bindgen.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index f5457c27a..3e4421166 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -66,7 +66,7 @@ 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 mut sig = "(".to_owned(); @@ -82,11 +82,11 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { sig.push_str(wasm_type(*result)); } let module_name = self.wasm_import_module; - let export_name = make_external_symbol(module_name, name, AbiVariant::GuestImport); + let export_name = String::from(module_prefix) + &make_external_symbol(module_name, name, AbiVariant::GuestImport); uwrite!( self.src, " - #[link(wasm_import_module = \"{module_name}\")] + #[link(wasm_import_module = \"{module_prefix}{module_name}\")] extern \"C\" {{ #[cfg_attr(target_arch = \"wasm32\", link_name = \"{name}\")] fn {export_name}{sig}; @@ -677,7 +677,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.gen.gen.opts.symmetric { self.push_str(&format!("let {} = {};\n", val, operands[0])); } else { let op0 = operands.pop().unwrap(); @@ -709,7 +709,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.gen.gen.opts.symmetric { self.push_str(&format!("let {} = {};\n", val, operands[0])); } else { let op0 = format!("{}.into_bytes()", operands[0]); @@ -732,7 +732,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { if self.gen.gen.opts.symmetric { uwriteln!( self.src, - "let string{tmp} = String::from(std::str::from_utf8(std::slice::from_raw_parts({}, {len}).unwrap());", + "let string{tmp} = String::from(std::str::from_utf8(std::slice::from_raw_parts({}, {len})).unwrap());", operands[0], ); results.push(format!("string{tmp}")); @@ -838,8 +838,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { 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 func = self.declare_import(module_prefix, name, &sig.params, &sig.results); // ... then call the function with all our operands if !sig.results.is_empty() { @@ -853,7 +853,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } Instruction::AsyncCallWasm { name, size, align } => { - let func = self.declare_import(name, &[WasmType::Pointer; 3], &[WasmType::I32]); + let func = self.declare_import("", name, &[WasmType::Pointer; 3], &[WasmType::I32]); let async_support = self.gen.path_to_async_support(); let tmp = self.tmp(); @@ -940,7 +940,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { params, results: call_results, } => { - let func = self.declare_import(name, params, call_results); + let func = self.declare_import("", name, params, call_results); if !call_results.is_empty() { self.push_str("let ret = "); @@ -971,7 +971,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, From be953c192920bab53897fe134456cbfad0c0f1ce Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 26 Jul 2024 12:08:28 +0100 Subject: [PATCH 297/672] fully functional Rust code generation for symmetric strings example --- crates/core/src/abi.rs | 8 +- crates/cpp/src/lib.rs | 36 +++--- .../rust_comp_a/src/the_world.rs | 107 ++++++++++-------- crates/rust/src/bindgen.rs | 22 +++- crates/rust/src/interface.rs | 51 +++++---- 5 files changed, 128 insertions(+), 96 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 94073c42c..86a1a36b9 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -741,7 +741,7 @@ pub fn call( async_: bool, ) { if matches!(lift_lower, LiftLower::Symmetric) { - let sig = wasm_signature_symmetric(resolve, variant, func); + let sig = wasm_signature_symmetric(resolve, variant, func, true); Generator::new(resolve, variant, lift_lower, bindgen, async_) .call_with_signature(func, sig); } else { @@ -2276,9 +2276,13 @@ fn push_flat_symmetric(resolve: &Resolve, ty: &Type, vec: &mut Vec) { // another hack pub fn wasm_signature_symmetric( resolve: &Resolve, - _variant: AbiVariant, + 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; diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index b14f3f1f0..a613b4e6c 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -969,26 +969,22 @@ impl CppInterfaceGenerator<'_> { // local patching of borrows function needs more complex solution fn patched_wasm_signature(&self, variant: AbiVariant, func: &Function) -> WasmSignature { - if self.gen.opts.symmetric { - abi::wasm_signature_symmetric(self.resolve, variant, func) - } else { - self.resolve.wasm_signature(variant, func) - // 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)); - // } - // } - // } - // } - // } - // } - } + 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) 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 index e9fa6cdc2..617e95fae 100644 --- 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 @@ -1,6 +1,5 @@ -// Generated by `wit-bindgen` 0.27.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.28.0. DO NOT EDIT! // Options used: -// * with "foo:foo/strings" = generate #[allow(dead_code)] pub mod foo { #[allow(dead_code)] @@ -13,7 +12,7 @@ pub mod foo { 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,){ + pub fn a(x: &str,) -> (){ unsafe { let vec0 = x; let ptr0 = vec0.as_ptr().cast::(); @@ -30,32 +29,40 @@ pub mod foo { #[allow(unused_unsafe, clippy::all)] pub fn b() -> _rt::String{ unsafe { - #[repr(align(8))] - struct RetArea([::core::mem::MaybeUninit::; 16]); - let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 16]); + + #[cfg_attr(target_pointer_width="64", repr(align(8)))] + #[cfg_attr(target_pointer_width="32", repr(align(4)))] + struct RetArea([::core::mem::MaybeUninit::; (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::(); #[link(wasm_import_module = "foo:foo/strings")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "b")] - fn fooX3AfooX2FstringsX00b(_: *mut u8); - #[cfg_attr(target_arch = "wasm32", link_name = "cabi_post_b")] - fn cabi_post_fooX3AfooX2FstringsX00b(_: *mut u8); + fn fooX3AfooX2FstringsX00b(_: *mut u8, ); } fooX3AfooX2FstringsX00b(ptr0); let l1 = *ptr0.add(0).cast::<*mut u8>(); - let l2 = *ptr0.add(8).cast::(); + let l2 = *ptr0.add(core::mem::size_of::<*const u8>()).cast::(); let len3 = l2; - let res = String::from(std::str::from_utf8(std::slice::from_raw_parts(l1, len3)).unwrap()); + let string3 = String::from(std::str::from_utf8(std::slice::from_raw_parts(l1, len3)).unwrap()); + + #[link(wasm_import_module = "cabi_post_foo:foo/strings")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "b")] + fn cabi_post_fooX3AfooX2FstringsX00b(_: *mut u8, ); + } cabi_post_fooX3AfooX2FstringsX00b(ptr0); - res + string3 } } #[allow(unused_unsafe, clippy::all)] pub fn c(a: &str,b: &str,) -> _rt::String{ unsafe { - #[repr(align(8))] - struct RetArea([::core::mem::MaybeUninit::; 16]); - let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 16]); + + #[cfg_attr(target_pointer_width="64", repr(align(8)))] + #[cfg_attr(target_pointer_width="32", repr(align(4)))] + struct RetArea([::core::mem::MaybeUninit::; (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::(); let len0 = vec0.len(); @@ -66,17 +73,21 @@ pub mod foo { #[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); - #[cfg_attr(target_arch = "wasm32", link_name = "cabi_post_c")] - fn cabi_post_fooX3AfooX2FstringsX00c(_: *mut u8); + 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::(); + let l4 = *ptr2.add(core::mem::size_of::<*const u8>()).cast::(); let len5 = l4; - let res = String::from(std::str::from_utf8(std::slice::from_raw_parts(l3, len5)).unwrap()); + let string5 = String::from(std::str::from_utf8(std::slice::from_raw_parts(l3, len5)).unwrap()); + + #[link(wasm_import_module = "cabi_post_foo:foo/strings")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "c")] + fn cabi_post_fooX3AfooX2FstringsX00c(_: *mut u8, ); + } cabi_post_fooX3AfooX2FstringsX00c(ptr2); - res + string5 } } @@ -101,52 +112,51 @@ pub mod exports { #[allow(non_snake_case)] pub unsafe fn _export_a_cabi(arg0: *mut u8,arg1: usize,) {#[cfg(target_arch="wasm32")] _rt::run_ctors_once();let len0 = arg1; - let s = String::from(std::str::from_utf8(std::slice::from_raw_parts(arg0, len0)).unwrap()); - // let bytes0 = _rt::Vec::from_raw_parts(arg0.cast(), len0, len0); - T::a(s); + 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(ptr1: *mut u8) {#[cfg(target_arch="wasm32")] + pub unsafe fn _export_b_cabi(arg0: *mut u8,) {#[cfg(target_arch="wasm32")] _rt::run_ctors_once();let result0 = T::b(); - let vec2 = (result0.into_bytes()).into_boxed_slice(); - let ptr2 = vec2.as_ptr().cast::(); - let len2 = vec2.len(); - ::core::mem::forget(vec2); - *ptr1.add(8).cast::() = len2; - *ptr1.add(0).cast::<*mut u8>() = ptr2.cast_mut(); + let vec1 = (result0.into_bytes()).into_boxed_slice(); + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + ::core::mem::forget(vec1); + *arg0.add(core::mem::size_of::<*const u8>()).cast::() = len1; + *arg0.add(0).cast::<*mut u8>() = ptr1.cast_mut(); } #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn __post_return_b(arg0: *mut u8,) { let l0 = *arg0.add(0).cast::<*mut u8>(); - let l1 = *arg0.add(8).cast::(); + let l1 = *arg0.add(core::mem::size_of::<*const u8>()).cast::(); _rt::cabi_dealloc(l0, l1, 1); } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_c_cabi(arg0: *mut u8,arg1: usize,arg2: *mut u8,arg3: usize, ptr3: *mut u8) {#[cfg(target_arch="wasm32")] + pub unsafe fn _export_c_cabi(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 bytes0 = String::from(std::str::from_utf8(std::slice::from_raw_parts(arg0, len0)).unwrap()); + let string0 = String::from(std::str::from_utf8(std::slice::from_raw_parts(arg0, len0)).unwrap()); let len1 = arg3; - let bytes1 = String::from(std::str::from_utf8(std::slice::from_raw_parts(arg2, len1)).unwrap()); - let result2 = T::c(bytes0, bytes1); - let vec4 = (result2.into_bytes()).into_boxed_slice(); - let ptr4 = vec4.as_ptr().cast::(); - let len4 = vec4.len(); - ::core::mem::forget(vec4); - *ptr3.add(8).cast::() = len4; - *ptr3.add(0).cast::<*mut u8>() = ptr4.cast_mut(); + 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::(); + let len3 = vec3.len(); + ::core::mem::forget(vec3); + *arg4.add(core::mem::size_of::<*const u8>()).cast::() = len3; + *arg4.add(0).cast::<*mut u8>() = ptr3.cast_mut(); } #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn __post_return_c(arg0: *mut u8,) { let l0 = *arg0.add(0).cast::<*mut u8>(); - let l1 = *arg0.add(8).cast::(); + let l1 = *arg0.add(core::mem::size_of::<*const u8>()).cast::(); _rt::cabi_dealloc(l0, l1, 1); } pub trait Guest { - fn a(x: _rt::String,); + fn a(x: _rt::String,) -> (); fn b() -> _rt::String; fn c(a: _rt::String,b: _rt::String,) -> _rt::String; } @@ -192,7 +202,8 @@ pub mod exports { } mod _rt { pub use alloc_crate::string::String; - + pub use alloc_crate::vec::Vec; + #[cfg(target_arch = "wasm32")] pub fn run_ctors_once() { wit_bindgen::rt::run_ctors_once(); @@ -202,7 +213,7 @@ mod _rt { return; } let layout = alloc::Layout::from_size_align_unchecked(size, align); - alloc::dealloc(ptr as *mut u8, layout); + alloc::dealloc(ptr, layout); } extern crate alloc as alloc_crate; pub use alloc_crate::alloc; @@ -237,7 +248,7 @@ macro_rules! __export_the_world_impl { pub(crate) use __export_the_world_impl as export; #[cfg(target_arch = "wasm32")] -#[link_section = "component-type:wit-bindgen:0.27.0:the-world:encoded world"] +#[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\ @@ -246,7 +257,7 @@ A\x04\x01B\x06\x01@\x01\x01xs\x01\0\x04\0\x01a\x01\0\x01@\0\0s\x04\0\x01b\x01\x0 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.212.0\x10wit-bindgen-rust\x060.27.0"; +processed-by\x02\x0dwit-component\x070.214.0\x10wit-bindgen-rust\x060.28.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 3e4421166..dcc03329d 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -66,7 +66,13 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } - fn declare_import(&mut self, module_prefix: &str, 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 mut sig = "(".to_owned(); @@ -82,7 +88,8 @@ 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); + let export_name = String::from(module_prefix) + + &make_external_symbol(module_name, name, AbiVariant::GuestImport); uwrite!( self.src, " @@ -709,7 +716,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { let val = format!("vec{}", tmp); let ptr = format!("ptr{}", tmp); let len = format!("len{}", tmp); - if realloc.is_none() || self.gen.gen.opts.symmetric { + if realloc.is_none() || (self.gen.in_import && self.gen.gen.opts.symmetric) { self.push_str(&format!("let {} = {};\n", val, operands[0])); } else { let op0 = format!("{}.into_bytes()", operands[0]); @@ -717,7 +724,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } self.push_str(&format!("let {} = {}.as_ptr().cast::();\n", ptr, val)); self.push_str(&format!("let {} = {}.len();\n", len, val)); - if realloc.is_some() && !self.gen.gen.opts.symmetric { + if realloc.is_some() && !(self.gen.in_import && self.gen.gen.opts.symmetric) { self.push_str(&format!("::core::mem::forget({});\n", val)); } results.push(format!("{ptr}.cast_mut()")); @@ -838,7 +845,12 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::IterBasePointer => results.push("base".to_string()), - Instruction::CallWasm { name, sig, module_prefix, .. } => { + Instruction::CallWasm { + name, + sig, + module_prefix, + .. + } => { let func = self.declare_import(module_prefix, name, &sig.params, &sig.results); // ... then call the function with all our operands diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index b71c43e89..830496bde 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -378,22 +378,23 @@ macro_rules! {macro_name} {{ } } + pub fn align_area(&mut self, alignment: Alignment) { + match alignment { + Alignment::Pointer => uwriteln!( + self.src, + "#[cfg_attr(target_pointer_width=\"64\", repr(align(8)))] + #[cfg_attr(target_pointer_width=\"32\", repr(align(4)))]" + ), + Alignment::Bytes(bytes) => { + uwriteln!(self.src, "#[repr(align({align}))]", align = bytes.get()) + } + } + } + pub fn finish(&mut self) -> String { if !self.return_pointer_area_size.is_empty() { - match self.return_pointer_area_align { - Alignment::Pointer => uwriteln!( - self.src, - "\ - #[cgf_attr(target_pointer_width=\"64\", repr(align(8))] - #[cgf_attr(target_pointer_width=\"32\", repr(align(4))]" - ), - Alignment::Bytes(bytes) => uwriteln!( - self.src, - "\ - #[repr(align({align}))]", - align = bytes.get() - ), - } + uwriteln!(self.src,); + self.align_area(self.return_pointer_area_align); uwrite!( self.src, "struct _RetArea([::core::mem::MaybeUninit::; {size}]); @@ -851,14 +852,13 @@ impl {async_support}::StreamPayload for {name} {{ } assert!(handle_decls.is_empty()); if !import_return_pointer_area_size.is_empty() { + uwriteln!(self.src,); + self.align_area(import_return_pointer_area_align); uwrite!( self.src, - "\ - #[repr(align({import_return_pointer_area_align}))] - struct RetArea([::core::mem::MaybeUninit::; {import_return_pointer_area_size}]); + "struct RetArea([::core::mem::MaybeUninit::; {import_return_pointer_area_size}]); let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); {import_return_pointer_area_size}]); -", import_return_pointer_area_size = import_return_pointer_area_size.format(POINTER_SIZE_EXPRESSION), -import_return_pointer_area_align = import_return_pointer_area_align.format(POINTER_SIZE_EXPRESSION) +", import_return_pointer_area_size = import_return_pointer_area_size.format(POINTER_SIZE_EXPRESSION) ); } self.src.push_str(&String::from(src)); @@ -911,7 +911,11 @@ import_return_pointer_area_align = import_return_pointer_area_align.format(POINT abi::call( f.gen.resolve, AbiVariant::GuestExport, - LiftLower::LiftArgsLowerResults, + if f.gen.gen.opts.symmetric { + LiftLower::Symmetric + } else { + LiftLower::LiftArgsLowerResults + }, func, &mut f, async_, @@ -1050,7 +1054,12 @@ import_return_pointer_area_align = import_return_pointer_area_align.format(POINT fn print_export_sig(&mut self, func: &Function) -> Vec { 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); From 5e8a2e34dfd638254716b0817492b8a7aedbdaa5 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 26 Jul 2024 12:12:57 +0100 Subject: [PATCH 298/672] format the Rust code --- .../meshless_strings/rust_comp_a/build.rs | 5 +- .../meshless_strings/rust_comp_a/src/main.rs | 6 +- .../rust_comp_a/src/the_world.rs | 379 ++++++++++-------- 3 files changed, 213 insertions(+), 177 deletions(-) diff --git a/crates/cpp/tests/meshless_strings/rust_comp_a/build.rs b/crates/cpp/tests/meshless_strings/rust_comp_a/build.rs index e1076e6bf..f6026a6f7 100644 --- a/crates/cpp/tests/meshless_strings/rust_comp_a/build.rs +++ b/crates/cpp/tests/meshless_strings/rust_comp_a/build.rs @@ -2,7 +2,10 @@ 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-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 index 5dbc31c74..c211223fe 100644 --- a/crates/cpp/tests/meshless_strings/rust_comp_a/src/main.rs +++ b/crates/cpp/tests/meshless_strings/rust_comp_a/src/main.rs @@ -1,12 +1,12 @@ -use the_world::foo::foo::strings; 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,) { + fn a(x: String) { println!("{x}"); } @@ -14,7 +14,7 @@ impl Guest for MyWorld { String::from("hello B") } - fn c(a: String,b: String,) -> String { + fn c(a: String, b: String) -> String { println!("{a}|{b}"); "hello C".into() } 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 index 617e95fae..8b4688b4e 100644 --- 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 @@ -2,167 +2,203 @@ // 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::(); - 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::; (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::(); - #[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::(); - let len3 = l2; - let string3 = String::from(std::str::from_utf8(std::slice::from_raw_parts(l1, len3)).unwrap()); + #[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::(); + let len0 = vec0.len(); - #[link(wasm_import_module = "cabi_post_foo:foo/strings")] - extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "b")] - fn cabi_post_fooX3AfooX2FstringsX00b(_: *mut u8, ); - } - cabi_post_fooX3AfooX2FstringsX00b(ptr0); - string3 - } - } - #[allow(unused_unsafe, clippy::all)] - pub fn c(a: &str,b: &str,) -> _rt::String{ - unsafe { + #[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; (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::(); + #[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::(); + let len3 = l2; + let string3 = String::from( + std::str::from_utf8(std::slice::from_raw_parts(l1, len3)).unwrap(), + ); - #[cfg_attr(target_pointer_width="64", repr(align(8)))] - #[cfg_attr(target_pointer_width="32", repr(align(4)))] - struct RetArea([::core::mem::MaybeUninit::; (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::(); - let len0 = vec0.len(); - let vec1 = b; - let ptr1 = vec1.as_ptr().cast::(); - let len1 = vec1.len(); - let ptr2 = ret_area.0.as_mut_ptr().cast::(); - #[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::(); - let len5 = l4; - let string5 = String::from(std::str::from_utf8(std::slice::from_raw_parts(l3, len5)).unwrap()); + #[link(wasm_import_module = "cabi_post_foo:foo/strings")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "b")] + fn cabi_post_fooX3AfooX2FstringsX00b(_: *mut u8); + } + cabi_post_fooX3AfooX2FstringsX00b(ptr0); + string3 + } + } + #[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; (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::(); + let len0 = vec0.len(); + let vec1 = b; + let ptr1 = vec1.as_ptr().cast::(); + let len1 = vec1.len(); + let ptr2 = ret_area.0.as_mut_ptr().cast::(); + #[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::(); + let len5 = l4; + let string5 = String::from( + std::str::from_utf8(std::slice::from_raw_parts(l3, len5)).unwrap(), + ); - #[link(wasm_import_module = "cabi_post_foo:foo/strings")] - extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "c")] - fn cabi_post_fooX3AfooX2FstringsX00c(_: *mut u8, ); - } - cabi_post_fooX3AfooX2FstringsX00c(ptr2); - string5 + #[link(wasm_import_module = "cabi_post_foo:foo/strings")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "c")] + fn cabi_post_fooX3AfooX2FstringsX00c(_: *mut u8); + } + cabi_post_fooX3AfooX2FstringsX00c(ptr2); + string5 + } + } } - } - } - - } } #[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(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(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::(); - let len1 = vec1.len(); - ::core::mem::forget(vec1); - *arg0.add(core::mem::size_of::<*const u8>()).cast::() = len1; - *arg0.add(0).cast::<*mut u8>() = ptr1.cast_mut(); - } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn __post_return_b(arg0: *mut u8,) { - let l0 = *arg0.add(0).cast::<*mut u8>(); - let l1 = *arg0.add(core::mem::size_of::<*const u8>()).cast::(); - _rt::cabi_dealloc(l0, l1, 1); - } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn _export_c_cabi(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::(); - let len3 = vec3.len(); - ::core::mem::forget(vec3); - *arg4.add(core::mem::size_of::<*const u8>()).cast::() = len3; - *arg4.add(0).cast::<*mut u8>() = ptr3.cast_mut(); - } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn __post_return_c(arg0: *mut u8,) { - let l0 = *arg0.add(0).cast::<*mut u8>(); - let l1 = *arg0.add(core::mem::size_of::<*const u8>()).cast::(); - _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)] + #[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(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(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::(); + let len1 = vec1.len(); + ::core::mem::forget(vec1); + *arg0.add(core::mem::size_of::<*const u8>()).cast::() = len1; + *arg0.add(0).cast::<*mut u8>() = ptr1.cast_mut(); + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __post_return_b(arg0: *mut u8) { + let l0 = *arg0.add(0).cast::<*mut u8>(); + let l1 = *arg0.add(core::mem::size_of::<*const u8>()).cast::(); + _rt::cabi_dealloc(l0, l1, 1); + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_c_cabi( + 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::(); + let len3 = vec3.len(); + ::core::mem::forget(vec3); + *arg4.add(core::mem::size_of::<*const u8>()).cast::() = len3; + *arg4.add(0).cast::<*mut u8>() = ptr3.cast_mut(); + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __post_return_c(arg0: *mut u8) { + let l0 = *arg0.add(0).cast::<*mut u8>(); + let l1 = *arg0.add(core::mem::size_of::<*const u8>()).cast::(); + _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{ + 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")] @@ -192,31 +228,29 @@ pub mod exports { } };); } - #[doc(hidden)] - pub(crate) use __export_foo_foo_strings_cabi; - -} - -} -} + #[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 use alloc_crate::string::String; + pub use alloc_crate::vec::Vec; - #[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; + #[cfg(target_arch = "wasm32")] + pub fn run_ctors_once() { + wit_bindgen::rt::run_ctors_once(); } - 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; + 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 @@ -263,6 +297,5 @@ processed-by\x02\x0dwit-component\x070.214.0\x10wit-bindgen-rust\x060.28.0"; #[doc(hidden)] #[cfg(target_arch = "wasm32")] pub fn __link_custom_section_describing_imports() { - wit_bindgen::rt::maybe_link_cabi_realloc(); + wit_bindgen::rt::maybe_link_cabi_realloc(); } - From 57b6b33ec9788e9b3a3ccb238f4f398d9ea734b8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 26 Jul 2024 13:05:51 +0100 Subject: [PATCH 299/672] first half of ptr sized handles --- crates/rust/src/lib.rs | 81 +++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index fefe8a240..5e9367174 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -550,12 +550,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. @@ -570,36 +570,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 { - // NB: This would ideally be `u32` but it is not. The fact that this has +pub struct Resource {{ + // 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, -} +}} /// 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 Resource { +impl Resource {{ #[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`. /// @@ -614,41 +614,48 @@ impl Resource { /// `take_handle` should only be exposed internally to generated code, not /// to user code. #[doc(hidden)] - pub fn take_handle(resource: &Resource) -> u32 { - resource.handle.swap(u32::MAX, Relaxed) - } + pub fn take_handle(resource: &Resource) -> {handle_type} {{ + resource.handle.swap({invalid_value}, Relaxed) + }} #[doc(hidden)] - pub fn handle(resource: &Resource) -> u32 { + pub fn handle(resource: &Resource) -> {handle_type} {{ resource.handle.load(Relaxed) - } -} + }} +}} -impl fmt::Debug for Resource { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl fmt::Debug for Resource {{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {{ f.debug_struct("Resource") .field("handle", &self.handle) .finish() - } -} + }} +}} -impl Drop for Resource { - fn drop(&mut self) { - unsafe { - match self.handle.load(Relaxed) { +impl Drop for Resource {{ + 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" } + )); } RuntimeItem::AsyncSupport => { From a3128b7c06ff786b3a18bcb877fc94927c0d89f8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 26 Jul 2024 13:31:07 +0100 Subject: [PATCH 300/672] remove some casts --- crates/rust/src/bindgen.rs | 37 +++++++++++++++++++++---- crates/rust/src/interface.rs | 52 +++++++++++++++++++++++++----------- 2 files changed, 68 insertions(+), 21 deletions(-) diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index dcc03329d..24f07f900 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -420,7 +420,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.gen.gen.opts.symmetric { + "" + } else { + " as i32" + } + ); results.push(result); } @@ -429,7 +436,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.gen.gen.opts.symmetric { + "" + } else { + " as i32" + } + )) } Instruction::HandleLift { handle, .. } => { @@ -443,7 +457,15 @@ impl Bindgen for FunctionBindgen<'_, '_> { let result = if is_own { let name = self.gen.type_path(dealiased_resource, true); - format!("{name}::from_handle({op} as u32)") + + format!( + "{name}::from_handle({op}{cast})", + cast = if self.gen.gen.opts.symmetric { + "" + } else { + " as u32" + } + ) } else if self.gen.is_exported_resource(*resource) { let name = resolve.types[*resource] .name @@ -457,9 +479,14 @@ impl Bindgen for FunctionBindgen<'_, '_> { let name = self.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.gen.gen.opts.symmetric { + "" + } else { + " as u32" + } ) }; results.push(result); diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 830496bde..7d4264824 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -227,32 +227,37 @@ impl InterfaceGenerator<'_> { self.src, r#" #[doc(hidden)] -unsafe fn _resource_new(val: *mut u8) -> u32 +unsafe fn _resource_new(val: *mut u8) -> {handle_type} where Self: Sized {{ #[link(wasm_import_module = "[export]{module}")] extern "C" {{ #[cfg_attr(target_arch = "wasm32", link_name = "[resource-new]{resource_name}")] - fn {external_new}(_: *mut u8) -> u32; + fn {external_new}(_: *mut u8) -> {handle_type}; }} {external_new}(val) }} #[doc(hidden)] -fn _resource_rep(handle: u32) -> *mut u8 +fn _resource_rep(handle: {handle_type}) -> *mut u8 where Self: Sized {{ #[link(wasm_import_module = "[export]{module}")] extern "C" {{ #[cfg_attr(target_arch = "wasm32", link_name = "[resource-rep]{resource_name}")] - fn {external_rep}(_: u32) -> *mut u8; + fn {external_rep}(_: {handle_type}) -> *mut u8; }} unsafe {{ {external_rep}(handle) }} }} - "# + "#, + handle_type = if self.gen.opts.symmetric { + "usize" + } else { + "u32" + } ); for method in methods { self.src.push_str(method); @@ -2334,23 +2339,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: {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 { @@ -2407,19 +2417,19 @@ impl {camel} {{ }} #[doc(hidden)] - pub unsafe fn from_handle(handle: u32) -> Self {{ + pub unsafe fn from_handle(handle: {handle_type}) -> Self {{ Self {{ handle: {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) }} @@ -2483,7 +2493,12 @@ impl<'a> {camel}Borrow<'a>{{ self.rep.cast() }} }} - "# + "#, + handle_type = if self.gen.opts.symmetric { + "usize" + } else { + "u32" + } ); format!("[export]{module}") }; @@ -2499,19 +2514,24 @@ impl<'a> {camel}Borrow<'a>{{ r#" unsafe impl {wasm_resource} for {camel} {{ #[inline] - unsafe fn drop(_handle: u32) {{ + unsafe fn drop(_handle: {handle_type}) {{ {{ #[link(wasm_import_module = "{wasm_import_module}")] extern "C" {{ #[cfg_attr(target_arch = "wasm32", link_name = "[resource-drop]{name}")] - fn {export_name}(_: u32); + fn {export_name}(_: {handle_type}); }} {export_name}(_handle); }} }} }} - "# + "#, + handle_type = if self.gen.opts.symmetric { + "usize" + } else { + "u32" + } ); } From 1dbfd83821a94642e4a74cc4e426128b2c5e1132 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 26 Jul 2024 19:48:46 +0100 Subject: [PATCH 301/672] add some compatibility casts, meshless res import fully functional --- .../meshless_resources/rust_comp_a/src/a.rs | 35 +++++++++---------- crates/rust/src/bindgen.rs | 6 ++-- 2 files changed, 20 insertions(+), 21 deletions(-) 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 index aad24d720..d2dd5e071 100644 --- a/crates/cpp/tests/meshless_resources/rust_comp_a/src/a.rs +++ b/crates/cpp/tests/meshless_resources/rust_comp_a/src/a.rs @@ -1,6 +1,5 @@ -// Generated by `wit-bindgen` 0.27.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.28.0. DO NOT EDIT! // Options used: -// * with "foo:foo/resources" = generate #[allow(dead_code)] pub mod foo { #[allow(dead_code)] @@ -28,12 +27,12 @@ pub mod foo { } #[doc(hidden)] - pub fn take_handle(&self) -> usize { + pub fn take_handle(&self) -> usize{ _rt::Resource::take_handle(&self.handle) } #[doc(hidden)] - pub fn handle(&self) -> usize { + pub fn handle(&self) -> usize{ _rt::Resource::handle(&self.handle) } } @@ -62,24 +61,24 @@ pub mod foo { #[link(wasm_import_module = "foo:foo/resources")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "[constructor]r")] - fn fooX3AfooX2FresourcesX00X5BconstructorX5Dr(_: i32, ) -> usize; + fn fooX3AfooX2FresourcesX00X5BconstructorX5Dr(_: i32, ) -> *mut u8; } let ret = fooX3AfooX2FresourcesX00X5BconstructorX5Dr(_rt::as_i32(&a)); - R::from_handle(ret) + R::from_handle(ret as usize) } } } impl R { #[allow(unused_unsafe, clippy::all)] - pub fn add(&self,b: u32,){ + 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(_: usize, _: i32, ); + fn fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(_: *mut u8, _: i32, ); } - fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd((self).handle(), _rt::as_i32(&b)); + fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd((self).handle() as *mut u8, _rt::as_i32(&b)); } } } @@ -90,23 +89,23 @@ pub mod foo { #[link(wasm_import_module = "foo:foo/resources")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "create")] - fn fooX3AfooX2FresourcesX00create() -> usize; + fn fooX3AfooX2FresourcesX00create() -> *mut u8; } let ret = fooX3AfooX2FresourcesX00create(); - R::from_handle(ret) + R::from_handle(ret as usize) } } #[allow(unused_unsafe, clippy::all)] /// borrows: func(o: borrow); - pub fn consume(o: 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(_: usize, ); + fn fooX3AfooX2FresourcesX00consume(_: *mut u8, ); } - fooX3AfooX2FresourcesX00consume((&o).take_handle()); + fooX3AfooX2FresourcesX00consume((&o).take_handle() as *mut u8); } } @@ -135,12 +134,12 @@ mod _rt { /// resources. #[repr(transparent)] pub struct Resource { - // NB: This would ideally be `u32` but it is not. The fact that this has + // 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 `u32::MAX`. + // invalid it's stored as `0`. handle: AtomicUsize, _marker: marker::PhantomData, } @@ -284,7 +283,7 @@ mod _rt { } #[cfg(target_arch = "wasm32")] -#[link_section = "component-type:wit-bindgen:0.27.0:a:encoded world"] +#[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\ @@ -293,7 +292,7 @@ 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.212.0\x10wit-bindgen-rust\x060.27.0"; +0.214.0\x10wit-bindgen-rust\x060.28.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 24f07f900..ec8da8836 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -423,7 +423,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { let result = format!( "({op}).take_handle(){cast}", cast = if self.gen.gen.opts.symmetric { - "" + " as *mut u8" } else { " as i32" } @@ -439,7 +439,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { results.push(format!( "({op}).handle(){cast}", cast = if self.gen.gen.opts.symmetric { - "" + " as *mut u8" } else { " as i32" } @@ -461,7 +461,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { format!( "{name}::from_handle({op}{cast})", cast = if self.gen.gen.opts.symmetric { - "" + " as usize" } else { " as u32" } From 860b1d34959e111ddd67d4c5429ccdd540d644e8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 28 Jul 2024 20:40:04 +0200 Subject: [PATCH 302/672] first fully working symmetric resource generation --- .../meshless_resources/rust_comp_b/src/b.rs | 187 +++++++----------- crates/rust/src/interface.rs | 117 ++++++++--- 2 files changed, 160 insertions(+), 144 deletions(-) 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 index 9583a1e1c..77eb00084 100644 --- a/crates/cpp/tests/meshless_resources/rust_comp_b/src/b.rs +++ b/crates/cpp/tests/meshless_resources/rust_comp_b/src/b.rs @@ -1,6 +1,3 @@ -// Generated by `wit-bindgen` 0.27.0. DO NOT EDIT! -// Options used: -// * with "foo:foo/resources" = generate #[allow(dead_code)] pub mod exports { #[allow(dead_code)] @@ -14,16 +11,13 @@ pub mod exports { #[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, } - type _RRep = Option; - impl R { /// Creates a new resource from the specified representation. /// @@ -36,74 +30,64 @@ pub mod exports { let ptr: *mut _RRep = _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(&self) -> &T { let ptr = unsafe { &*self.as_ptr::() }; ptr.as_ref().unwrap() } - /// Gets mutable access to the underlying `T` which represents this /// resource. pub fn get_mut(&mut self) -> &mut T { let ptr = unsafe { &mut *self.as_ptr::() }; ptr.as_mut().unwrap() } - /// Consumes this resource and returns the underlying `T`. pub fn into_inner(self) -> T { let ptr = unsafe { &mut *self.as_ptr::() }; 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 `GuestR` trait twice - // so guard against using it with two different types here. #[doc(hidden)] fn type_guard() { use core::any::TypeId; static mut LAST_TYPE: Option = None; unsafe { - //assert!(!cfg!(target_feature = "threads")); + assert!(!cfg!(target_feature = "threads")); let id = TypeId::of::(); match LAST_TYPE { - Some(ty) => assert!( - ty == id, - "cannot use two types with this resource 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(handle: *mut u8) { Self::type_guard::(); let _ = _rt::Box::from_raw(handle as *mut _RRep); } - fn as_ptr(&self) -> *mut _RRep { R::type_guard::(); T::_resource_rep(self.handle()).cast() } } - /// A borrowed version of [`R`] which represents a borrowed value /// with the lifetime `'a`. #[derive(Debug)] @@ -112,7 +96,6 @@ pub mod exports { rep: *mut u8, _marker: core::marker::PhantomData<&'a R>, } - impl<'a> RBorrow<'a> { #[doc(hidden)] pub unsafe fn lift(rep: usize) -> Self { @@ -121,80 +104,72 @@ pub mod exports { _marker: core::marker::PhantomData, } } - /// Gets access to the underlying `T` in this resource. pub fn get(&self) -> &T { let ptr = unsafe { &mut *self.as_ptr::() }; 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(&self) -> *mut _RRep { R::type_guard::(); self.rep.cast() } } - unsafe impl _rt::WasmResource for R { #[inline] - unsafe fn drop(handle: usize) { - extern "C" { - fn fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(arg0: usize); + 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); } - unsafe { fooX3AfooX2FresourcesX00X5Bresource_dropX5Dr(handle) }; } } - #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_constructor_r_cabi(arg0: i32) -> usize { + pub unsafe fn _export_constructor_r_cabi(arg0: i32) -> *mut u8 { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); let result0 = R::new(T::new(arg0 as u32)); - (result0).take_handle() + (result0).take_handle() as *mut u8 } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_method_r_add_cabi(arg0: usize, arg1: i32) { + pub unsafe fn _export_method_r_add_cabi(arg0: *mut u8, arg1: i32) { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); - let r = R::from_handle(arg0); - let a = r.as_ptr::(); - r.take_handle(); - (*a).as_ref().unwrap().add(arg1 as u32); + T::add(RBorrow::lift(arg0 as usize).get(), arg1 as u32); } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_create_cabi() -> usize { + pub unsafe fn _export_create_cabi() -> *mut u8 { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); let result0 = T::create(); - (result0).take_handle() + (result0).take_handle() as *mut u8 } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_consume_cabi(arg0: usize) { + pub unsafe fn _export_consume_cabi(arg0: *mut u8) { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); - T::consume(R::from_handle(arg0)); + T::consume(R::from_handle(arg0 as usize)); + } + pub trait Guest { + type R: GuestR; + fn create() -> R; + /// borrows: func(o: borrow); + fn consume(o: R) -> (); } - #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_drop_cabi(arg0: usize) { + pub unsafe fn _export_drop_r_cabi(arg0: usize) { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); R::dtor::(arg0 as *mut u8); } - - pub trait Guest { - type R: GuestR; - fn create() -> R; - /// borrows: func(o: borrow); - fn consume(o: R); - } pub trait GuestR: 'static { #[doc(hidden)] unsafe fn _resource_new(val: *mut u8) -> usize @@ -203,7 +178,6 @@ pub mod exports { { val as usize } - #[doc(hidden)] fn _resource_rep(handle: usize) -> *mut u8 where @@ -211,42 +185,37 @@ pub mod exports { { handle as *mut u8 } - fn new(a: u32) -> Self; - fn add(&self, b: u32); + 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 fooX3AfooX2FresourcesX00X5BconstructorX5Dr(arg0: i32,) -> usize { - $($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 fooX3AfooX2FresourcesX00X5BmethodX5DrX2Eadd(arg0: usize,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 fooX3AfooX2FresourcesX00create() -> usize { - $($path_to_types)*::_export_create_cabi::<$ty>() - } - #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/resources#consume")] - #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn fooX3AfooX2FresourcesX00consume(arg0: usize,) { - $($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_cabi::<<$ty as $($path_to_types)*::Guest>::R>(arg0) - } - };); -} + 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; } @@ -254,11 +223,9 @@ pub mod exports { } } 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. /// @@ -273,16 +240,9 @@ mod _rt { /// resources. #[repr(transparent)] pub struct Resource { - // 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: AtomicUsize, _marker: marker::PhantomData, } - /// A trait which all wasm resources implement, namely providing the ability to /// drop a resource. /// @@ -292,7 +252,6 @@ mod _rt { /// Invokes the `[resource-drop]...` intrinsic. unsafe fn drop(handle: usize); } - impl Resource { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { @@ -302,7 +261,6 @@ mod _rt { _marker: marker::PhantomData, } } - /// Takes ownership of the handle owned by `resource`. /// /// Note that this ideally would be `into_handle` taking `Resource` by @@ -319,13 +277,11 @@ mod _rt { pub fn take_handle(resource: &Resource) -> usize { resource.handle.swap(0, Relaxed) } - #[doc(hidden)] pub fn handle(resource: &Resource) -> usize { resource.handle.load(Relaxed) } } - impl fmt::Debug for Resource { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Resource") @@ -333,31 +289,23 @@ mod _rt { .finish() } } - impl Drop for Resource { 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(); } extern crate alloc as alloc_crate; } - /// Generates `#[no_mangle]` functions to export the specified type as the /// root implementation of all generated traits. /// @@ -376,18 +324,20 @@ mod _rt { /// ``` #[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); - ) + ($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.27.0:b:encoded world"] +#[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\ @@ -396,8 +346,7 @@ 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.212.0\x10wit-bindgen-rust\x060.27.0"; - +0.214.0\x10wit-bindgen-rust\x060.28.0"; #[inline(never)] #[doc(hidden)] #[cfg(target_arch = "wasm32")] diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 7d4264824..80a09d75c 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -208,9 +208,24 @@ impl InterfaceGenerator<'_> { } 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(arg0: usize) {{ + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + {resource_type}::dtor::(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); let external_new = make_external_symbol( @@ -223,9 +238,31 @@ impl InterfaceGenerator<'_> { &(String::from("[resource-rep]") + &resource_name), AbiVariant::GuestImport, ); - uwriteln!( - self.src, - r#" + if self.gen.opts.symmetric { + uwriteln!( + self.src, + r#" + #[doc(hidden)] + unsafe fn _resource_new(val: *mut u8) -> {handle_type} + where Self: Sized + {{ + val as {handle_type} + }} + + #[doc(hidden)] + fn _resource_rep(handle: {handle_type}) -> *mut u8 + where Self: Sized + {{ + 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 @@ -253,15 +290,13 @@ fn _resource_rep(handle: {handle_type}) -> *mut u8 }} "#, - handle_type = if self.gen.opts.symmetric { - "usize" - } else { - "u32" - } - ); + handle_type = "u32" + ); + } for method in methods { self.src.push_str(method); } + uwriteln!(self.src, "}}"); } @@ -323,14 +358,29 @@ macro_rules! {macro_name} {{ Identifier::None | Identifier::World(_) => unreachable!(), }; let camel = name.to_upper_camel_case(); - let dtor_symbol = make_external_symbol( - &module, - &(String::from("[dtor]") + &name), - AbiVariant::GuestExport, - ); - 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)] #[cfg_attr(target_arch = "wasm32", export_name = "{export_prefix}{module}#[dtor]{name}")] @@ -343,7 +393,8 @@ macro_rules! {macro_name} {{ }} }}; "# - ); + ); + } } uwriteln!(self.src, "}};);"); uwriteln!(self.src, "}}"); @@ -992,13 +1043,25 @@ impl {async_support}::StreamPayload for {name} {{ Identifier::None => unreachable!(), }; let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); - let export_name = func.core_export_name(wasm_module_export_name.as_deref()); - let export_name = if async_ { - format!("[async]{export_name}") + let (export_name, external_name) = if self.gen.opts.symmetric { + let export_name = func.name.clone(); // item_name().to_owned(); + let external_name = make_external_symbol( + &wasm_module_export_name.unwrap_or_default(), + &func.name, + AbiVariant::GuestImport, + ); + (export_name, external_name) } else { - export_name.to_string() + let export_name = func.core_export_name(wasm_module_export_name.as_deref()); + let export_name = if async_ { + format!("[async]{export_name}") + } else { + export_name.to_string() + }; + let external_name = + make_external_component(&(String::from(export_prefix) + &export_name)); + (export_name, external_name) }; - let external_name = make_external_component(&(String::from(export_prefix) + &export_name)); uwrite!( self.src, "\ @@ -2500,7 +2563,11 @@ impl<'a> {camel}Borrow<'a>{{ "u32" } ); - format!("[export]{module}") + if self.gen.opts.symmetric { + module.clone() + } else { + format!("[export]{module}") + } }; let wasm_resource = self.path_to_wasm_resource(); From df019cf327c97cc60b768076910b3116cd3672ba Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 1 Aug 2024 23:09:25 +0200 Subject: [PATCH 303/672] small merge fix and dependency update --- Cargo.lock | 90 +++++++++++++++++++++--------------------- src/bin/wit-bindgen.rs | 4 +- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a9d24eba6..9367d15ad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1433,10 +1433,10 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.214.0", + "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-core", "wit-component", - "wit-parser 0.214.0", + "wit-parser 0.215.0", ] [[package]] @@ -1710,26 +1710,26 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#fcbcd794d865bd52593a2cb6854c9b75b6ef3bbc" +version = "0.215.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb56df3e06b8e6b77e37d2969a50ba51281029a9aeb3855e76b7f49b6418847" dependencies = [ "leb128", - "wasmparser 0.214.0", ] [[package]] name = "wasm-encoder" version = "0.215.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb56df3e06b8e6b77e37d2969a50ba51281029a9aeb3855e76b7f49b6418847" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#3d92311bdd95208e835c0bbf1f0611ea7040db3e" dependencies = [ "leb128", + "wasmparser 0.215.0", ] [[package]] name = "wasm-metadata" -version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#fcbcd794d865bd52593a2cb6854c9b75b6ef3bbc" +version = "0.215.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#3d92311bdd95208e835c0bbf1f0611ea7040db3e" dependencies = [ "anyhow", "indexmap", @@ -1737,8 +1737,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.214.0", - "wasmparser 0.214.0", + "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.215.0", ] [[package]] @@ -1757,8 +1757,8 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#fcbcd794d865bd52593a2cb6854c9b75b6ef3bbc" +version = "0.215.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#3d92311bdd95208e835c0bbf1f0611ea7040db3e" dependencies = [ "ahash", "bitflags", @@ -1830,7 +1830,7 @@ dependencies = [ "wasmtime-slab", "wasmtime-versioned-export-macros", "wasmtime-winch", - "wat 1.215.0", + "wat 1.215.0 (registry+https://github.com/rust-lang/crates.io-index)", "windows-sys", ] @@ -2073,44 +2073,44 @@ dependencies = [ [[package]] name = "wast" -version = "214.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#fcbcd794d865bd52593a2cb6854c9b75b6ef3bbc" +version = "215.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff1d00d893593249e60720be04a7c1f42f1c4dc3806a2869f4e66ab61eb54cb" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.214.0", + "wasm-encoder 0.215.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wast" version = "215.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff1d00d893593249e60720be04a7c1f42f1c4dc3806a2869f4e66ab61eb54cb" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#3d92311bdd95208e835c0bbf1f0611ea7040db3e" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.215.0", + "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] name = "wat" -version = "1.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#fcbcd794d865bd52593a2cb6854c9b75b6ef3bbc" +version = "1.215.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670bf4d9c8cf76ae242d70ded47c546525b6dafaa6871f9bcb065344bf2b4e3d" dependencies = [ - "wast 214.0.0", + "wast 215.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wat" version = "1.215.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670bf4d9c8cf76ae242d70ded47c546525b6dafaa6871f9bcb065344bf2b4e3d" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#3d92311bdd95208e835c0bbf1f0611ea7040db3e" dependencies = [ - "wast 215.0.0", + "wast 215.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2322,11 +2322,11 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.214.0", + "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wit-bindgen-core", "wit-component", - "wit-parser 0.214.0", + "wit-parser 0.215.0", ] [[package]] @@ -2337,8 +2337,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.214.0", - "wasmparser 0.214.0", + "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.215.0", "wasmtime", "wasmtime-wasi", "wit-bindgen-bridge", @@ -2351,7 +2351,7 @@ dependencies = [ "wit-bindgen-rust", "wit-bindgen-teavm-java", "wit-component", - "wit-parser 0.214.0", + "wit-parser 0.215.0", ] [[package]] @@ -2360,7 +2360,7 @@ version = "0.28.0" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser 0.214.0", + "wit-parser 0.215.0", ] [[package]] @@ -2371,7 +2371,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.214.0", + "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", @@ -2387,12 +2387,12 @@ dependencies = [ "heck 0.5.0", "indexmap", "test-helpers", - "wasm-encoder 0.214.0", + "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", - "wasmparser 0.214.0", + "wasmparser 0.215.0", "wit-bindgen-core", "wit-component", - "wit-parser 0.214.0", + "wit-parser 0.215.0", ] [[package]] @@ -2473,8 +2473,8 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#fcbcd794d865bd52593a2cb6854c9b75b6ef3bbc" +version = "0.215.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#3d92311bdd95208e835c0bbf1f0611ea7040db3e" dependencies = [ "anyhow", "bitflags", @@ -2483,11 +2483,11 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.214.0", + "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", - "wasmparser 0.214.0", - "wat 1.214.0", - "wit-parser 0.214.0", + "wasmparser 0.215.0", + "wat 1.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.215.0", ] [[package]] @@ -2510,8 +2510,8 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.214.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#fcbcd794d865bd52593a2cb6854c9b75b6ef3bbc" +version = "0.215.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#3d92311bdd95208e835c0bbf1f0611ea7040db3e" dependencies = [ "anyhow", "id-arena", @@ -2522,7 +2522,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.214.0", + "wasmparser 0.215.0", ] [[package]] diff --git a/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs index 8bfde4256..6c97dbd9e 100644 --- a/src/bin/wit-bindgen.rs +++ b/src/bin/wit-bindgen.rs @@ -211,9 +211,9 @@ fn gen_world( resolve.features.insert(feature.to_string()); } } - let (pkgs, _files) = resolve.push_path(&opts.wit)?; + let (pkg, _files) = resolve.push_path(&opts.wit)?; resolve.add_future_and_stream_results(); - let world = resolve.select_world(&pkgs, opts.world.as_deref())?; + let world = resolve.select_world(pkg, opts.world.as_deref())?; generator.generate(&resolve, world, files)?; Ok(()) From a2d05a74da39d1fcc8b17a380354ade5448a733e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 1 Aug 2024 23:34:33 +0200 Subject: [PATCH 304/672] simplify the logic by using owned results --- crates/cpp/DESIGN.md | 8 +-- .../component_a/the_world.cpp | 32 +---------- .../component_b/the_world.cpp | 32 +---------- .../rust_comp_a/src/the_world.rs | 55 ++++--------------- 4 files changed, 18 insertions(+), 109 deletions(-) diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md index da1ecce29..c541d65b2 100644 --- a/crates/cpp/DESIGN.md +++ b/crates/cpp/DESIGN.md @@ -72,11 +72,9 @@ For now for functions the following convention is used in both directions: - (unchanged) If there are too many (>1) flat results, a local uninitialized ret_area is passed via the last argument - - (change) Returned strings and lists become views, valid until a - cabi_post on the ret_area ptr which was passed to the first call. - This is mostly a concession to functional safety, avoiding all - allocations in the hot path. Caller provided buffers or custom realloc - would solve this in a different way. + - (unchanged) Returned objects are owned. + For functional safety, i.e. avoiding all + allocations in the hot path, the hope is with #385. - The imported resource ABI is used also for exporting with one modification: diff --git a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp index 716f97cf5..dba6afa23 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp @@ -31,15 +31,9 @@ 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("cabi_post_foo:foo/strings"))) -__attribute__((import_name("b"))) void -cabi_post_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 *); -extern "C" __attribute__((import_module("cabi_post_foo:foo/strings"))) -__attribute__((import_name("c"))) void -cabi_post_fooX3AfooX2FstringsX00c(uint8_t *); void comp_a::foo::foo::strings::A(std::string_view x) { auto const &vec0 = x; auto ptr0 = (uint8_t *)(vec0.data()); @@ -53,10 +47,7 @@ wit::string comp_a::foo::foo::strings::B() { fooX3AfooX2FstringsX00b(ptr0); auto len1 = *((size_t *)(ptr0 + sizeof(void *))); - auto string1 = wit::string::from_view( - std::string_view((char const *)(*((uint8_t **)(ptr0 + 0))), len1)); - - cabi_post_fooX3AfooX2FstringsX00b(ptr0); + auto string1 = wit::string((char const *)(*((uint8_t **)(ptr0 + 0))), len1); return string1; } wit::string comp_a::foo::foo::strings::C(std::string_view a, @@ -73,10 +64,7 @@ wit::string comp_a::foo::foo::strings::C(std::string_view a, fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2); auto len3 = *((size_t *)(ptr2 + sizeof(void *))); - auto string3 = wit::string::from_view( - std::string_view((char const *)(*((uint8_t **)(ptr2 + 0))), len3)); - - cabi_post_fooX3AfooX2FstringsX00c(ptr2); + auto string3 = wit::string((char const *)(*((uint8_t **)(ptr2 + 0))), len3); return string3; } extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void @@ -99,14 +87,6 @@ a_fooX3AfooX2FstringsX00b(uint8_t *arg0) { *((size_t *)(arg0 + sizeof(void *))) = len1; *((uint8_t **)(arg0 + 0)) = ptr1; } -extern "C" - __attribute__((__weak__, - __export_name__("cabi_post_fooX3AfooX2FstringsX00b"))) void - a_cabi_post_fooX3AfooX2FstringsX00b(uint8_t *retptr) { - if ((*((size_t *)(retptr + sizeof(void *)))) > 0) { - wit::string::drop_raw((void *)(*((uint8_t **)(retptr + 0)))); - } -} 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) { @@ -130,13 +110,5 @@ a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3 *((size_t *)(arg4 + sizeof(void *))) = len3; *((uint8_t **)(arg4 + 0)) = ptr3; } -extern "C" - __attribute__((__weak__, - __export_name__("cabi_post_fooX3AfooX2FstringsX00c"))) void - a_cabi_post_fooX3AfooX2FstringsX00c(uint8_t *retptr) { - if ((*((size_t *)(retptr + sizeof(void *)))) > 0) { - wit::string::drop_raw((void *)(*((uint8_t **)(retptr + 0)))); - } -} // Component Adapters diff --git a/crates/cpp/tests/meshless_strings/component_b/the_world.cpp b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp index 6465c665f..b3c848c71 100644 --- a/crates/cpp/tests/meshless_strings/component_b/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp @@ -31,15 +31,9 @@ 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("cabi_post_foo:foo/strings"))) -__attribute__((import_name("b"))) void -a_cabi_post_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 *); -extern "C" __attribute__((import_module("cabi_post_foo:foo/strings"))) -__attribute__((import_name("c"))) void -a_cabi_post_fooX3AfooX2FstringsX00c(uint8_t *); void foo::foo::strings::A(std::string_view x) { auto const &vec0 = x; auto ptr0 = (uint8_t *)(vec0.data()); @@ -53,10 +47,7 @@ wit::string foo::foo::strings::B() { a_fooX3AfooX2FstringsX00b(ptr0); auto len1 = *((size_t *)(ptr0 + sizeof(void *))); - auto string1 = wit::string::from_view( - std::string_view((char const *)(*((uint8_t **)(ptr0 + 0))), len1)); - - a_cabi_post_fooX3AfooX2FstringsX00b(ptr0); + auto string1 = wit::string((char const *)(*((uint8_t **)(ptr0 + 0))), len1); return string1; } wit::string foo::foo::strings::C(std::string_view a, std::string_view b) { @@ -72,10 +63,7 @@ wit::string foo::foo::strings::C(std::string_view a, std::string_view b) { a_fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2); auto len3 = *((size_t *)(ptr2 + sizeof(void *))); - auto string3 = wit::string::from_view( - std::string_view((char const *)(*((uint8_t **)(ptr2 + 0))), len3)); - - a_cabi_post_fooX3AfooX2FstringsX00c(ptr2); + auto string3 = wit::string((char const *)(*((uint8_t **)(ptr2 + 0))), len3); return string3; } extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void @@ -98,14 +86,6 @@ fooX3AfooX2FstringsX00b(uint8_t *arg0) { *((size_t *)(arg0 + sizeof(void *))) = len1; *((uint8_t **)(arg0 + 0)) = ptr1; } -extern "C" - __attribute__((__weak__, - __export_name__("cabi_post_fooX3AfooX2FstringsX00b"))) void - cabi_post_fooX3AfooX2FstringsX00b(uint8_t *retptr) { - if ((*((size_t *)(retptr + sizeof(void *)))) > 0) { - wit::string::drop_raw((void *)(*((uint8_t **)(retptr + 0)))); - } -} 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) { @@ -129,13 +109,5 @@ fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3, *((size_t *)(arg4 + sizeof(void *))) = len3; *((uint8_t **)(arg4 + 0)) = ptr3; } -extern "C" - __attribute__((__weak__, - __export_name__("cabi_post_fooX3AfooX2FstringsX00c"))) void - cabi_post_fooX3AfooX2FstringsX00c(uint8_t *retptr) { - if ((*((size_t *)(retptr + sizeof(void *)))) > 0) { - wit::string::drop_raw((void *)(*((uint8_t **)(retptr + 0)))); - } -} // Component Adapters 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 index 8b4688b4e..e365b727d 100644 --- 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 @@ -49,16 +49,8 @@ pub mod foo { let l1 = *ptr0.add(0).cast::<*mut u8>(); let l2 = *ptr0.add(core::mem::size_of::<*const u8>()).cast::(); let len3 = l2; - let string3 = String::from( - std::str::from_utf8(std::slice::from_raw_parts(l1, len3)).unwrap(), - ); - - #[link(wasm_import_module = "cabi_post_foo:foo/strings")] - extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "b")] - fn cabi_post_fooX3AfooX2FstringsX00b(_: *mut u8); - } - cabi_post_fooX3AfooX2FstringsX00b(ptr0); + let bytes3 = _rt::Vec::from_raw_parts(l1.cast(), len3, len3); + let string3 = _rt::string_lift(bytes3); string3 } } @@ -96,16 +88,8 @@ pub mod foo { let l3 = *ptr2.add(0).cast::<*mut u8>(); let l4 = *ptr2.add(core::mem::size_of::<*const u8>()).cast::(); let len5 = l4; - let string5 = String::from( - std::str::from_utf8(std::slice::from_raw_parts(l3, len5)).unwrap(), - ); - - #[link(wasm_import_module = "cabi_post_foo:foo/strings")] - extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "c")] - fn cabi_post_fooX3AfooX2FstringsX00c(_: *mut u8); - } - cabi_post_fooX3AfooX2FstringsX00c(ptr2); + let bytes5 = _rt::Vec::from_raw_parts(l3.cast(), len5, len5); + let string5 = _rt::string_lift(bytes5); string5 } } @@ -152,13 +136,6 @@ pub mod exports { } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn __post_return_b(arg0: *mut u8) { - let l0 = *arg0.add(0).cast::<*mut u8>(); - let l1 = *arg0.add(core::mem::size_of::<*const u8>()).cast::(); - _rt::cabi_dealloc(l0, l1, 1); - } - #[doc(hidden)] - #[allow(non_snake_case)] pub unsafe fn _export_c_cabi( arg0: *mut u8, arg1: usize, @@ -184,13 +161,6 @@ pub mod exports { *arg4.add(core::mem::size_of::<*const u8>()).cast::() = len3; *arg4.add(0).cast::<*mut u8>() = ptr3.cast_mut(); } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn __post_return_c(arg0: *mut u8) { - let l0 = *arg0.add(0).cast::<*mut u8>(); - let l1 = *arg0.add(core::mem::size_of::<*const u8>()).cast::(); - _rt::cabi_dealloc(l0, l1, 1); - } pub trait Guest { fn a(x: _rt::String) -> (); fn b() -> _rt::String; @@ -211,21 +181,11 @@ pub mod exports { unsafe extern "C" fn a_fooX3AfooX2FstringsX00b(ptr: *mut u8) { $($path_to_types)*::_export_b_cabi::<$ty>(ptr) } - #[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 a_cabi_post_fooX3AfooX2FstringsX00b(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 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) } - #[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 a_cabi_post_fooX3AfooX2FstringsX00c(arg0: *mut u8,) { - $($path_to_types)*::__post_return_c::<$ty>(arg0) - } };); } #[doc(hidden)] @@ -237,6 +197,13 @@ pub mod exports { mod _rt { pub use alloc_crate::string::String; pub use alloc_crate::vec::Vec; + pub unsafe fn string_lift(bytes: Vec) -> 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() { From 888d8a79fd22333008df3068f20b2585ec240626 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 1 Aug 2024 23:53:01 +0200 Subject: [PATCH 305/672] symmetric C++ code matches new convention --- crates/core/src/abi.rs | 30 +++++++++---------- crates/cpp/src/lib.rs | 15 ++++++---- .../component_a/the_world.cpp | 6 ++-- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 86a1a36b9..588977c1b 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -983,21 +983,21 @@ impl<'a, B: Bindgen> Generator<'a, B> { retptr.clone().unwrap(), Default::default(), ); - if guest_export_needs_post_return(self.resolve, func) { - let post_sig = WasmSignature { - params: vec![WasmType::Pointer], - results: Vec::new(), - indirect_params: false, - retptr: false, - }; - // TODO: can we get this name from somewhere? - self.stack.push(retptr.unwrap()); - self.emit(&Instruction::CallWasm { - name: &func.name, - sig: &post_sig, - module_prefix: "cabi_post_", - }); - } + // if guest_export_needs_post_return(self.resolve, func) { + // let post_sig = WasmSignature { + // params: vec![WasmType::Pointer], + // results: Vec::new(), + // indirect_params: false, + // retptr: false, + // }; + // // TODO: can we get this name from somewhere? + // self.stack.push(retptr.unwrap()); + // self.emit(&Instruction::CallWasm { + // name: &func.name, + // sig: &post_sig, + // module_prefix: "cabi_post_", + // }); + // } } 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 diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index a613b4e6c..5a6946aa0 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1694,6 +1694,7 @@ impl CppInterfaceGenerator<'_> { 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) { @@ -2789,13 +2790,15 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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 { + let result = if self.gen.gen.opts.symmetric + && matches!(self.variant, AbiVariant::GuestExport) + { uwriteln!(self.src, "auto string{tmp} = wit::string::from_view(std::string_view((char const *)({}), {len}));\n", operands[0]); - if matches!(self.variant, AbiVariant::GuestExport) { - format!("std::move(string{tmp})") - } else { - format!("string{tmp}") - } + // if matches!(self.variant, AbiVariant::GuestExport) { + format!("std::move(string{tmp})") + // } else { + // format!("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) diff --git a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp index dba6afa23..c4cb7be87 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp @@ -47,8 +47,7 @@ wit::string comp_a::foo::foo::strings::B() { fooX3AfooX2FstringsX00b(ptr0); auto len1 = *((size_t *)(ptr0 + sizeof(void *))); - auto string1 = wit::string((char const *)(*((uint8_t **)(ptr0 + 0))), len1); - return string1; + 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) { @@ -64,8 +63,7 @@ wit::string comp_a::foo::foo::strings::C(std::string_view a, fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2); auto len3 = *((size_t *)(ptr2 + sizeof(void *))); - auto string3 = wit::string((char const *)(*((uint8_t **)(ptr2 + 0))), len3); - return string3; + 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) { From 3b47e3a7c4a60db860fcf5c95310d87aff488b0d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 2 Aug 2024 00:06:42 +0200 Subject: [PATCH 306/672] proper Rust code generation for symmetric --- .../rust_comp_a/src/the_world.rs | 131 +++++++++--------- crates/rust/src/bindgen.rs | 2 +- crates/rust/src/interface.rs | 4 +- 3 files changed, 66 insertions(+), 71 deletions(-) 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 index e365b727d..a2ff06f4f 100644 --- 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 @@ -1,5 +1,3 @@ -// Generated by `wit-bindgen` 0.28.0. DO NOT EDIT! -// Options used: #[allow(dead_code)] pub mod foo { #[allow(dead_code)] @@ -9,8 +7,7 @@ pub mod foo { #[used] #[doc(hidden)] #[cfg(target_arch = "wasm32")] - static __FORCE_SECTION_REF: fn() = - super::super::super::__link_custom_section_describing_imports; + 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) -> () { @@ -18,7 +15,6 @@ pub mod foo { let vec0 = x; let ptr0 = vec0.as_ptr().cast::(); let len0 = vec0.len(); - #[link(wasm_import_module = "foo:foo/strings")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "a")] @@ -33,11 +29,13 @@ pub mod foo { #[cfg_attr(target_pointer_width = "64", repr(align(8)))] #[cfg_attr(target_pointer_width = "32", repr(align(4)))] struct RetArea( - [::core::mem::MaybeUninit; (2 * core::mem::size_of::<*const u8>())], + [::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>())], + [::core::mem::MaybeUninit::uninit(); (2 + * core::mem::size_of::<*const u8>())], ); let ptr0 = ret_area.0.as_mut_ptr().cast::(); #[link(wasm_import_module = "foo:foo/strings")] @@ -47,11 +45,12 @@ pub mod foo { } fooX3AfooX2FstringsX00b(ptr0); let l1 = *ptr0.add(0).cast::<*mut u8>(); - let l2 = *ptr0.add(core::mem::size_of::<*const u8>()).cast::(); + let l2 = *ptr0 + .add(core::mem::size_of::<*const u8>()) + .cast::(); let len3 = l2; let bytes3 = _rt::Vec::from_raw_parts(l1.cast(), len3, len3); - let string3 = _rt::string_lift(bytes3); - string3 + _rt::string_lift(bytes3) } } #[allow(unused_unsafe, clippy::all)] @@ -60,11 +59,13 @@ pub mod foo { #[cfg_attr(target_pointer_width = "64", repr(align(8)))] #[cfg_attr(target_pointer_width = "32", repr(align(4)))] struct RetArea( - [::core::mem::MaybeUninit; (2 * core::mem::size_of::<*const u8>())], + [::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>())], + [::core::mem::MaybeUninit::uninit(); (2 + * core::mem::size_of::<*const u8>())], ); let vec0 = a; let ptr0 = vec0.as_ptr().cast::(); @@ -84,13 +85,20 @@ pub mod foo { _: *mut u8, ); } - fooX3AfooX2FstringsX00c(ptr0.cast_mut(), len0, ptr1.cast_mut(), len1, ptr2); + 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::(); + let l4 = *ptr2 + .add(core::mem::size_of::<*const u8>()) + .cast::(); let len5 = l4; let bytes5 = _rt::Vec::from_raw_parts(l3.cast(), len5, len5); - let string5 = _rt::string_lift(bytes5); - string5 + _rt::string_lift(bytes5) } } } @@ -107,25 +115,23 @@ pub mod exports { #[used] #[doc(hidden)] #[cfg(target_arch = "wasm32")] - static __FORCE_SECTION_REF: fn() = - super::super::super::super::__link_custom_section_describing_imports; + 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(arg0: *mut u8, arg1: usize) { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); + #[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(), + 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(arg0: *mut u8) { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); + #[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::(); @@ -143,15 +149,16 @@ pub mod exports { arg3: usize, arg4: *mut u8, ) { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); + #[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(), + 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(), + 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(); @@ -167,27 +174,24 @@ pub mod exports { 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 a_fooX3AfooX2FstringsX00a(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 a_fooX3AfooX2FstringsX00b(ptr: *mut u8) { - $($path_to_types)*::_export_b_cabi::<$ty>(ptr) - } - #[cfg_attr(target_arch = "wasm32", export_name = "foo:foo/strings#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) - } - };); - } + 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; } @@ -204,22 +208,12 @@ mod _rt { 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. /// @@ -238,16 +232,18 @@ mod _rt { /// ``` #[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); - ) + ($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)] @@ -258,8 +254,7 @@ A\x04\x01B\x06\x01@\x01\x01xs\x01\0\x04\0\x01a\x01\0\x01@\0\0s\x04\0\x01b\x01\x0 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"; - +processed-by\x02\x0dwit-component\x070.215.0\x10wit-bindgen-rust\x060.28.0"; #[inline(never)] #[doc(hidden)] #[cfg(target_arch = "wasm32")] diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index ec8da8836..b848709df 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -763,7 +763,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { let tmp = self.tmp(); let len = format!("len{}", tmp); uwriteln!(self.src, "let {len} = {};", operands[1]); - if self.gen.gen.opts.symmetric { + if self.gen.gen.opts.symmetric && !self.gen.in_import { uwriteln!( self.src, "let string{tmp} = String::from(std::str::from_utf8(std::slice::from_raw_parts({}, {len})).unwrap());", diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 80a09d75c..23333fbef 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -1002,7 +1002,7 @@ impl {async_support}::StreamPayload for {name} {{ }} " ); - } 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, "\ @@ -1096,7 +1096,7 @@ impl {async_support}::StreamPayload for {name} {{ }} " ); - } 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_" From 2b747f17ae810beb5147a87460351b9966c28337 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 Aug 2024 13:54:40 +0200 Subject: [PATCH 307/672] use the new SizeAlign64 type intended for upstream --- Cargo.lock | 14 +++++++------- crates/c/src/lib.rs | 4 ++-- crates/core/src/abi.rs | 4 ++-- crates/cpp/src/lib.rs | 10 +++++----- crates/rust/src/bindgen.rs | 2 +- crates/rust/src/interface.rs | 2 +- crates/rust/src/lib.rs | 4 ++-- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aa331775f..466f6b987 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1720,7 +1720,7 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.215.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#3d92311bdd95208e835c0bbf1f0611ea7040db3e" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6df489f6d283a159aa6ecae866f22514d2643d34" dependencies = [ "leb128", "wasmparser 0.215.0", @@ -1729,7 +1729,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.215.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#3d92311bdd95208e835c0bbf1f0611ea7040db3e" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6df489f6d283a159aa6ecae866f22514d2643d34" dependencies = [ "anyhow", "indexmap", @@ -1758,7 +1758,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.215.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#3d92311bdd95208e835c0bbf1f0611ea7040db3e" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6df489f6d283a159aa6ecae866f22514d2643d34" dependencies = [ "ahash", "bitflags", @@ -2087,7 +2087,7 @@ dependencies = [ [[package]] name = "wast" version = "215.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#3d92311bdd95208e835c0bbf1f0611ea7040db3e" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6df489f6d283a159aa6ecae866f22514d2643d34" dependencies = [ "bumpalo", "leb128", @@ -2108,7 +2108,7 @@ dependencies = [ [[package]] name = "wat" version = "1.215.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#3d92311bdd95208e835c0bbf1f0611ea7040db3e" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6df489f6d283a159aa6ecae866f22514d2643d34" dependencies = [ "wast 215.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] @@ -2474,7 +2474,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.215.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#3d92311bdd95208e835c0bbf1f0611ea7040db3e" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6df489f6d283a159aa6ecae866f22514d2643d34" dependencies = [ "anyhow", "bitflags", @@ -2511,7 +2511,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.215.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#3d92311bdd95208e835c0bbf1f0611ea7040db3e" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6df489f6d283a159aa6ecae866f22514d2643d34" dependencies = [ "anyhow", "id-arena", diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 759cc05a5..793683095 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -28,7 +28,7 @@ struct C { needs_union_double_int64: bool, prim_names: HashSet, world: String, - sizes: SizeAlign, + sizes: SizeAlign64, renamed_interfaces: HashMap, world_id: Option, @@ -2233,7 +2233,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { impl Bindgen for FunctionBindgen<'_, '_> { type Operand = String; - fn sizes(&self) -> &SizeAlign { + fn sizes(&self) -> &SizeAlign64 { &self.gen.gen.sizes } diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 588977c1b..4b430b0ba 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -1,7 +1,7 @@ pub use wit_parser::abi::{AbiVariant, WasmSignature, WasmType}; use wit_parser::{ align_to_arch, Alignment, ArchitectureSize, ElementInfo, Enum, Flags, FlagsRepr, Function, - Handle, Int, Record, Resolve, Result_, Results, SizeAlign, Tuple, Type, TypeDefKind, TypeId, + Handle, Int, Record, Resolve, Result_, Results, SizeAlign64, Tuple, Type, TypeDefKind, TypeId, Variant, }; @@ -709,7 +709,7 @@ pub trait Bindgen { fn finish_block(&mut self, operand: &mut Vec); /// Returns size information that was previously calculated for all types. - fn sizes(&self) -> &SizeAlign; + fn sizes(&self) -> &SizeAlign64; /// Returns whether or not the specified element type is represented in a /// "canonical" form for lists. This dictates whether the `ListCanonLower` diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 5a6946aa0..8b0039710 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -12,7 +12,7 @@ use wit_bindgen_core::{ make_external_component, make_external_symbol, uwrite, uwriteln, wit_parser::{ Alignment, ArchitectureSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, - Resolve, Results, SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, + Resolve, Results, SizeAlign64, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, }, Files, InterfaceGenerator, Source, WorldGenerator, @@ -283,9 +283,9 @@ impl Cpp { wasm_import_module: Option, ) -> CppInterfaceGenerator<'a> { let mut sizes = if self.opts.symmetric { - SizeAlign::new_symmetric() + SizeAlign64::new_symmetric() } else { - SizeAlign::new() + SizeAlign64::default() }; sizes.fill(resolve); @@ -862,7 +862,7 @@ struct CppInterfaceGenerator<'a> { resolve: &'a Resolve, interface: Option, _name: Option<&'a WorldKey>, - sizes: SizeAlign, + sizes: SizeAlign64, in_guest_import: bool, // return_pointer_area_size: usize, // return_pointer_area_align: usize, @@ -3708,7 +3708,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // uwriteln!(self.src, "// finish_block()"); } - fn sizes(&self) -> &wit_bindgen_core::wit_parser::SizeAlign { + fn sizes(&self) -> &wit_bindgen_core::wit_parser::SizeAlign64 { &self.gen.sizes } diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index b848709df..faa27003c 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -292,7 +292,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { format!("ptr{}", tmp) } - fn sizes(&self) -> &SizeAlign { + fn sizes(&self) -> &SizeAlign64 { &self.gen.sizes } diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 23333fbef..2b26514e1 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -18,7 +18,7 @@ pub struct InterfaceGenerator<'a> { pub src: Source, pub(super) identifier: Identifier<'a>, pub in_import: bool, - pub sizes: SizeAlign, + pub sizes: SizeAlign64, pub(super) gen: &'a mut RustWasm, pub wasm_import_module: &'a str, pub resolve: &'a Resolve, diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 5e9367174..c94754c9d 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -294,9 +294,9 @@ impl RustWasm { in_import: bool, ) -> InterfaceGenerator<'a> { let mut sizes = if self.opts.symmetric { - SizeAlign::new_symmetric() + SizeAlign64::new_symmetric() } else { - SizeAlign::new() + SizeAlign64::default() }; sizes.fill(resolve); From 0f2c6474146b7cd0b1e2a97d57b4751e90074602 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 Aug 2024 15:00:50 +0200 Subject: [PATCH 308/672] fix storing of u8 and u16 types --- crates/cpp/src/lib.rs | 4 ++-- crates/cpp/tests/smoke_details/result8.wit | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 crates/cpp/tests/smoke_details/result8.wit diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 8b0039710..c1679694a 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2644,8 +2644,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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("int32_t", *offset, operands), - abi::Instruction::I32Store16 { 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), 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; +} From 317ab27a6bb86d40285182c40b46206e570bc93e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 25 Aug 2024 21:28:45 +0200 Subject: [PATCH 309/672] properly guard wit-guest against multiple inclusion --- crates/cpp/helper-types/wit-guest.h | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index b6d696ce5..18af90ee4 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -1,3 +1,4 @@ +#pragma once #include "wit-common.h" #include #include // unique_ptr From fec5e493596c7cb6c5a5da78e11eff3adfd5073d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 25 Aug 2024 22:21:53 +0200 Subject: [PATCH 310/672] adapt to newer wamr version --- crates/cpp/helper-types/wit-host.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 8049ba582..3ccbef813 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -21,12 +21,15 @@ 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 @@ -76,7 +79,7 @@ class string { 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_runtime_get_module_inst(exec_env), "cabi_realloc"/*, "(*$ii)*"*/); wasm_val_t wasm_results[1] = {WASM_INIT_VAL}; wasm_val_t wasm_args[4] = { @@ -190,7 +193,7 @@ class ResourceExportBase : public ResourceTable { int32_t index; public: - ResourceExportBase() : rep(nullptr), index(-1) {} + 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; @@ -204,7 +207,7 @@ class ResourceExportBase : public ResourceTable { b.rep = 0; } ~ResourceExportBase() { - if (index >= 0 && rep != nullptr) { + if (index >= 0 && rep != INVALID_GUEST_ADDRESS) { remove_resource(index); } } From 4b5121b8e98b5abfa5e5a6ac12ef42cff487a722 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 26 Aug 2024 23:10:40 +0200 Subject: [PATCH 311/672] fix post-merge conflicts --- Cargo.lock | 116 +++++++++++++++++------------------ crates/c/src/lib.rs | 4 +- crates/core/src/abi.rs | 9 +-- crates/cpp/src/lib.rs | 10 +-- crates/rust/src/bindgen.rs | 2 +- crates/rust/src/interface.rs | 10 ++- crates/rust/src/lib.rs | 4 +- 7 files changed, 78 insertions(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f7983559b..227a6d813 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -469,7 +469,7 @@ dependencies = [ "itertools", "log", "smallvec", - "wasmparser 0.215.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.215.0", "wasmtime-types", ] @@ -1441,10 +1441,10 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.216.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-core", "wit-component", - "wit-parser 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.216.0", ] [[package]] @@ -1719,26 +1719,26 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.215.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6df489f6d283a159aa6ecae866f22514d2643d34" +version = "0.216.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c23aebea22c8a75833ae08ed31ccc020835b12a41999e58c31464271b94a88" dependencies = [ "leb128", - "wasmparser 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] name = "wasm-encoder" version = "0.216.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04c23aebea22c8a75833ae08ed31ccc020835b12a41999e58c31464271b94a88" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#1e43a18af72c1fa79f2e6d9e3a9a726c0d1b2c27" dependencies = [ "leb128", + "wasmparser 0.216.0", ] [[package]] name = "wasm-metadata" -version = "0.215.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6df489f6d283a159aa6ecae866f22514d2643d34" +version = "0.216.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#1e43a18af72c1fa79f2e6d9e3a9a726c0d1b2c27" dependencies = [ "anyhow", "indexmap", @@ -1746,8 +1746,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", - "wasmparser 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.216.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.216.0", ] [[package]] @@ -1766,8 +1766,8 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.215.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6df489f6d283a159aa6ecae866f22514d2643d34" +version = "0.216.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#1e43a18af72c1fa79f2e6d9e3a9a726c0d1b2c27" dependencies = [ "ahash", "bitflags", @@ -1785,7 +1785,7 @@ checksum = "d8e9a325d85053408209b3d2ce5eaddd0dd6864d1cff7a007147ba073157defc" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.215.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.215.0", ] [[package]] @@ -1826,8 +1826,8 @@ dependencies = [ "smallvec", "sptr", "target-lexicon", - "wasm-encoder 0.215.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.215.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder 0.215.0", + "wasmparser 0.215.0", "wasmtime-asm-macros", "wasmtime-cache", "wasmtime-component-macro", @@ -1840,7 +1840,7 @@ dependencies = [ "wasmtime-slab", "wasmtime-versioned-export-macros", "wasmtime-winch", - "wat 1.216.0", + "wat 1.216.0 (registry+https://github.com/rust-lang/crates.io-index)", "windows-sys 0.52.0", ] @@ -1885,7 +1885,7 @@ dependencies = [ "syn", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser 0.215.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wit-parser 0.215.0", ] [[package]] @@ -1913,7 +1913,7 @@ dependencies = [ "object", "target-lexicon", "thiserror", - "wasmparser 0.215.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.215.0", "wasmtime-environ", "wasmtime-versioned-export-macros", ] @@ -1938,8 +1938,8 @@ dependencies = [ "serde", "serde_derive", "target-lexicon", - "wasm-encoder 0.215.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.215.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder 0.215.0", + "wasmparser 0.215.0", "wasmprinter", "wasmtime-component-util", "wasmtime-types", @@ -2001,7 +2001,7 @@ dependencies = [ "serde", "serde_derive", "smallvec", - "wasmparser 0.215.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.215.0", ] [[package]] @@ -2057,7 +2057,7 @@ dependencies = [ "gimli", "object", "target-lexicon", - "wasmparser 0.215.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.215.0", "wasmtime-cranelift", "wasmtime-environ", "winch-codegen", @@ -2072,7 +2072,7 @@ dependencies = [ "anyhow", "heck 0.4.1", "indexmap", - "wit-parser 0.215.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wit-parser 0.215.0", ] [[package]] @@ -2086,44 +2086,44 @@ dependencies = [ [[package]] name = "wast" -version = "215.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6df489f6d283a159aa6ecae866f22514d2643d34" +version = "216.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7eb1f2eecd913fdde0dc6c3439d0f24530a98ac6db6cb3d14d92a5328554a08" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.216.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wast" version = "216.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7eb1f2eecd913fdde0dc6c3439d0f24530a98ac6db6cb3d14d92a5328554a08" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#1e43a18af72c1fa79f2e6d9e3a9a726c0d1b2c27" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.216.0", + "wasm-encoder 0.216.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] name = "wat" -version = "1.215.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6df489f6d283a159aa6ecae866f22514d2643d34" +version = "1.216.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac0409090fb5154f95fb5ba3235675fd9e579e731524d63b6a2f653e1280c82a" dependencies = [ - "wast 215.0.0", + "wast 216.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wat" version = "1.216.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac0409090fb5154f95fb5ba3235675fd9e579e731524d63b6a2f653e1280c82a" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#1e43a18af72c1fa79f2e6d9e3a9a726c0d1b2c27" dependencies = [ - "wast 216.0.0", + "wast 216.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2211,7 +2211,7 @@ dependencies = [ "regalloc2", "smallvec", "target-lexicon", - "wasmparser 0.215.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.215.0", "wasmtime-cranelift", "wasmtime-environ", ] @@ -2353,11 +2353,11 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.216.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wit-bindgen-core", "wit-component", - "wit-parser 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.216.0", ] [[package]] @@ -2368,8 +2368,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", - "wasmparser 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.216.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.216.0", "wasmtime", "wasmtime-wasi", "wit-bindgen-bridge", @@ -2383,7 +2383,7 @@ dependencies = [ "wit-bindgen-rust", "wit-bindgen-teavm-java", "wit-component", - "wit-parser 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.216.0", ] [[package]] @@ -2392,7 +2392,7 @@ version = "0.30.0" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.216.0", ] [[package]] @@ -2403,7 +2403,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.216.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", @@ -2419,12 +2419,12 @@ dependencies = [ "heck 0.5.0", "indexmap", "test-helpers", - "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.216.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", - "wasmparser 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.216.0", "wit-bindgen-core", "wit-component", - "wit-parser 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.216.0", ] [[package]] @@ -2518,8 +2518,8 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.215.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6df489f6d283a159aa6ecae866f22514d2643d34" +version = "0.216.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#1e43a18af72c1fa79f2e6d9e3a9a726c0d1b2c27" dependencies = [ "anyhow", "bitflags", @@ -2528,11 +2528,11 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.216.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", - "wasmparser 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", - "wat 1.215.0", - "wit-parser 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.216.0", + "wat 1.216.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.216.0", ] [[package]] @@ -2550,13 +2550,13 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.215.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.215.0", ] [[package]] name = "wit-parser" -version = "0.215.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6df489f6d283a159aa6ecae866f22514d2643d34" +version = "0.216.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#1e43a18af72c1fa79f2e6d9e3a9a726c0d1b2c27" dependencies = [ "anyhow", "id-arena", @@ -2567,7 +2567,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.215.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.216.0", ] [[package]] diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 4792d1f16..ac16dff6e 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -28,7 +28,7 @@ struct C { needs_union_double_int64: bool, prim_names: HashSet, world: String, - sizes: SizeAlign64, + sizes: SizeAlign, renamed_interfaces: HashMap, world_id: Option, @@ -2234,7 +2234,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { impl Bindgen for FunctionBindgen<'_, '_> { type Operand = String; - fn sizes(&self) -> &SizeAlign64 { + fn sizes(&self) -> &SizeAlign { &self.gen.gen.sizes } diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 6bdf1adeb..411a6bece 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -1,7 +1,7 @@ pub use wit_parser::abi::{AbiVariant, WasmSignature, WasmType}; use wit_parser::{ align_to_arch, Alignment, ArchitectureSize, ElementInfo, Enum, Flags, FlagsRepr, Function, - Handle, Int, Record, Resolve, Result_, Results, SizeAlign64, Tuple, Type, TypeDefKind, TypeId, + Handle, Int, Record, Resolve, Result_, Results, SizeAlign, Tuple, Type, TypeDefKind, TypeId, Variant, }; @@ -709,7 +709,7 @@ pub trait Bindgen { fn finish_block(&mut self, operand: &mut Vec); /// Returns size information that was previously calculated for all types. - fn sizes(&self) -> &SizeAlign64; + fn sizes(&self) -> &SizeAlign; /// Returns whether or not the specified element type is represented in a /// "canonical" form for lists. This dictates whether the `ListCanonLower` @@ -1145,10 +1145,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { .sizes() .record(func.params.iter().map(|t| &t.1)); self.emit(&Instruction::GetArg { nth: 0 }); - self.emit(&Instruction::GuestDeallocate { - size: info.size.size_wasm32(), - align: info.align.align_wasm32(), - }); + self.emit(&Instruction::GuestDeallocate { size, align }); } } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index c1679694a..cf51ecfbb 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -12,7 +12,7 @@ use wit_bindgen_core::{ make_external_component, make_external_symbol, uwrite, uwriteln, wit_parser::{ Alignment, ArchitectureSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, - Resolve, Results, SizeAlign64, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, + Resolve, Results, SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, }, Files, InterfaceGenerator, Source, WorldGenerator, @@ -283,9 +283,9 @@ impl Cpp { wasm_import_module: Option, ) -> CppInterfaceGenerator<'a> { let mut sizes = if self.opts.symmetric { - SizeAlign64::new_symmetric() + SizeAlign::new_symmetric() } else { - SizeAlign64::default() + SizeAlign::default() }; sizes.fill(resolve); @@ -862,7 +862,7 @@ struct CppInterfaceGenerator<'a> { resolve: &'a Resolve, interface: Option, _name: Option<&'a WorldKey>, - sizes: SizeAlign64, + sizes: SizeAlign, in_guest_import: bool, // return_pointer_area_size: usize, // return_pointer_area_align: usize, @@ -3708,7 +3708,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // uwriteln!(self.src, "// finish_block()"); } - fn sizes(&self) -> &wit_bindgen_core::wit_parser::SizeAlign64 { + fn sizes(&self) -> &wit_bindgen_core::wit_parser::SizeAlign { &self.gen.sizes } diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index e81ffda1f..56c7be9ca 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -292,7 +292,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { format!("ptr{}", tmp) } - fn sizes(&self) -> &SizeAlign64 { + fn sizes(&self) -> &SizeAlign { &self.gen.sizes } diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 6e9ed6da2..762ae2d18 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -18,7 +18,7 @@ pub struct InterfaceGenerator<'a> { pub src: Source, pub(super) identifier: Identifier<'a>, pub in_import: bool, - pub sizes: SizeAlign64, + pub sizes: SizeAlign, pub(super) gen: &'a mut RustWasm, pub wasm_import_module: &'a str, pub resolve: &'a Resolve, @@ -1011,7 +1011,9 @@ impl {async_support}::StreamPayload for {name} {{ }} " ); - } else if abi::guest_export_needs_post_return(self.resolve, func) && !self.gen.opts.symmetric { + } else if abi::guest_export_needs_post_return(self.resolve, func) + && !self.gen.opts.symmetric + { uwrite!( self.src, "\ @@ -1105,7 +1107,9 @@ impl {async_support}::StreamPayload for {name} {{ }} " ); - } else if abi::guest_export_needs_post_return(self.resolve, func) && !self.gen.opts.symmetric { + } 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_" diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index c11e7e760..d9207aa51 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -302,9 +302,9 @@ impl RustWasm { in_import: bool, ) -> InterfaceGenerator<'a> { let mut sizes = if self.opts.symmetric { - SizeAlign64::new_symmetric() + SizeAlign::new_symmetric() } else { - SizeAlign64::default() + SizeAlign::default() }; sizes.fill(resolve); From 3eea267682bb5a61e6f2599543ad8d75cef42524 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 28 Aug 2024 00:35:23 +0200 Subject: [PATCH 312/672] size and alignment fixes --- Cargo.lock | 14 +++--- crates/csharp/src/lib.rs | 44 +++++++++--------- crates/moonbit/src/lib.rs | 88 +++++++++++++++++++++++++----------- crates/rust/src/bindgen.rs | 12 ++--- crates/teavm-java/src/lib.rs | 64 ++++++++++++++++---------- 5 files changed, 139 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 227a6d813..bfab67b88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1729,7 +1729,7 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#1e43a18af72c1fa79f2e6d9e3a9a726c0d1b2c27" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0280d4131316fff27a42d928360190c1a855e31b" dependencies = [ "leb128", "wasmparser 0.216.0", @@ -1738,7 +1738,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#1e43a18af72c1fa79f2e6d9e3a9a726c0d1b2c27" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0280d4131316fff27a42d928360190c1a855e31b" dependencies = [ "anyhow", "indexmap", @@ -1767,7 +1767,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#1e43a18af72c1fa79f2e6d9e3a9a726c0d1b2c27" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0280d4131316fff27a42d928360190c1a855e31b" dependencies = [ "ahash", "bitflags", @@ -2100,7 +2100,7 @@ dependencies = [ [[package]] name = "wast" version = "216.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#1e43a18af72c1fa79f2e6d9e3a9a726c0d1b2c27" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0280d4131316fff27a42d928360190c1a855e31b" dependencies = [ "bumpalo", "leb128", @@ -2121,7 +2121,7 @@ dependencies = [ [[package]] name = "wat" version = "1.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#1e43a18af72c1fa79f2e6d9e3a9a726c0d1b2c27" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0280d4131316fff27a42d928360190c1a855e31b" dependencies = [ "wast 216.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] @@ -2519,7 +2519,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#1e43a18af72c1fa79f2e6d9e3a9a726c0d1b2c27" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0280d4131316fff27a42d928360190c1a855e31b" dependencies = [ "anyhow", "bitflags", @@ -2556,7 +2556,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#1e43a18af72c1fa79f2e6d9e3a9a726c0d1b2c27" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0280d4131316fff27a42d928360190c1a855e31b" dependencies = [ "anyhow", "id-arena", diff --git a/crates/csharp/src/lib.rs b/crates/csharp/src/lib.rs index 1f6d08007..88c546de0 100644 --- a/crates/csharp/src/lib.rs +++ b/crates/csharp/src/lib.rs @@ -134,8 +134,8 @@ pub enum FunctionLevel { pub struct CSharp { opts: Opts, name: String, - return_area_size: usize, - return_area_align: usize, + return_area_size: ArchitectureSize, + return_area_align: Alignment, tuple_counts: HashSet, needs_result: bool, needs_option: bool, @@ -567,8 +567,10 @@ impl WorldGenerator for CSharp { if self.needs_export_return_area { let mut ret_area_str = String::new(); - let (array_size, element_type) = - dotnet_aligned_array(self.return_area_size, self.return_area_align); + let (array_size, element_type) = dotnet_aligned_array( + self.return_area_size.size_wasm32(), + self.return_area_align.align_wasm32(), + ); uwrite!( ret_area_str, " @@ -592,7 +594,7 @@ impl WorldGenerator for CSharp { }} ", array_size, - self.return_area_align, + self.return_area_align.align_wasm32(), element_type ); @@ -1993,8 +1995,8 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { payloads: Vec::new(), needs_cleanup_list: false, cleanup: Vec::new(), - import_return_pointer_area_size: 0, - import_return_pointer_area_align: 0, + import_return_pointer_area_size: Default::default(), + import_return_pointer_area_align: Default::default(), fixed: 0, resource_drops: Vec::new(), } @@ -2178,22 +2180,22 @@ impl Bindgen for FunctionBindgen<'_, '_> { })), Instruction::I32Load { offset } | Instruction::PointerLoad { offset } - | Instruction::LengthLoad { offset } => results.push(format!("BitConverter.ToInt32(new Span((void*)({} + {offset}), 4))",operands[0])), - Instruction::I32Load8U { offset } => results.push(format!("new Span((void*)({} + {offset}), 1)[0]",operands[0])), - Instruction::I32Load8S { offset } => results.push(format!("(sbyte)new Span((void*)({} + {offset}), 1)[0]",operands[0])), - Instruction::I32Load16U { offset } => results.push(format!("BitConverter.ToUInt16(new Span((void*)({} + {offset}), 2))",operands[0])), - Instruction::I32Load16S { offset } => results.push(format!("BitConverter.ToInt16(new Span((void*)({} + {offset}), 2))",operands[0])), - Instruction::I64Load { offset } => results.push(format!("BitConverter.ToInt64(new Span((void*)({} + {offset}), 8))",operands[0])), - Instruction::F32Load { offset } => results.push(format!("BitConverter.ToSingle(new Span((void*)({} + {offset}), 4))",operands[0])), - Instruction::F64Load { offset } => results.push(format!("BitConverter.ToDouble(new Span((void*)({} + {offset}), 8))",operands[0])), + | Instruction::LengthLoad { offset } => results.push(format!("BitConverter.ToInt32(new Span((void*)({} + {offset}), 4))",operands[0],offset=offset.size_wasm32())), + Instruction::I32Load8U { offset } => results.push(format!("new Span((void*)({} + {offset}), 1)[0]",operands[0],offset=offset.size_wasm32())), + Instruction::I32Load8S { offset } => results.push(format!("(sbyte)new Span((void*)({} + {offset}), 1)[0]",operands[0],offset=offset.size_wasm32())), + Instruction::I32Load16U { offset } => results.push(format!("BitConverter.ToUInt16(new Span((void*)({} + {offset}), 2))",operands[0],offset=offset.size_wasm32())), + Instruction::I32Load16S { offset } => results.push(format!("BitConverter.ToInt16(new Span((void*)({} + {offset}), 2))",operands[0],offset=offset.size_wasm32())), + Instruction::I64Load { offset } => results.push(format!("BitConverter.ToInt64(new Span((void*)({} + {offset}), 8))",operands[0],offset=offset.size_wasm32())), + Instruction::F32Load { offset } => results.push(format!("BitConverter.ToSingle(new Span((void*)({} + {offset}), 4))",operands[0],offset=offset.size_wasm32())), + Instruction::F64Load { offset } => results.push(format!("BitConverter.ToDouble(new Span((void*)({} + {offset}), 8))",operands[0],offset=offset.size_wasm32())), Instruction::I32Store { offset } | Instruction::PointerStore { offset } - | Instruction::LengthStore { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 4), unchecked((int){}));", operands[1], operands[0]), - Instruction::I32Store8 { offset } => uwriteln!(self.src, "*(byte*)({} + {offset}) = (byte){};", operands[1], operands[0]), - Instruction::I32Store16 { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 2), (short){});", operands[1], operands[0]), - Instruction::I64Store { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 8), unchecked((long){}));", operands[1], operands[0]), - Instruction::F32Store { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 4), unchecked((float){}));", operands[1], operands[0]), - Instruction::F64Store { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 8), unchecked((double){}));", operands[1], operands[0]), + | Instruction::LengthStore { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 4), unchecked((int){}));", operands[1], operands[0],offset=offset.size_wasm32()), + Instruction::I32Store8 { offset } => uwriteln!(self.src, "*(byte*)({} + {offset}) = (byte){};", operands[1], operands[0],offset=offset.size_wasm32()), + Instruction::I32Store16 { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 2), (short){});", operands[1], operands[0],offset=offset.size_wasm32()), + Instruction::I64Store { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 8), unchecked((long){}));", operands[1], operands[0],offset=offset.size_wasm32()), + Instruction::F32Store { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 4), unchecked((float){}));", operands[1], operands[0],offset=offset.size_wasm32()), + Instruction::F64Store { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 8), unchecked((double){}));", operands[1], operands[0],offset=offset.size_wasm32()), Instruction::I64FromU64 => results.push(format!("unchecked((long)({}))", operands[0])), Instruction::I32FromChar => results.push(format!("((int){})", operands[0])), diff --git a/crates/moonbit/src/lib.rs b/crates/moonbit/src/lib.rs index 5fd190fff..49ea01ec8 100644 --- a/crates/moonbit/src/lib.rs +++ b/crates/moonbit/src/lib.rs @@ -6,9 +6,9 @@ use wit_bindgen_core::{ abi::{self, AbiVariant, Bindgen, Bitcast, Instruction, LiftLower, WasmType}, dealias, uwrite, uwriteln, wit_parser::{ - Docs, Enum, Flags, FlagsRepr, Function, FunctionKind, Handle, Int, InterfaceId, Record, - Resolve, Result_, SizeAlign, Tuple, Type, TypeDef, TypeDefKind, TypeId, TypeOwner, Variant, - WorldId, WorldKey, + Alignment, ArchitectureSize, Docs, Enum, Flags, FlagsRepr, Function, FunctionKind, Handle, + Int, InterfaceId, Record, Resolve, Result_, SizeAlign, Tuple, Type, TypeDef, TypeDefKind, + TypeId, TypeOwner, Variant, WorldId, WorldKey, }, Direction, Files, InterfaceGenerator as _, Ns, Source, WorldGenerator, }; @@ -240,8 +240,8 @@ pub struct MoonBit { export: HashMap, export_ns: Ns, // return area allocation - return_area_size: usize, - return_area_align: usize, + return_area_size: ArchitectureSize, + return_area_align: Alignment, } impl MoonBit { @@ -571,7 +571,7 @@ impl WorldGenerator for MoonBit { let return_area : Int = {ffi_qualifier}malloc({}) ", - self.return_area_size, + self.return_area_size.size_wasm32(), ); files.push(&format!("{EXPORT_DIR}/ffi.mbt"), indent(&body).as_bytes()); self.export @@ -750,6 +750,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut bindgen, + false, ); let src = bindgen.src; @@ -834,6 +835,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut bindgen, + false, ); assert!(!bindgen.needs_cleanup_list); @@ -893,7 +895,7 @@ impl InterfaceGenerator<'_> { (0..sig.results.len()).map(|i| format!("p{i}")).collect(), ); - abi::post_return(bindgen.gen.resolve, func, &mut bindgen); + abi::post_return(bindgen.gen.resolve, func, &mut bindgen, false); let src = bindgen.src; @@ -2247,42 +2249,50 @@ impl Bindgen for FunctionBindgen<'_, '_> { | Instruction::PointerLoad { offset } | Instruction::LengthLoad { offset } => results.push(format!( "{ffi_qualifier}load32(({}) + {offset})", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::I32Load8U { offset } => results.push(format!( "{ffi_qualifier}load8_u(({}) + {offset})", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::I32Load8S { offset } => results.push(format!( "{ffi_qualifier}load8(({}) + {offset})", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::I32Load16U { offset } => results.push(format!( "{ffi_qualifier}load16_u(({}) + {offset})", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::I32Load16S { offset } => results.push(format!( "{ffi_qualifier}load16(({}) + {offset})", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::I64Load { offset } => results.push(format!( "{ffi_qualifier}load64(({}) + {offset})", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::F32Load { offset } => results.push(format!( "{ffi_qualifier}loadf32(({}) + {offset})", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::F64Load { offset } => results.push(format!( "{ffi_qualifier}loadf64(({}) + {offset})", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::I32Store { offset } @@ -2291,46 +2301,52 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src, "{ffi_qualifier}store32(({}) + {offset}, {})", operands[1], - operands[0] + operands[0], + offset = offset.size_wasm32() ), Instruction::I32Store8 { offset } => uwriteln!( self.src, "{ffi_qualifier}store8(({}) + {offset}, {})", operands[1], - operands[0] + operands[0], + offset = offset.size_wasm32() ), Instruction::I32Store16 { offset } => uwriteln!( self.src, "{ffi_qualifier}store16(({}) + {offset}, {})", operands[1], - operands[0] + operands[0], + offset = offset.size_wasm32() ), Instruction::I64Store { offset } => uwriteln!( self.src, "{ffi_qualifier}store64(({}) + {offset}, {})", operands[1], - operands[0] + operands[0], + offset = offset.size_wasm32() ), Instruction::F32Store { offset } => uwriteln!( self.src, "{ffi_qualifier}storef32(({}) + {offset}, {})", operands[1], - operands[0] + operands[0], + offset = offset.size_wasm32() ), Instruction::F64Store { offset } => uwriteln!( self.src, "{ffi_qualifier}storef64(({}) + {offset}, {})", operands[1], - operands[0] + operands[0], + offset = offset.size_wasm32() ), // TODO: see what we can do with align Instruction::Malloc { size, .. } => { - uwriteln!(self.src, "{ffi_qualifier}malloc({})", size) + uwriteln!(self.src, "{ffi_qualifier}malloc({})", size.size_wasm32()) } Instruction::GuestDeallocate { .. } => { @@ -2404,18 +2420,38 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!(self.src, "{ffi_qualifier}free({address})"); } + Instruction::FutureLower { payload, ty } => todo!(), + Instruction::FutureLift { payload, ty } => todo!(), + Instruction::StreamLower { payload, ty } => todo!(), + Instruction::StreamLift { payload, ty } => todo!(), + Instruction::ErrorLower { ty } => todo!(), + Instruction::ErrorLift { ty } => todo!(), + Instruction::AsyncMalloc { size, align } => todo!(), + Instruction::AsyncCallWasm { name, size, align } => todo!(), + Instruction::AsyncCallStart { + name, + params, + results, + } => todo!(), + Instruction::AsyncPostCallInterface { func } => todo!(), + Instruction::AsyncCallReturn { name, params } => todo!(), + Instruction::Flush { amt } => todo!(), } } - fn return_pointer(&mut self, size: usize, align: usize) -> String { + fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> String { if self.gen.direction == Direction::Import { let ffi_qualifier = self.gen.qualify_package(&FFI_DIR.to_string()); let address = self.locals.tmp("return_area"); - uwriteln!(self.src, "let {address} = {ffi_qualifier}malloc({})", size,); + uwriteln!( + self.src, + "let {address} = {ffi_qualifier}malloc({})", + size.size_wasm32(), + ); self.cleanup.push(Cleanup::Memory { address: address.clone(), - size: size.to_string(), - align, + size: size.size_wasm32().to_string(), + align: align.align_wasm32(), }); address } else { diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 56c7be9ca..d86497504 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -797,8 +797,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { operand0 = operands[0] )); self.push_str(&format!("let {len} = {vec}.len();\n")); - let size = self.gen.sizes.size(element).size_wasm32(); - let align = self.gen.sizes.align(element).align_wasm32(); + let size = self.gen.sizes.size(element); + let align = self.gen.sizes.align(element); self.push_str(&format!( "let {layout} = {alloc}::Layout::from_size_align_unchecked({vec}.len() * {}, {});\n", size.format(POINTER_SIZE_EXPRESSION), align.format(POINTER_SIZE_EXPRESSION), @@ -832,8 +832,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::ListLift { element, .. } => { let body = self.blocks.pop().unwrap(); let tmp = self.tmp(); - let size = self.gen.sizes.size(element).size_wasm32(); - let align = self.gen.sizes.align(element).align_wasm32(); + let size = self.gen.sizes.size(element); + let align = self.gen.sizes.align(element); let len = format!("len{tmp}"); let base = format!("base{tmp}"); let result = format!("result{tmp}"); @@ -1258,8 +1258,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::GuestDeallocateList { element } => { let body = self.blocks.pop().unwrap(); let tmp = self.tmp(); - let size = self.gen.sizes.size(element).size_wasm32(); - let align = self.gen.sizes.align(element).align_wasm32(); + let size = self.gen.sizes.size(element); + let align = self.gen.sizes.align(element); let len = format!("len{tmp}"); let base = format!("base{tmp}"); self.push_str(&format!( diff --git a/crates/teavm-java/src/lib.rs b/crates/teavm-java/src/lib.rs index 788361753..20f0e5f93 100644 --- a/crates/teavm-java/src/lib.rs +++ b/crates/teavm-java/src/lib.rs @@ -53,8 +53,8 @@ struct InterfaceFragment { pub struct TeaVmJava { opts: Opts, name: String, - return_area_size: usize, - return_area_align: usize, + return_area_size: ArchitectureSize, + return_area_align: Alignment, tuple_counts: HashSet, needs_cleanup: bool, needs_result: bool, @@ -345,9 +345,9 @@ impl WorldGenerator for TeaVmJava { ); } - if self.return_area_align > 0 { - let size = self.return_area_size; - let align = self.return_area_align; + if !self.return_area_size.is_empty() { + let size = self.return_area_size.size_wasm32(); + let align = self.return_area_align.align_wasm32(); uwriteln!( src, @@ -1669,8 +1669,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { assert!(block_results.is_empty()); let op = &operands[0]; - let size = self.gen.gen.sizes.size(element).size_wasm32(); - let align = self.gen.gen.sizes.align(element).align_wasm32(); + let size = self.gen.gen.sizes.size(element); + let align = self.gen.gen.sizes.align(element); let address = self.locals.tmp("address"); let ty = self.gen.type_name(element); let index = self.locals.tmp("index"); @@ -1684,7 +1684,9 @@ impl Bindgen for FunctionBindgen<'_, '_> { int {base} = {address} + ({index} * {size}); {body} }} - " + ", + align = align.align_wasm32(), + size = size.size_wasm32() ); if realloc.is_none() { @@ -1867,42 +1869,50 @@ impl Bindgen for FunctionBindgen<'_, '_> { | Instruction::PointerLoad { offset } | Instruction::LengthLoad { offset } => results.push(format!( "org.teavm.interop.Address.fromInt(({}) + {offset}).getInt()", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::I32Load8U { offset } => results.push(format!( "(((int) org.teavm.interop.Address.fromInt(({}) + {offset}).getByte()) & 0xFF)", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::I32Load8S { offset } => results.push(format!( "((int) org.teavm.interop.Address.fromInt(({}) + {offset}).getByte())", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::I32Load16U { offset } => results.push(format!( "(((int) org.teavm.interop.Address.fromInt(({}) + {offset}).getShort()) & 0xFFFF)", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::I32Load16S { offset } => results.push(format!( "((int) org.teavm.interop.Address.fromInt(({}) + {offset}).getShort())", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::I64Load { offset } => results.push(format!( "org.teavm.interop.Address.fromInt(({}) + {offset}).getLong()", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::F32Load { offset } => results.push(format!( "org.teavm.interop.Address.fromInt(({}) + {offset}).getFloat()", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::F64Load { offset } => results.push(format!( "org.teavm.interop.Address.fromInt(({}) + {offset}).getDouble()", - operands[0] + operands[0], + offset = offset.size_wasm32() )), Instruction::I32Store { offset } @@ -1911,42 +1921,48 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src, "org.teavm.interop.Address.fromInt(({}) + {offset}).putInt({});", operands[1], - operands[0] + operands[0], + offset = offset.size_wasm32() ), Instruction::I32Store8 { offset } => uwriteln!( self.src, "org.teavm.interop.Address.fromInt(({}) + {offset}).putByte((byte) ({}));", operands[1], - operands[0] + operands[0], + offset = offset.size_wasm32() ), Instruction::I32Store16 { offset } => uwriteln!( self.src, "org.teavm.interop.Address.fromInt(({}) + {offset}).putShort((short) ({}));", operands[1], - operands[0] + operands[0], + offset = offset.size_wasm32() ), Instruction::I64Store { offset } => uwriteln!( self.src, "org.teavm.interop.Address.fromInt(({}) + {offset}).putLong({});", operands[1], - operands[0] + operands[0], + offset = offset.size_wasm32() ), Instruction::F32Store { offset } => uwriteln!( self.src, "org.teavm.interop.Address.fromInt(({}) + {offset}).putFloat({});", operands[1], - operands[0] + operands[0], + offset = offset.size_wasm32() ), Instruction::F64Store { offset } => uwriteln!( self.src, "org.teavm.interop.Address.fromInt(({}) + {offset}).putDouble({});", operands[1], - operands[0] + operands[0], + offset = offset.size_wasm32() ), Instruction::Malloc { .. } => unimplemented!(), @@ -1955,7 +1971,9 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!( self.src, "Memory.free(org.teavm.interop.Address.fromInt({}), {size}, {align});", - operands[0] + operands[0], + size = size.size_wasm32(), + align = align.align_wasm32() ) } From ee038e19868f23b7c5fd7267c2d8c84c36336b5a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 28 Aug 2024 23:18:55 +0200 Subject: [PATCH 313/672] example for symmetric API --- crates/cpp/src/lib.rs | 5 +++++ .../cpp/tests/meshless_strings/component_a/Makefile | 2 +- .../cpp/tests/meshless_strings/component_a/main.cpp | 10 +++++----- .../tests/meshless_strings/component_a/the_world.cpp | 12 ++++++------ .../meshless_strings/component_a/the_world_cpp.h | 4 ++-- 5 files changed, 19 insertions(+), 14 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index cf51ecfbb..4652e7d1c 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -220,6 +220,11 @@ pub struct Opts { /// 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 { diff --git a/crates/cpp/tests/meshless_strings/component_a/Makefile b/crates/cpp/tests/meshless_strings/component_a/Makefile index d5c67dda3..1b0496e00 100644 --- a/crates/cpp/tests/meshless_strings/component_a/Makefile +++ b/crates/cpp/tests/meshless_strings/component_a/Makefile @@ -6,7 +6,7 @@ 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 --wasm64 --format + ../../../../../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 index 19d56bd26..03e6d1448 100644 --- a/crates/cpp/tests/meshless_strings/component_a/main.cpp +++ b/crates/cpp/tests/meshless_strings/component_a/main.cpp @@ -2,16 +2,16 @@ #include "the_world_cpp.h" #include -void comp_a::exports::foo::foo::strings::A(wit::string &&x) { - std::cout << x.get_view() << std::endl; +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(wit::string &&a, - wit::string &&b) { - std::cout << a.get_view() << '|' << b.get_view() << std::endl; +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; } diff --git a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp index c4cb7be87..547714e0f 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp @@ -70,9 +70,9 @@ a_fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { auto len0 = arg1; auto string0 = - wit::string::from_view(std::string_view((char const *)(arg0), len0)); + std::string_view((char const *)(arg0), len0); - comp_a::exports::foo::foo::strings::A(std::move(string0)); + comp_a::exports::foo::foo::strings::A(string0); } extern "C" __attribute__((__export_name__("foo:foo/strings#b"))) void a_fooX3AfooX2FstringsX00b(uint8_t *arg0) { @@ -91,15 +91,15 @@ a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3 auto len0 = arg1; auto string0 = - wit::string::from_view(std::string_view((char const *)(arg0), len0)); + std::string_view((char const *)(arg0), len0); auto len1 = arg3; auto string1 = - wit::string::from_view(std::string_view((char const *)(arg2), len1)); + std::string_view((char const *)(arg2), len1); - auto result2 = comp_a::exports::foo::foo::strings::C(std::move(string0), - std::move(string1)); + auto result2 = comp_a::exports::foo::foo::strings::C(string0, + string1); auto const &vec3 = result2; auto ptr3 = (uint8_t *)(vec3.data()); auto len3 = (size_t)(vec3.size()); 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 index 0955df5f7..019af892c 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world_cpp.h +++ b/crates/cpp/tests/meshless_strings/component_a/the_world_cpp.h @@ -21,9 +21,9 @@ namespace exports { namespace foo { namespace foo { namespace strings { -void A(wit::string &&x); +void A(std::string_view x); wit::string B(); -wit::string C(wit::string &&a, wit::string &&b); +wit::string C(std::string_view a, std::string_view b); } // namespace strings } // namespace foo } // namespace foo From 512b955717d258d1bc0aecc0cae7486ca958aa1c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 28 Aug 2024 23:19:17 +0200 Subject: [PATCH 314/672] fix warnings --- crates/moonbit/src/lib.rs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/crates/moonbit/src/lib.rs b/crates/moonbit/src/lib.rs index 49ea01ec8..a87ec3437 100644 --- a/crates/moonbit/src/lib.rs +++ b/crates/moonbit/src/lib.rs @@ -2420,22 +2420,18 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!(self.src, "{ffi_qualifier}free({address})"); } - Instruction::FutureLower { payload, ty } => todo!(), - Instruction::FutureLift { payload, ty } => todo!(), - Instruction::StreamLower { payload, ty } => todo!(), - Instruction::StreamLift { payload, ty } => todo!(), - Instruction::ErrorLower { ty } => todo!(), - Instruction::ErrorLift { ty } => todo!(), - Instruction::AsyncMalloc { size, align } => todo!(), - Instruction::AsyncCallWasm { name, size, align } => todo!(), - Instruction::AsyncCallStart { - name, - params, - results, - } => todo!(), - Instruction::AsyncPostCallInterface { func } => todo!(), - Instruction::AsyncCallReturn { name, params } => todo!(), - Instruction::Flush { amt } => todo!(), + Instruction::FutureLower { .. } => todo!(), + Instruction::FutureLift { .. } => todo!(), + Instruction::StreamLower { .. } => todo!(), + Instruction::StreamLift { .. } => todo!(), + Instruction::ErrorLower { .. } => todo!(), + Instruction::ErrorLift { .. } => todo!(), + Instruction::AsyncMalloc { .. } => todo!(), + Instruction::AsyncCallWasm { .. } => todo!(), + Instruction::AsyncCallStart { .. } => todo!(), + Instruction::AsyncPostCallInterface { .. } => todo!(), + Instruction::AsyncCallReturn { .. } => todo!(), + Instruction::Flush { .. } => todo!(), } } From fe7816fa4272a85c3e48d33ea15ac880230753e0 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 28 Aug 2024 23:19:41 +0200 Subject: [PATCH 315/672] fix another warning --- tests/runtime/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runtime/main.rs b/tests/runtime/main.rs index 0cee2f960..89fccbe5f 100644 --- a/tests/runtime/main.rs +++ b/tests/runtime/main.rs @@ -278,7 +278,7 @@ fn tests(name: &str, dir_name: &str) -> Result> { let snake = world_name.replace("-", "_"); let mut files = Default::default(); - let mut opts = wit_bindgen_cpp::Opts::default(); + let opts = wit_bindgen_cpp::Opts::default(); opts.build().generate(&resolve, world, &mut files).unwrap(); for (file, contents) in files.iter() { From a46c041914d3678a88f7533878eb8d818bdfcf62 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 28 Aug 2024 23:47:45 +0200 Subject: [PATCH 316/672] new symmetric API for strings --- crates/cpp/src/lib.rs | 12 +- crates/cpp/src/lib_old.rs | 2628 ----------------- .../component_a/the_world.cpp | 17 +- 3 files changed, 13 insertions(+), 2644 deletions(-) delete mode 100644 crates/cpp/src/lib_old.rs diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 4652e7d1c..c32af2076 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1862,7 +1862,9 @@ impl CppInterfaceGenerator<'_> { Type::F32 => "float".into(), Type::F64 => "double".into(), Type::String => match flavor { - Flavor::Argument(AbiVariant::GuestImport) => { + Flavor::Argument(var) + if matches!(var, AbiVariant::GuestImport) || self.gen.opts.new_api => + { self.gen.dependencies.needs_string_view = true; "std::string_view".into() } @@ -1946,7 +1948,9 @@ impl CppInterfaceGenerator<'_> { let inner = self.type_name(ty, from_namespace, flavor); match flavor { //self.gen.dependencies.needs_vector = true; - Flavor::Argument(AbiVariant::GuestImport) => { + Flavor::Argument(var) + if matches!(var, AbiVariant::GuestImport) || self.gen.opts.new_api => + { self.gen.dependencies.needs_wit = true; format!("wit::span<{inner} const>") } @@ -2795,7 +2799,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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 + 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]); @@ -2807,7 +2811,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } 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 { + } else if self.gen.gen.opts.short_cut || (self.gen.gen.opts.new_api && matches!(self.variant, AbiVariant::GuestExport)) { format!("std::string_view((char const*)({}), {len})", operands[0]) } else { format!("wit::string((char const*)({}), {len})", operands[0]) diff --git a/crates/cpp/src/lib_old.rs b/crates/cpp/src/lib_old.rs deleted file mode 100644 index acc05812a..000000000 --- a/crates/cpp/src/lib_old.rs +++ /dev/null @@ -1,2628 +0,0 @@ -use heck::*; -use std::collections::{BTreeMap, HashMap, HashSet}; -use std::fmt::Write as _; -use std::mem; -use wit_bindgen_core::abi::{self, AbiVariant, Bindgen, Instruction, LiftLower, WasmType}; -use wit_bindgen_core::{ - uwriteln, wit_parser::*, Files, InterfaceGenerator as _, Source, TypeInfo, Types, - WorldGenerator, -}; -use wit_bindgen_cpp_host::RESOURCE_BASE_CLASS_NAME; -use wit_bindgen_rust::{ - dealias, FnSig, Ownership, RustFlagsRepr, RustFunctionGenerator, RustGenerator, TypeMode, -}; - -#[derive(Default, Copy, Clone, PartialEq, Eq)] -enum Direction { - #[default] - Import, - Export, -} - -#[derive(Default)] -struct ResourceInfo { - direction: Direction, - owned: bool, - docs: Docs, -} - -#[derive(Default)] -struct Cpp { - types: Types, - src: Source, - opts: Opts, - import_modules: BTreeMap, Vec>, - export_modules: BTreeMap, Vec>, - skip: HashSet, - interface_names: HashMap, - resources: HashMap, - import_funcs_called: bool, - world: Option, -} - -#[cfg(feature = "clap")] -fn parse_map(s: &str) -> Result, String> { - if s.is_empty() { - Ok(HashMap::default()) - } else { - s.split(',') - .map(|entry| { - let (key, value) = entry.split_once('=').ok_or_else(|| { - format!("expected string of form `=[,=...]`; got `{s}`") - })?; - Ok((key.to_owned(), value.to_owned())) - }) - .collect() - } -} - -#[derive(Default, Debug, Clone)] -#[cfg_attr(feature = "clap", derive(clap::Args))] -pub struct Opts { - /// Names of functions to skip generating bindings for. - #[cfg_attr(feature = "clap", arg(long))] - pub skip: Vec, - - /// Name of the concrete type which implements the trait representing any - /// top-level functions exported by the world. - #[cfg_attr(feature = "clap", arg(long))] - pub world_exports: Option, - - /// Names of the concrete types which implement the traits representing any - /// interfaces exported by the world. - #[cfg_attr(feature = "clap", arg(long, value_parser = parse_map, default_value = ""))] - pub interface_exports: HashMap, - - /// Names of the concrete types which implement the traits representing any - /// resources exported by the world. - #[cfg_attr(feature = "clap", arg(long, value_parser = parse_map, default_value = ""))] - pub resource_exports: HashMap, - - /// If true, generate stub implementations for any exported functions, - /// interfaces, and/or resources. - #[cfg_attr(feature = "clap", arg(long))] - pub stubs: 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, - - /// 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, -} - -impl Opts { - pub fn build(self) -> Box { - let mut r = Cpp::new(); - r.skip = self.skip.iter().cloned().collect(); - r.opts = self; - Box::new(r) - } -} - -impl Cpp { - fn new() -> Cpp { - Cpp::default() - } - - fn interface<'a>( - &'a mut self, - identifier: Identifier<'a>, - wasm_import_module: Option<&'a str>, - resolve: &'a Resolve, - in_import: bool, - ) -> InterfaceGenerator<'a> { - let mut sizes = SizeAlign::default(); - sizes.fill(resolve); - - InterfaceGenerator { - identifier, - wasm_import_module, - src: Source::default(), - in_import, - gen: self, - sizes, - resolve, - return_pointer_area_size: 0, - return_pointer_area_align: 0, - } - } - - fn emit_modules(&mut self, modules: &BTreeMap, Vec>) { - let mut map = BTreeMap::new(); - for (pkg, modules) in modules { - match pkg { - Some(pkg) => { - let prev = map - .entry(&pkg.namespace) - .or_insert(BTreeMap::new()) - .insert(&pkg.name, modules); - assert!(prev.is_none()); - } - None => { - for module in modules { - uwriteln!(self.src, "{module}"); - } - } - } - } - for (ns, pkgs) in map { - uwriteln!(self.src, "namespace {} {{", ns.to_snake_case()); - for (pkg, modules) in pkgs { - uwriteln!(self.src, "namespace {} {{", pkg.to_snake_case()); - for module in modules { - uwriteln!(self.src, "{module}"); - } - uwriteln!(self.src, "}}"); - } - uwriteln!(self.src, "}}"); - } - } -} - -impl WorldGenerator for Cpp { - fn preprocess(&mut self, resolve: &Resolve, world: WorldId) { - self.world = Some(world); - let version = env!("CARGO_PKG_VERSION"); - uwriteln!( - self.src, - "// Generated by `wit-bindgen` {version}. DO NOT EDIT!" - ); - uwriteln!( - self.src, - r#"#include "{}_cpp.h" - #include - - 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; - }} - - "#, - resolve.worlds[world].name.to_snake_case(), - ); - self.types.analyze(resolve); - } - - fn import_interface( - &mut self, - resolve: &Resolve, - name: &WorldKey, - id: InterfaceId, - _files: &mut Files, - ) { - let wasm_import_module = resolve.name_world_key(name); - let mut gen = self.interface( - Identifier::Interface(id, name), - Some(&wasm_import_module), - resolve, - true, - ); - let (snake, path_to_root, pkg) = gen.start_append_submodule(name); - gen.types(id); - - gen.generate_imports(resolve.interfaces[id].functions.values()); - - gen.finish_append_submodule(&snake, &path_to_root, pkg); - } - - fn import_funcs( - &mut self, - resolve: &Resolve, - world: WorldId, - funcs: &[(&str, &Function)], - _files: &mut Files, - ) { - self.import_funcs_called = true; - - let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true); - - gen.generate_imports(funcs.iter().map(|(_, func)| *func)); - - let src = gen.finish(); - self.src.push_str(&src); - } - - fn export_interface( - &mut self, - resolve: &Resolve, - name: &WorldKey, - id: InterfaceId, - _files: &mut Files, - ) -> std::result::Result<(), anyhow::Error> { - let (pkg, inner_name) = match name { - WorldKey::Name(name) => (None, name), - WorldKey::Interface(id) => { - let interface = &resolve.interfaces[*id]; - ( - Some(&resolve.packages[interface.package.unwrap()].name), - interface.name.as_ref().unwrap(), - ) - } - }; - let path = format!( - "{}{inner_name}", - if let Some(pkg) = pkg { - format!("{}::{}::", pkg.namespace, pkg.name) - } else { - String::new() - } - ); - let impl_name = self - .opts - .interface_exports - .get(&path) - .cloned() - .or_else(|| self.opts.stubs.then(|| "Stub".to_owned())) - .ok_or_else(|| format!("interface export implementation required for `{path}`")); - let mut gen = self.interface(Identifier::Interface(id, name), None, resolve, false); - let (snake, path_to_root, pkg) = gen.start_append_submodule(name); - gen.types(id); - gen.generate_exports( - &inner_name.to_upper_camel_case(), - Some(&path), - impl_name.as_deref(), - Some(name), - resolve.interfaces[id].functions.values(), - ); - gen.finish_append_submodule(&snake, &path_to_root, pkg); - Ok(()) - } - - fn export_funcs( - &mut self, - resolve: &Resolve, - world: WorldId, - funcs: &[(&str, &Function)], - _files: &mut Files, - ) -> std::result::Result<(), anyhow::Error> { - let world_name = &resolve.worlds[world].name; - let impl_name = self - .opts - .world_exports - .clone() - .or_else(|| self.opts.stubs.then(|| "Stub".to_owned())) - .ok_or_else(|| format!("world export implementation required")); - let trait_name = world_name.to_upper_camel_case(); - let mut gen = self.interface(Identifier::World(world), None, resolve, false); - gen.generate_exports( - &trait_name, - None, - impl_name.as_deref(), - None, - funcs.iter().map(|f| f.1), - ); - let src = gen.finish(); - self.src.push_str(&src); - Ok(()) - } - - fn import_types( - &mut self, - resolve: &Resolve, - world: WorldId, - types: &[(&str, TypeId)], - _files: &mut Files, - ) { - let mut gen = self.interface(Identifier::World(world), None, resolve, true); - for (name, ty) in types { - gen.define_type(name, *ty); - } - let src = gen.finish(); - self.src.push_str(&src); - } - - fn finish(&mut self, resolve: &Resolve, world: WorldId, files: &mut Files) { - if !self.import_funcs_called { - // We call `import_funcs` even if the world doesn't import any - // functions since one of the side effects of that method is to - // generate `struct`s for any imported resources. - self.import_funcs(resolve, world, &[], files); - } - - let name = &resolve.worlds[world].name; - let imports = mem::take(&mut self.import_modules); - self.emit_modules(&imports); - let _exports = mem::take(&mut self.export_modules); - // if !exports.is_empty() { - // self.src.push_str("pub mod exports {\n"); - // self.emit_modules(&exports); - // self.src.push_str("}\n"); - // } - - // The custom section name here must start with "component-type" but - // otherwise is attempted to be unique here to ensure that this doesn't get - // concatenated to other custom sections by LLD by accident since LLD will - // concatenate custom sections of the same name. - let mut producers = wasm_metadata::Producers::empty(); - producers.add( - "processed-by", - env!("CARGO_PKG_NAME"), - env!("CARGO_PKG_VERSION"), - ); - - let _component_type = wit_component::metadata::encode( - resolve, - world, - wit_component::StringEncoding::UTF8, - Some(&producers), - ) - .unwrap(); - - // if self.opts.stubs { - // self.src.push_str("\npub struct Stub;\n"); - // let world_id = world; - // let world = &resolve.worlds[world]; - // let mut funcs = Vec::new(); - // for (name, export) in world.exports.iter() { - // let (pkg, name) = match name { - // WorldKey::Name(name) => (None, name), - // WorldKey::Interface(id) => { - // let interface = &resolve.interfaces[*id]; - // ( - // Some(&resolve.packages[interface.package.unwrap()].name), - // interface.name.as_ref().unwrap(), - // ) - // } - // }; - // match export { - // WorldItem::Function(func) => { - // funcs.push(func); - // } - // WorldItem::Interface(id) => { - // for (resource, funcs) in - // group_by_resource(resolve.interfaces[*id].functions.values()) - // { - // let mut gen = - // self.interface(Identifier::World(world_id), None, resolve, false); - // gen.generate_stub(resource, pkg, name, true, &funcs); - // let stub = gen.finish(); - // self.src.push_str(&stub); - // } - // } - // WorldItem::Type(_) => unreachable!(), - // } - // } - - // for (resource, funcs) in group_by_resource(funcs.into_iter()) { - // let mut gen = self.interface(Identifier::World(world_id), None, resolve, false); - // gen.generate_stub(resource, None, &world.name, false, &funcs); - // let stub = gen.finish(); - // self.src.push_str(&stub); - // } - // } - - let src = mem::take(&mut self.src); - let module_name = name.to_snake_case(); - files.push(&format!("{module_name}.cpp"), src.as_bytes()); - } -} - -#[derive(Clone)] -enum Identifier<'a> { - World(WorldId), - Interface(InterfaceId, &'a WorldKey), -} - -struct InterfaceGenerator<'a> { - src: Source, - identifier: Identifier<'a>, - in_import: bool, - sizes: SizeAlign, - gen: &'a mut Cpp, - wasm_import_module: Option<&'a str>, - resolve: &'a Resolve, - return_pointer_area_size: usize, - return_pointer_area_align: usize, -} - -impl InterfaceGenerator<'_> { - fn generate_exports<'a>( - &mut self, - trait_name: &str, - path: Option<&str>, - impl_name: Result<&str, &String>, - interface_name: Option<&WorldKey>, - funcs: impl Iterator, - ) { - let mut by_resource = group_by_resource(funcs); - - // Make sure we generate code for resources with no methods: - match self.identifier { - Identifier::Interface(id, _) => { - for ty in self.resolve.interfaces[id].types.values() { - if let TypeDefKind::Resource = &self.resolve.types[*ty].kind { - by_resource.entry(Some(*ty)).or_default(); - } - } - } - Identifier::World(id) => { - let world = &self.resolve.worlds[id]; - for item in world.exports.values() { - if let WorldItem::Type(_) = item { - // As of this writing, there's no way this can be represented in WIT, but it should be easy - // to handle if that changes. - todo!() - } - } - } - } - - for (resource, funcs) in by_resource { - let trait_name = if let Some(ty) = resource { - self.resolve.types[ty] - .name - .as_deref() - .unwrap() - .to_upper_camel_case() - } else { - trait_name.to_owned() - }; - let mut saw_export = false; - uwriteln!(self.src, "pub trait {trait_name} {{"); - for &func in &funcs { - if self.gen.skip.contains(&func.name) { - continue; - } - saw_export = true; - let mut sig = FnSig::default(); - sig.use_item_name = true; - sig.private = true; - if let FunctionKind::Method(_) = &func.kind { - // sig.self_arg = Some("&self".into()); - // sig.self_is_first_param = true; - } - self.print_signature(func, TypeMode::Owned, &sig); - self.src.push_str(";\n"); - } - uwriteln!(self.src, "}}"); - - if saw_export || resource.is_some() { - let mut path_to_root = String::new(); - if let Some(key) = interface_name { - if !self.in_import { - path_to_root.push_str("super::"); - } - if let WorldKey::Interface(_) = key { - path_to_root.push_str("super::super::"); - } - path_to_root.push_str("super::"); - } - if let Some(ty) = resource { - let name = self.resolve.types[ty].name.as_deref().unwrap(); - let path = if let Some(path) = path { - format!("{path}::{name}") - } else { - name.to_owned() - }; - let impl_name = self - .gen - .opts - .resource_exports - .get(&path) - .cloned() - .or_else(|| self.gen.opts.stubs.then(|| "Stub".to_owned())) - .ok_or_else(|| { - format!("resource export implementation required for `{path}`") - }) - .unwrap(); - - uwriteln!( - self.src, - "use {path_to_root}{impl_name} as Rep{trait_name};" - ); - } else { - let impl_name = impl_name.unwrap(); - uwriteln!( - self.src, - "use {path_to_root}{impl_name} as {trait_name}Impl;" - ); - } - if saw_export { - self.src.push_str("const _: () = {\n"); - for &func in &funcs { - self.generate_guest_export(func, interface_name, &trait_name); - } - self.src.push_str("};\n"); - } - - if let Some(ty) = resource { - self.finish_resource_export(ty); - } - } - } - } - - fn make_export_name(input: &str) -> String { - input - .chars() - .map(|c| match c { - 'A'..='Z' | 'a'..='z' | '0'..='9' => c, - _ => '_', - }) - .collect() - } - - fn export_name2(module_name: &str, name: &str) -> String { - let mut res = Self::make_export_name(module_name); - res.push('_'); - res.push_str(&Self::make_export_name(name)); - res - } - - fn declare_import2( - module_name: &str, - name: &str, - args: &str, - result: &str, - ) -> (String, String) { - let extern_name = Self::export_name2(module_name, name); - let import = format!("extern __attribute__((import_module(\"{module_name}\")))\n __attribute__((import_name(\"{name}\")))\n {result} {extern_name}({args});\n"); - (extern_name, import) - } - - fn generate_imports<'a>(&mut self, funcs: impl Iterator) { - let wasm_import_module = self.wasm_import_module.unwrap(); - let mut by_resource = group_by_resource(funcs); - - // Make sure we generate code for resources with no methods: - match self.identifier { - Identifier::Interface(id, _) => { - for ty in self.resolve.interfaces[id].types.values() { - if let TypeDefKind::Resource = &self.resolve.types[*ty].kind { - by_resource.entry(Some(*ty)).or_default(); - } - } - } - Identifier::World(id) => { - let world = &self.resolve.worlds[id]; - for item in world.imports.values() { - if let WorldItem::Type(ty) = item { - if let TypeDefKind::Resource = &self.resolve.types[*ty].kind { - by_resource.entry(Some(*ty)).or_default(); - } - } - } - } - } - - for (resource, funcs) in by_resource { - if let Some(resource) = resource { - let name = self.resolve.types[resource].name.as_deref().unwrap(); - - let camel = name.to_upper_camel_case(); - - let (name_drop, code) = Self::declare_import2( - wasm_import_module, - &format!("[resource-drop]{name}"), - "int32_t", - "void", - ); - // destructor - uwriteln!( - self.src, - r#"{camel}::~{camel}() {{ - {code} - if (handle>=0) - {name_drop}(handle); - }} - "# - ); - // construct from handle (in binding) - let world = self - .gen - .world - .map(|w| &self.resolve.worlds[w].name) - .unwrap() - .to_snake_case(); - let base_name = format!("{world}::{RESOURCE_BASE_CLASS_NAME}"); - uwriteln!( - self.src, - r#"{camel}::{camel}({base_name}&& handle) : {base_name}(std::move(handle)) {{}}"# - ); - } - for func in funcs { - self.generate_guest_import(func); - } - if resource.is_some() { - self.src.push_str("}\n"); - } - } - } - - fn finish(&mut self) -> String { - // if self.return_pointer_area_align > 0 { - // uwrite!( - // self.src, - // " - // #[allow(unused_imports)] - // use wit_bindgen::rt::{{alloc, vec::Vec, string::String}}; - - // #[repr(align({align}))] - // struct _RetArea([u8; {size}]); - // static mut _RET_AREA: _RetArea = _RetArea([0; {size}]); - // ", - // align = self.return_pointer_area_align, - // size = self.return_pointer_area_size, - // ); - // } - - mem::take(&mut self.src).into() - } - - fn finish_resource_export(&mut self, id: TypeId) { - let _info = self.gen.resources.entry(id).or_default(); - let name = self.resolve.types[id].name.as_deref().unwrap(); - let _camel = name.to_upper_camel_case(); - let _snake = to_rust_ident(name); - let _export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); - let _interface_name = if let TypeOwner::Interface(id) = self.resolve.types[id].owner { - &self.gen.interface_names[&id] - } else { - unreachable!() - }; - } - fn start_append_submodule(&mut self, name: &WorldKey) -> (String, String, Option) { - let snake = match name { - WorldKey::Name(name) => to_rust_ident(name), - WorldKey::Interface(id) => { - to_rust_ident(self.resolve.interfaces[*id].name.as_ref().unwrap()) - } - }; - let mut path_to_root = String::from("super::"); - let pkg = match name { - WorldKey::Name(_) => None, - WorldKey::Interface(id) => { - let pkg = self.resolve.interfaces[*id].package.unwrap(); - Some(self.resolve.packages[pkg].name.clone()) - } - }; - if let Identifier::Interface(id, _) = self.identifier { - let mut path = String::new(); - if !self.in_import { - path.push_str("exports::"); - path_to_root.push_str("super::"); - } - if let Some(name) = &pkg { - path.push_str(&format!( - "{}::{}::", - name.namespace.to_snake_case(), - name.name.to_snake_case() - )); - path_to_root.push_str("super::super::"); - } - path.push_str(&snake); - self.gen.interface_names.insert(id, path); - } - (snake, path_to_root, pkg) - } - - fn finish_append_submodule( - mut self, - snake: &str, - _path_to_root: &str, - pkg: Option, - ) { - let module = self.finish(); - let module = format!( - " - namespace {snake} {{ - {module} - ", - ); - let map = if self.in_import { - &mut self.gen.import_modules - } else { - &mut self.gen.export_modules - }; - map.entry(pkg).or_insert(Vec::new()).push(module); - } - - // fn print_signature_cpp( - // &mut self, - // func: &Function, - // param_mode: TypeMode, - // sig: &FnSig, - // ) -> Vec { - // if !matches!(func.kind, FunctionKind::Constructor(_)) { - // self.print_results_cpp(&func.results, TypeMode::Owned); - // self.push_str(" "); - // } - // let params = self.print_docs_and_params_cpp(func, param_mode, &sig); - // params - // } - - // fn print_docs_and_params_cpp( - // &mut self, - // func: &Function, - // param_mode: TypeMode, - // sig: &FnSig, - // ) -> Vec { - // // self.rustdoc(&func.docs); - // // self.rustdoc_params(&func.params, "Parameters"); - // // TODO: re-add this when docs are back - // // self.rustdoc_params(&func.results, "Return"); - - // let object = match &func.kind { - // FunctionKind::Freestanding => None, - // FunctionKind::Method(i) => Some(i), - // FunctionKind::Static(i) => Some(i), - // FunctionKind::Constructor(i) => Some(i), - // } - // .map(|i| { - // self.resolve.types[*i] - // .name - // .as_ref() - // .unwrap() - // .to_pascal_case() - // }) - // .unwrap_or_default(); - // let func_name = if sig.use_item_name { - // if let FunctionKind::Constructor(i) = &func.kind { - // format!("{object}::{object}") - // } else { - // format!("{object}::{}", func.item_name().to_pascal_case()) - // } - // } else { - // func.name.to_pascal_case() - // }; - // self.push_str(&func_name); - // if let Some(generics) = &sig.generics { - // self.push_str(generics); - // } - // self.push_str("("); - // if let Some(arg) = &sig.self_arg { - // self.push_str(arg); - // self.push_str(","); - // } - // let mut params = Vec::new(); - // for (i, (name, param)) in func.params.iter().enumerate() { - // params.push(name.clone()); - // if i == 0 && sig.self_is_first_param { - // // params.push("self".to_string()); - // continue; - // } - // if i == 0 && name == "self" { - // continue; - // } - // let name = to_rust_ident(name); - // self.print_ty_cpp(param, param_mode); - // self.push_str(" "); - // self.push_str(&name); - // if i + 1 != func.params.len() { - // self.push_str(","); - // } - // } - // self.push_str(")"); - // params - // } - - // fn print_tyid_cpp(&mut self, id: TypeId, mode: TypeMode) { - // let info = self.info(id); - // let lt = self.lifetime_for(&info, mode); - // let ty = &RustGenerator::resolve(self).types[id]; - // if ty.name.is_some() { - // // If this type has a list internally, no lifetime is being printed, - // // but we're in a borrowed mode, then that means we're in a borrowed - // // context and don't want ownership of the type but we're using an - // // owned type definition. Inject a `&` in front to indicate that, at - // // the API level, ownership isn't required. - // if info.has_list && lt.is_none() { - // if let TypeMode::AllBorrowed(lt) | TypeMode::LeafBorrowed(lt) = mode { - // self.push_str("&"); - // if lt != "'_" { - // self.push_str(lt); - // self.push_str(" "); - // } - // } - // } - // let name = self.type_path(id, lt.is_none()); - // self.push_str(&name); - - // // If the type recursively owns data and it's a - // // variant/record/list, then we need to place the - // // lifetime parameter on the type as well. - // if info.has_list && needs_generics(RustGenerator::resolve(self), &ty.kind) { - // self.print_generics(lt); - // } - - // return; - - // fn needs_generics(resolve: &Resolve, ty: &TypeDefKind) -> bool { - // match ty { - // TypeDefKind::Variant(_) - // | TypeDefKind::Record(_) - // | TypeDefKind::Option(_) - // | TypeDefKind::Result(_) - // | TypeDefKind::Future(_) - // | TypeDefKind::Stream(_) - // | TypeDefKind::List(_) - // | TypeDefKind::Flags(_) - // | TypeDefKind::Enum(_) - // | TypeDefKind::Tuple(_) - // | TypeDefKind::Union(_) => true, - // TypeDefKind::Type(Type::Id(t)) => { - // needs_generics(resolve, &resolve.types[*t].kind) - // } - // TypeDefKind::Type(Type::String) => true, - // TypeDefKind::Resource | TypeDefKind::Handle(_) | TypeDefKind::Type(_) => false, - // TypeDefKind::Unknown => unreachable!(), - // } - // } - // } - - // match &ty.kind { - // TypeDefKind::List(t) => self.print_list(t, mode), - - // TypeDefKind::Option(t) => { - // self.push_str("Option<"); - // self.print_ty(t, mode); - // self.push_str(">"); - // } - - // TypeDefKind::Result(r) => { - // self.push_str("Result<"); - // self.print_optional_ty(r.ok.as_ref(), mode); - // self.push_str(","); - // self.print_optional_ty(r.err.as_ref(), mode); - // self.push_str(">"); - // } - - // TypeDefKind::Variant(_) => panic!("unsupported anonymous variant"), - - // // Tuple-like records are mapped directly to Rust tuples of - // // types. Note the trailing comma after each member to - // // appropriately handle 1-tuples. - // TypeDefKind::Tuple(t) => { - // self.push_str("("); - // for ty in t.types.iter() { - // self.print_ty(ty, mode); - // self.push_str(","); - // } - // self.push_str(")"); - // } - // TypeDefKind::Resource => { - // panic!("unsupported anonymous type reference: resource") - // } - // TypeDefKind::Record(_) => { - // panic!("unsupported anonymous type reference: record") - // } - // TypeDefKind::Flags(_) => { - // panic!("unsupported anonymous type reference: flags") - // } - // TypeDefKind::Enum(_) => { - // panic!("unsupported anonymous type reference: enum") - // } - // TypeDefKind::Union(_) => { - // panic!("unsupported anonymous type reference: union") - // } - // TypeDefKind::Future(ty) => { - // self.push_str("Future<"); - // self.print_optional_ty(ty.as_ref(), mode); - // self.push_str(">"); - // } - // TypeDefKind::Stream(stream) => { - // self.push_str("Stream<"); - // self.print_optional_ty(stream.element.as_ref(), mode); - // self.push_str(","); - // self.print_optional_ty(stream.end.as_ref(), mode); - // self.push_str(">"); - // } - - // TypeDefKind::Handle(Handle::Own(ty)) => { - // self.mark_resource_owned(*ty); - // self.print_ty(&Type::Id(*ty), mode); - // } - - // TypeDefKind::Handle(Handle::Borrow(ty)) => { - // self.push_str("&"); - // self.print_ty(&Type::Id(*ty), mode); - // } - - // TypeDefKind::Type(t) => self.print_ty(t, mode), - - // TypeDefKind::Resource => { - // todo!("implement resources") - // } - - // TypeDefKind::Unknown => unreachable!(), - // } - // } - - // fn print_ty_cpp(&mut self, ty: &Type, mode: TypeMode) { - // match ty { - // Type::Id(t) => self.print_tyid_cpp(*t, mode), - // Type::Bool => self.push_str("bool"), - // Type::U8 => self.push_str("uint8_t"), - // Type::U16 => self.push_str("uint16_t"), - // Type::U32 => self.push_str("uint32_t"), - // Type::U64 => self.push_str("uint64_t"), - // Type::S8 => self.push_str("int8_t"), - // Type::S16 => self.push_str("int16_t"), - // Type::S32 => self.push_str("int32_t"), - // Type::S64 => self.push_str("int64_t"), - // Type::Float32 => self.push_str("float"), - // Type::Float64 => self.push_str("double"), - // Type::Char => self.push_str("int32_t"), - // Type::String => match mode { - // TypeMode::AllBorrowed(lt) | TypeMode::LeafBorrowed(lt) => { - // self.push_str("std::string_view"); - // } - // TypeMode::Owned => { - // self.push_str("std::string"); - // } - // }, - // } - // } - - // fn print_option_ty_cpp(&mut self, ty: Option<&Type>, mode: TypeMode) { - // match ty { - // Some(ty) => self.print_ty_cpp(ty, mode), - // None => self.push_str("void"), - // } - // } - - // fn print_results_cpp(&mut self, results: &Results, mode: TypeMode) { - // match results.len() { - // 0 | 1 => self.print_option_ty_cpp(results.iter_types().next(), mode), - // _ => todo!(), - // } - // } - - fn generate_guest_import(&mut self, func: &Function) { - if self.gen.skip.contains(&func.name) { - return; - } - - let mut sig = FnSig::default(); - let param_mode = TypeMode::AllBorrowed("'_"); - match &func.kind { - FunctionKind::Freestanding => {} - FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::Constructor(_) => { - sig.use_item_name = true; - // if let FunctionKind::Method(_) = &func.kind { - // sig.self_arg = Some("&self".into()); - // sig.self_is_first_param = true; - // } - } - } - // self.src.push_str("#[allow(clippy::all)]\n"); - let params = self.print_signature(func, param_mode, &sig); - if matches!(func.kind, FunctionKind::Method(_)) { - self.src.push_str("const"); - } - self.src.push_str("{\n"); - - let mut f = FunctionBindgen::new(self, params, None); - abi::call( - f.gen.resolve, - AbiVariant::GuestImport, - LiftLower::LowerArgsLiftResults, - func, - &mut f, - ); - let FunctionBindgen { - // needs_cleanup_list, - src, - import_return_pointer_area_size, - import_return_pointer_area_align, - .. - } = f; - - // if needs_cleanup_list { - // self.src.push_str("let mut cleanup_list = Vec::new();\n"); - // } - if import_return_pointer_area_size > 0 { - let align = import_return_pointer_area_align.max(4); - let elems = (import_return_pointer_area_size + (align - 1)) / align; - let tp = match align { - 4 => "uint32_t", - 8 => "uint64_t", - _ => todo!(), - }; - uwriteln!(self.src, " {tp} ret_area[{elems}];") - } - // uwrite!( - // self.src, - // " - // #[repr(align({import_return_pointer_area_align}))] - // struct RetArea([u8; {import_return_pointer_area_size}]); - // let mut ret_area = ::core::mem::MaybeUninit::::uninit(); - // ", - // ); - // } - self.src.push_str(&String::from(src)); - - // self.src.push_str("}\n"); - self.src.push_str("}\n"); - } - - fn generate_guest_export( - &mut self, - func: &Function, - _interface_name: Option<&WorldKey>, - _trait_name: &str, - ) { - if self.gen.skip.contains(&func.name) { - return; - } - todo!(); - - // let name_snake = func.name.to_snake_case().replace('.', "_"); - // let wasm_module_export_name = interface_name.map(|k| self.resolve.name_world_key(k)); - // let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); - // let export_name = func.core_export_name(wasm_module_export_name.as_deref()); - // uwrite!( - // self.src, - // " - // #[doc(hidden)] - // #[export_name = \"{export_prefix}{export_name}\"] - // #[allow(non_snake_case)] - // unsafe extern \"C\" fn __export_{name_snake}(\ - // ", - // ); - - // let sig = self.resolve.wasm_signature(AbiVariant::GuestExport, func); - // let mut params = Vec::new(); - // for (i, param) in sig.params.iter().enumerate() { - // let name = format!("arg{}", i); - // uwrite!(self.src, "{name}: {},", wasm_type(*param)); - // params.push(name); - // } - // self.src.push_str(")"); - - // match sig.results.len() { - // 0 => {} - // 1 => { - // uwrite!(self.src, " -> {}", wasm_type(sig.results[0])); - // } - // _ => unimplemented!(), - // } - - // self.push_str(" {"); - - // let mut f = FunctionBindgen::new(self, params, Some(trait_name)); - // f.gen.resolve.call( - // AbiVariant::GuestExport, - // LiftLower::LiftArgsLowerResults, - // func, - // &mut f, - // ); - // let FunctionBindgen { - // needs_cleanup_list, - // src, - // .. - // } = f; - // assert!(!needs_cleanup_list); - // self.src.push_str(&String::from(src)); - // self.src.push_str("}\n"); - - // if self.resolve.guest_export_needs_post_return(func) { - // let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); - // let mut params = Vec::new(); - // for (i, result) in sig.results.iter().enumerate() { - // let name = format!("arg{}", i); - // uwrite!(self.src, "{name}: {},", wasm_type(*result)); - // params.push(name); - // } - // self.src.push_str(") {\n"); - - // let mut f = FunctionBindgen::new(self, params, Some(trait_name)); - // f.gen.resolve.post_return(func, &mut f); - // let FunctionBindgen { - // needs_cleanup_list, - // src, - // .. - // } = f; - // assert!(!needs_cleanup_list); - // self.src.push_str(&String::from(src)); - // self.src.push_str("}\n"); - // self.src.push_str("};\n"); - // } - } - - // fn generate_stub( - // &mut self, - // resource: Option, - // pkg: Option<&PackageName>, - // name: &str, - // in_interface: bool, - // funcs: &[&Function], - // ) { - // let path = if let Some(pkg) = pkg { - // format!( - // "{}::{}::{}", - // to_rust_ident(&pkg.namespace), - // to_rust_ident(&pkg.name), - // to_rust_ident(name), - // ) - // } else { - // to_rust_ident(name) - // }; - - // let name = resource - // .map(|ty| { - // self.resolve.types[ty] - // .name - // .as_deref() - // .unwrap() - // .to_upper_camel_case() - // }) - // .unwrap_or_else(|| name.to_upper_camel_case()); - - // let qualified_name = if in_interface { - // format!("exports::{path}::{name}") - // } else { - // name - // }; - - // uwriteln!(self.src, "impl {qualified_name} for Stub {{"); - - // for &func in funcs { - // if self.gen.skip.contains(&func.name) { - // continue; - // } - // let mut sig = FnSig::default(); - // sig.use_item_name = true; - // sig.private = true; - // if let FunctionKind::Method(_) = &func.kind { - // // sig.self_arg = Some("&self".into()); - // // sig.self_is_first_param = true; - // } - // self.print_signature(func, TypeMode::Owned, &sig); - // self.src.push_str("{ unreachable!() }\n"); - // } - - // self.src.push_str("}\n"); - // } -} - -impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> { - fn resolve(&self) -> &'a Resolve { - self.resolve - } - - fn ownership(&self) -> Ownership { - self.gen.opts.ownership - } - - fn path_to_interface(&self, interface: InterfaceId) -> Option { - let mut path = String::new(); - if let Identifier::Interface(cur, name) = self.identifier { - if cur == interface { - return None; - } - if !self.in_import { - //path.push_str("super::"); - } - match name { - WorldKey::Name(_) => { - //path.push_str("super::"); - } - WorldKey::Interface(_) => { - //path.push_str("super::super::super::"); - } - } - } - let name = &self.gen.interface_names[&interface]; - path.push_str(&name); - Some(path) - } - - fn is_exported_resource(&self, ty: TypeId) -> bool { - matches!( - self.gen - .resources - .get(&dealias(self.resolve, ty)) - .map(|info| info.direction), - Some(Direction::Export) - ) - } - - // fn add_own(&mut self, resource: TypeId, handle: TypeId) { - // self.gen - // .resources - // .entry(dealias(self.resolve, resource)) - // .or_default() - // .own = Some(handle); - // } - - fn push_str(&mut self, s: &str) { - self.src.push_str(s); - } - - fn info(&self, ty: TypeId) -> TypeInfo { - self.gen.types.get(ty) - } - - fn types_mut(&mut self) -> &mut Types { - &mut self.gen.types - } - - fn print_borrowed_slice( - &mut self, - mutbl: bool, - ty: &Type, - lifetime: &'static str, - mode: TypeMode, - ) { - self.print_rust_slice(mutbl, ty, lifetime, mode); - } - - fn print_borrowed_str(&mut self, _lifetime: &'static str) { - self.push_str("&"); - // if self.gen.opts.raw_strings { - // self.push_str("[u8]"); - // } else { - self.push_str("str"); - // } - } - - fn push_vec_name(&mut self) { - self.push_str("std::vector"); - } - - fn push_string_name(&mut self) { - self.push_str("std::string"); - } - - fn mark_resource_owned(&mut self, resource: TypeId) { - self.gen - .resources - .entry(dealias(self.resolve, resource)) - .or_default() - .owned = true; - } - - fn print_signature( - &mut self, - func: &Function, - param_mode: TypeMode, - sig: &FnSig, - ) -> Vec { - if !matches!(func.kind, FunctionKind::Constructor(_)) { - self.print_results(&func.results, TypeMode::Owned); - self.push_str(" "); - } - let params = self.print_docs_and_params(func, param_mode, &sig); - params - } - - fn print_docs_and_params( - &mut self, - func: &Function, - param_mode: TypeMode, - sig: &FnSig, - ) -> Vec { - // self.rustdoc(&func.docs); - // self.rustdoc_params(&func.params, "Parameters"); - // TODO: re-add this when docs are back - // self.rustdoc_params(&func.results, "Return"); - - let object = match &func.kind { - FunctionKind::Freestanding => None, - FunctionKind::Method(i) => Some(i), - FunctionKind::Static(i) => Some(i), - FunctionKind::Constructor(i) => Some(i), - } - .map(|i| { - self.resolve.types[*i] - .name - .as_ref() - .unwrap() - .to_pascal_case() - }) - .unwrap_or_default(); - let func_name = if sig.use_item_name { - if let FunctionKind::Constructor(_i) = &func.kind { - format!("{object}::{object}") - } else { - format!("{object}::{}", func.item_name().to_pascal_case()) - } - } else { - func.name.to_pascal_case() - }; - self.push_str(&func_name); - if let Some(generics) = &sig.generics { - self.push_str(generics); - } - self.push_str("("); - if let Some(arg) = &sig.self_arg { - self.push_str(arg); - self.push_str(","); - } - let mut params = Vec::new(); - for (i, (name, param)) in func.params.iter().enumerate() { - params.push(to_rust_ident(name)); - if i == 0 && sig.self_is_first_param { - // params.push("self".to_string()); - continue; - } - if i == 0 && name == "self" { - continue; - } - let name = to_rust_ident(name); - self.print_ty(param, param_mode); - self.push_str(" "); - self.push_str(&name); - if i + 1 != func.params.len() { - self.push_str(","); - } - } - self.push_str(")"); - params - } - - fn print_tyid(&mut self, id: TypeId, mode: TypeMode) { - let info = self.info(id); - let lt = self.lifetime_for(&info, mode); - let ty = &RustGenerator::resolve(self).types[id]; - if ty.name.is_some() { - // If this type has a list internally, no lifetime is being printed, - // but we're in a borrowed mode, then that means we're in a borrowed - // context and don't want ownership of the type but we're using an - // owned type definition. Inject a `&` in front to indicate that, at - // the API level, ownership isn't required. - if info.has_list && lt.is_none() { - if let TypeMode::AllBorrowed(lt) | TypeMode::LeafBorrowed(lt) = mode { - self.push_str("&"); - if lt != "'_" { - self.push_str(lt); - self.push_str(" "); - } - } - } - let name = self.type_path(id, lt.is_none()); - self.push_str(&name); - - // If the type recursively owns data and it's a - // variant/record/list, then we need to place the - // lifetime parameter on the type as well. - if info.has_list && needs_generics(RustGenerator::resolve(self), &ty.kind) { - self.print_generics(lt); - } - - return; - - fn needs_generics(resolve: &Resolve, ty: &TypeDefKind) -> bool { - match ty { - TypeDefKind::Variant(_) - | TypeDefKind::Record(_) - | TypeDefKind::Option(_) - | TypeDefKind::Result(_) - | TypeDefKind::Future(_) - | TypeDefKind::Stream(_) - | TypeDefKind::List(_) - | TypeDefKind::Flags(_) - | TypeDefKind::Enum(_) - | TypeDefKind::Tuple(_) => true, - TypeDefKind::Type(Type::Id(t)) => { - needs_generics(resolve, &resolve.types[*t].kind) - } - TypeDefKind::Type(Type::String) => true, - TypeDefKind::Resource | TypeDefKind::Handle(_) | TypeDefKind::Type(_) => false, - TypeDefKind::Unknown => unreachable!(), - } - } - } - - match &ty.kind { - TypeDefKind::List(t) => self.print_list(t, mode), - - TypeDefKind::Option(t) => { - self.push_str("std::option<"); - self.print_ty(t, mode); - self.push_str(">"); - } - - TypeDefKind::Result(r) => { - self.push_str("std::expected<"); - self.print_optional_ty(r.ok.as_ref(), mode); - self.push_str(","); - self.print_optional_ty(r.err.as_ref(), mode); - self.push_str(">"); - } - - TypeDefKind::Variant(_) => panic!("unsupported anonymous variant"), - - // Tuple-like records are mapped directly to Rust tuples of - // types. Note the trailing comma after each member to - // appropriately handle 1-tuples. - TypeDefKind::Tuple(t) => { - self.push_str("("); - for ty in t.types.iter() { - self.print_ty(ty, mode); - self.push_str(","); - } - self.push_str(")"); - } - TypeDefKind::Resource => { - panic!("unsupported anonymous type reference: resource") - } - TypeDefKind::Record(_) => { - panic!("unsupported anonymous type reference: record") - } - TypeDefKind::Flags(_) => { - panic!("unsupported anonymous type reference: flags") - } - TypeDefKind::Enum(_) => { - panic!("unsupported anonymous type reference: enum") - } - TypeDefKind::Future(ty) => { - self.push_str("Future<"); - self.print_optional_ty(ty.as_ref(), mode); - self.push_str(">"); - } - TypeDefKind::Stream(stream) => { - self.push_str("Stream<"); - self.print_optional_ty(stream.element.as_ref(), mode); - self.push_str(","); - self.print_optional_ty(stream.end.as_ref(), mode); - self.push_str(">"); - } - - TypeDefKind::Handle(Handle::Own(ty)) => { - self.mark_resource_owned(*ty); - self.print_ty(&Type::Id(*ty), mode); - } - - TypeDefKind::Handle(Handle::Borrow(ty)) => { - self.push_str("&"); - self.print_ty(&Type::Id(*ty), mode); - } - - TypeDefKind::Type(t) => self.print_ty(t, mode), - - // TypeDefKind::Resource => { - // todo!("implement resources") - // } - TypeDefKind::Unknown => unreachable!(), - } - } - - fn print_ty(&mut self, ty: &Type, mode: TypeMode) { - match ty { - Type::Id(t) => self.print_tyid(*t, mode), - Type::Bool => self.push_str("bool"), - Type::U8 => self.push_str("uint8_t"), - Type::U16 => self.push_str("uint16_t"), - Type::U32 => self.push_str("uint32_t"), - Type::U64 => self.push_str("uint64_t"), - Type::S8 => self.push_str("int8_t"), - Type::S16 => self.push_str("int16_t"), - Type::S32 => self.push_str("int32_t"), - Type::S64 => self.push_str("int64_t"), - Type::Float32 => self.push_str("float"), - Type::Float64 => self.push_str("double"), - Type::Char => self.push_str("int32_t"), - Type::String => match mode { - TypeMode::AllBorrowed(_lt) | TypeMode::LeafBorrowed(_lt) => { - self.push_str("std::string_view"); - } - TypeMode::Owned => { - self.push_str("std::string"); - } - TypeMode::HandlesBorrowed(_) => todo!(), - }, - } - } - - fn print_optional_ty(&mut self, ty: Option<&Type>, mode: TypeMode) { - match ty { - Some(ty) => self.print_ty(ty, mode), - None => self.push_str("void"), - } - } - - fn print_results(&mut self, results: &Results, mode: TypeMode) { - match results.len() { - 0 | 1 => self.print_optional_ty(results.iter_types().next(), mode), - _ => todo!(), - } - } - - fn wasm_type(&mut self, ty: WasmType) { - self.push_str(wasm_type(ty)); - } - - fn print_list(&mut self, ty: &Type, mode: TypeMode) { - let next_mode = if matches!(self.ownership(), Ownership::Owning) { - TypeMode::Owned - } else { - mode - }; - match mode { - TypeMode::AllBorrowed(lt) => { - self.print_borrowed_slice(false, ty, lt, next_mode); - } - TypeMode::LeafBorrowed(lt) => { - if RustGenerator::resolve(self).all_bits_valid(ty) { - self.print_borrowed_slice(false, ty, lt, next_mode); - } else { - self.push_vec_name(); - self.push_str("<"); - self.print_ty(ty, next_mode); - self.push_str(">"); - } - } - TypeMode::Owned => { - self.push_vec_name(); - self.push_str("<"); - self.print_ty(ty, next_mode); - self.push_str(">"); - } - TypeMode::HandlesBorrowed(_) => todo!(), - } - } - - fn print_rust_slice( - &mut self, - mutbl: bool, - ty: &Type, - _lifetime: &'static str, - mode: TypeMode, - ) { - self.push_str("std::vector<"); - self.print_ty(ty, mode); - self.push_str(">"); - if !mutbl { - self.push_str(" const "); - } - self.push_str("&"); - } -} - -fn wasm_type(ty: WasmType) -> &'static str { - match ty { - WasmType::I32 => "int32_t", - WasmType::I64 => "int64_t", - WasmType::F32 => "float", - WasmType::F64 => "double", - } -} - -impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { - fn resolve(&self) -> &'a Resolve { - self.resolve - } - - fn type_record(&mut self, _id: TypeId, _name: &str, _record: &Record, _docs: &Docs) { - //self.print_typedef_record(id, record, docs, false); - } - - fn type_resource(&mut self, id: TypeId, _name: &str, docs: &Docs) { - let entry = self - .gen - .resources - .entry(dealias(self.resolve, id)) - .or_default(); - if !self.in_import { - entry.direction = Direction::Export; - } - entry.docs = docs.clone(); - } - - fn type_tuple(&mut self, _id: TypeId, _name: &str, _tuple: &Tuple, _docs: &Docs) { - //self.print_typedef_tuple(id, tuple, docs); - } - - fn type_flags(&mut self, _id: TypeId, _name: &str, _flags: &Flags, _docs: &Docs) { - // self.src.push_str("wit_bindgen::bitflags::bitflags! {\n"); - // self.rustdoc(docs); - // let repr = RustFlagsRepr::new(flags); - // self.src.push_str(&format!( - // "pub struct {}: {repr} {{\n", - // name.to_upper_camel_case(), - // )); - // for (i, flag) in flags.flags.iter().enumerate() { - // self.rustdoc(&flag.docs); - // self.src.push_str(&format!( - // "const {} = 1 << {};\n", - // flag.name.to_shouty_snake_case(), - // i, - // )); - // } - // self.src.push_str("}\n"); - // self.src.push_str("}\n"); - } - - fn type_variant(&mut self, _id: TypeId, _name: &str, _variant: &Variant, _docs: &Docs) { - //self.print_typedef_variant(id, variant, docs, false); - } - - fn type_option(&mut self, _id: TypeId, _name: &str, _payload: &Type, _docs: &Docs) { - //self.print_typedef_option(id, payload, docs); - } - - fn type_result(&mut self, _id: TypeId, _name: &str, _result: &Result_, _docs: &Docs) { - //self.print_typedef_result(id, result, docs); - } - - fn type_enum(&mut self, _id: TypeId, _name: &str, _enum_: &Enum, _docs: &Docs) { - //self.print_typedef_enum(id, name, enum_, docs, &[], Box::new(|_| String::new())); - } - - fn type_alias(&mut self, _id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) { - //self.print_typedef_alias(id, ty, docs); - } - - fn type_list(&mut self, _id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) { - //self.print_type_list(id, ty, docs); - } - - fn type_builtin(&mut self, _id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) {} -} - -struct FunctionBindgen<'a, 'b> { - gen: &'b mut InterfaceGenerator<'a>, - params: Vec, - trait_name: Option<&'b str>, - src: Source, - blocks: Vec, - block_storage: Vec<(Source, Vec<(String, String)>)>, - tmp: usize, - needs_cleanup_list: bool, - cleanup: Vec<(String, String)>, - import_return_pointer_area_size: usize, - import_return_pointer_area_align: usize, -} - -impl<'a, 'b> FunctionBindgen<'a, 'b> { - fn new( - gen: &'b mut InterfaceGenerator<'a>, - params: Vec, - trait_name: Option<&'b str>, - ) -> FunctionBindgen<'a, 'b> { - FunctionBindgen { - gen, - params, - trait_name, - src: Default::default(), - blocks: Vec::new(), - block_storage: Vec::new(), - tmp: 0, - needs_cleanup_list: false, - cleanup: Vec::new(), - import_return_pointer_area_size: 0, - import_return_pointer_area_align: 0, - } - } - - fn emit_cleanup(&mut self) { - // for (ptr, layout) in mem::take(&mut self.cleanup) { - // self.push_str(&format!( - // "if {layout}.size() != 0 {{\nalloc::dealloc({ptr}, {layout});\n}}\n" - // )); - // } - // if self.needs_cleanup_list { - // self.push_str( - // "for (ptr, layout) in cleanup_list {\n - // if layout.size() != 0 {\n - // alloc::dealloc(ptr, layout);\n - // }\n - // }\n", - // ); - // } - } - - fn wasm_type_cpp(ty: WasmType) -> &'static str { - wit_bindgen_c::wasm_type(ty) - // match ty { - // WasmType::I32 => "int32_t", - // WasmType::I64 => "int64_t", - // WasmType::F32 => "float", - // WasmType::F64 => "double", - // } - } - - 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::wasm_type_cpp(*param)); - if n + 1 != params.len() { - args.push_str(", "); - } - } - let result = if results.is_empty() { - "void" - } else { - Self::wasm_type_cpp(results[0]) - }; - let (name, code) = InterfaceGenerator::declare_import2(module_name, name, &args, result); - self.src.push_str(&code); - name - // Define the actual function we're calling inline - //todo!(); - // let mut sig = "(".to_owned(); - // for param in params.iter() { - // sig.push_str("_: "); - // sig.push_str(wasm_type(*param)); - // sig.push_str(", "); - // } - // sig.push_str(")"); - // assert!(results.len() < 2); - // for result in results.iter() { - // sig.push_str(" -> "); - // sig.push_str(wasm_type(*result)); - // } - // uwriteln!( - // self.src, - // " - // #[cfg(target_arch = \"wasm32\")] - // #[link(wasm_import_module = \"{module_name}\")] - // extern \"C\" {{ - // #[link_name = \"{name}\"] - // fn wit_import{sig}; - // }} - - // #[cfg(not(target_arch = \"wasm32\"))] - // fn wit_import{sig} {{ unreachable!() }} - // " - // ); - // "wit_import".to_string() - } -} - -impl RustFunctionGenerator for FunctionBindgen<'_, '_> { - fn push_str(&mut self, s: &str) { - self.src.push_str(s); - } - - fn tmp(&mut self) -> usize { - let ret = self.tmp; - self.tmp += 1; - ret - } - - fn rust_gen(&self) -> &dyn RustGenerator { - self.gen - } - - fn lift_lower(&self) -> LiftLower { - if self.gen.in_import { - LiftLower::LowerArgsLiftResults - } else { - LiftLower::LiftArgsLowerResults - } - } -} - -impl Bindgen for FunctionBindgen<'_, '_> { - type Operand = String; - - fn push_block(&mut self) { - let prev_src = mem::take(&mut self.src); - let prev_cleanup = mem::take(&mut self.cleanup); - self.block_storage.push((prev_src, prev_cleanup)); - } - - fn finish_block(&mut self, operands: &mut Vec) { - if self.cleanup.len() > 0 { - self.needs_cleanup_list = true; - self.push_str("cleanup_list.extend_from_slice(&["); - for (ptr, layout) in mem::take(&mut self.cleanup) { - self.push_str("("); - self.push_str(&ptr); - self.push_str(", "); - self.push_str(&layout); - self.push_str("),"); - } - self.push_str("]);\n"); - } - let (prev_src, prev_cleanup) = self.block_storage.pop().unwrap(); - let src = mem::replace(&mut self.src, prev_src); - self.cleanup = prev_cleanup; - let expr = match operands.len() { - 0 => "()".to_string(), - 1 => operands[0].clone(), - _ => format!("({})", operands.join(", ")), - }; - if src.is_empty() { - self.blocks.push(expr); - } else if operands.is_empty() { - self.blocks.push(format!("{{\n{}\n}}", &src[..])); - } else { - self.blocks.push(format!("{{\n{}\n{}\n}}", &src[..], expr)); - } - } - - fn return_pointer(&mut self, size: usize, align: usize) -> String { - let tmp = self.tmp(); - - // Imports get a per-function return area to facilitate using the - // stack whereas exports use a per-module return area to cut down on - // stack usage. Note that for imports this also facilitates "adapter - // modules" for components to not have data segments. - if self.gen.in_import { - self.import_return_pointer_area_size = self.import_return_pointer_area_size.max(size); - self.import_return_pointer_area_align = - self.import_return_pointer_area_align.max(align); - uwriteln!(self.src, "auto ptr{tmp} = (int32_t)&ret_area;"); - } else { - todo!(); - // self.gen.return_pointer_area_size = self.gen.return_pointer_area_size.max(size); - // self.gen.return_pointer_area_align = self.gen.return_pointer_area_align.max(align); - // uwriteln!(self.src, "auto ptr{tmp} = _RET_AREA.0.as_mut_ptr() as i32;"); - } - format!("ptr{}", tmp) - } - - fn sizes(&self) -> &SizeAlign { - &self.gen.sizes - } - - fn is_list_canonical(&self, resolve: &Resolve, ty: &Type) -> bool { - resolve.all_bits_valid(ty) - } - - fn emit( - &mut self, - resolve: &Resolve, - inst: &Instruction<'_>, - operands: &mut Vec, - results: &mut Vec, - ) { - let mut top_as = |cvt: &str| { - results.push(format!("({cvt})({})", operands.pop().unwrap())); - }; - - // work around the fact that some functions only push - fn print_to_result<'a, 'b, 'c, T: FnOnce(&mut InterfaceGenerator<'a>)>( - slf: &'a mut FunctionBindgen<'b, 'c>, - resolve: &'a Resolve, - f: T, - ) -> String { - let mut sizes = SizeAlign::default(); - sizes.fill(resolve); - let mut gen = InterfaceGenerator { - identifier: slf.gen.identifier.clone(), - wasm_import_module: slf.gen.wasm_import_module.clone(), - src: Source::default(), - in_import: slf.gen.in_import.clone(), - gen: slf.gen.gen, - sizes, - resolve, - return_pointer_area_size: 0, - return_pointer_area_align: 0, - }; - f(&mut gen); - //gen.print_optional_ty(result.ok.as_ref(), TypeMode::Owned); - let mut ok_type = String::default(); - std::mem::swap(gen.src.as_mut_string(), &mut ok_type); - ok_type - } - - match inst { - Instruction::GetArg { nth } => results.push(self.params[*nth].clone()), - Instruction::I32Const { val } => results.push(format!("(int32_t){}", val)), - 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()), - } - } - } - - Instruction::I64FromU64 | Instruction::I64FromS64 => { - let s = operands.pop().unwrap(); - results.push(format!("(int64_t)({})", s)); - } - Instruction::I32FromChar - | Instruction::I32FromU8 - | Instruction::I32FromS8 - | Instruction::I32FromU16 - | Instruction::I32FromS16 - | Instruction::I32FromU32 - | Instruction::I32FromS32 => { - let s = operands.pop().unwrap(); - results.push(format!("(int32_t)({})", s)); - } - - Instruction::F32FromFloat32 => { - let s = operands.pop().unwrap(); - results.push(format!("(float)({})", s)); - } - Instruction::F64FromFloat64 => { - let s = operands.pop().unwrap(); - results.push(format!("(double)({})", s)); - } - Instruction::Float32FromF32 - | Instruction::Float64FromF64 - | Instruction::S32FromI32 - | Instruction::S64FromI64 => { - results.push(operands.pop().unwrap()); - } - Instruction::S8FromI32 => top_as("int8_t"), - Instruction::U8FromI32 => top_as("uint8_t"), - Instruction::S16FromI32 => top_as("int16_t"), - Instruction::U16FromI32 => top_as("uint16_t"), - Instruction::U32FromI32 => top_as("uint32_t"), - Instruction::U64FromI64 => top_as("uint64_t"), - Instruction::CharFromI32 => { - todo!(); - // results.push(format!( - // "{{ - // #[cfg(not(debug_assertions))] - // {{ ::core::char::from_u32_unchecked({} as u32) }} - // #[cfg(debug_assertions)] - // {{ ::core::char::from_u32({} as u32).unwrap() }} - // }}", - // operands[0], operands[0] - // )); - } - - Instruction::Bitcasts { casts } => { - wit_bindgen_rust_lib::bitcast(casts, operands, results) - } - - Instruction::I32FromBool => { - results.push(format!("(int32_t)({})", operands[0])); - } - Instruction::BoolFromI32 => { - results.push(format!("{}!=0", operands[0])); - } - - Instruction::FlagsLower { flags, .. } => { - let tmp = self.tmp(); - self.push_str(&format!("auto flags{} = {};\n", tmp, operands[0])); - for i in 0..flags.repr().count() { - results.push(format!("(flags{}.bits() >> {}) as i32", tmp, i * 32)); - } - } - Instruction::FlagsLift { flags, ty, .. } => { - let repr = RustFlagsRepr::new(flags); - let name = self.gen.type_path(*ty, true); - let mut result = format!("{name}::empty()"); - for (i, op) in operands.iter().enumerate() { - result.push_str(&format!( - " | {name}::from_bits_retain((({op} as {repr}) << {}) as _)", - i * 32 - )); - } - results.push(result); - } - - Instruction::HandleLower { - handle: Handle::Own(_), - .. - } => { - let op = &operands[0]; - results.push(format!("({op}).into_handle()")) - } - - Instruction::HandleLower { - handle: Handle::Borrow(_), - .. - } => { - let op = &operands[0]; - if op == "self" { - results.push("this->handle".into()); - } else { - results.push(format!("({op}).handle")); - } - } - - Instruction::HandleLift { handle, .. } => { - let op = &operands[0]; - let (prefix, resource, _owned) = match handle { - Handle::Borrow(resource) => ("&", resource, false), - Handle::Own(resource) => ("", resource, true), - }; - let resource = dealias(resolve, *resource); - - results.push( - if let Direction::Export = self.gen.gen.resources[&resource].direction { - match handle { - Handle::Borrow(_) => { - let name = resolve.types[resource] - .name - .as_deref() - .unwrap() - .to_upper_camel_case(); - format!( - "::core::mem::transmute::\ - ({op}.try_into().unwrap())" - ) - } - Handle::Own(_) => { - let name = self.gen.type_path(resource, true); - format!("{name}::from_handle({op})") - } - } - } else { - op.clone() - // let name = self.gen.type_path(resource, true); - // let world = self.gen.gen.world.map(|w| &resolve.worlds[w].name).unwrap(); - // format!("{prefix}{name}{{std::move({world}::{RESOURCE_BASE_CLASS_NAME}({op}))}}") - }, - ); - } - - Instruction::RecordLower { ty, record, .. } => { - self.record_lower(*ty, record, &operands[0], results); - } - Instruction::RecordLift { ty, record, .. } => { - let mut result = self.typename_lift(*ty); - result.push_str("{"); - for (_field, val) in record.fields.iter().zip(operands) { - // result.push_str(&to_rust_ident(&field.name)); - // result.push_str(":"); - result.push_str(&val); - result.push_str(", "); - } - result.push_str("}"); - results.push(result); - } - - Instruction::TupleLower { tuple, .. } => { - self.tuple_lower(tuple, &operands[0], results); - } - Instruction::TupleLift { .. } => { - self.tuple_lift(operands, results); - } - - Instruction::VariantPayloadName => results.push("e".to_string()), - - Instruction::VariantLower { - variant: _, - results: _, - ty, - .. - } => { - let name = self.typename_lower(*ty); - let op0 = &operands[0]; - self.push_str(&format!("({name}){op0}")); - } - - 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::>(); - 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)]"); - // } - 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); - } - - Instruction::OptionLower { - results: _result_types, - .. - } => { - todo!(); - // let some = self.blocks.pop().unwrap(); - // let none = self.blocks.pop().unwrap(); - // self.let_results(result_types.len(), results); - // let operand = &operands[0]; - // self.push_str(&format!( - // "match {operand} {{ - // Some(e) => {some}, - // None => {{\n{none}\n}}, - // }};" - // )); - } - - Instruction::OptionLift { .. } => { - let some = self.blocks.pop().unwrap(); - let none = self.blocks.pop().unwrap(); - assert_eq!(none, "()"); - let operand = &operands[0]; - results.push(format!( - "{operand}==1 ? std::optional<>(std::move({some})) : std::optional()" - )); - } - - Instruction::ResultLower { - results: _result_types, - // result, - .. - } => { - todo!(); - // let err = self.blocks.pop().unwrap(); - // let ok = self.blocks.pop().unwrap(); - // self.let_results(result_types.len(), results); - // let operand = &operands[0]; - // let ok_binding = if result.ok.is_some() { "e" } else { "_" }; - // let err_binding = if result.err.is_some() { "e" } else { "_" }; - // self.push_str(&format!( - // "match {operand} {{ - // Ok({ok_binding}) => {{ {ok} }}, - // Err({err_binding}) => {{ {err} }}, - // }};" - // )); - } - - Instruction::ResultLift { result, .. } => { - let mut err = self.blocks.pop().unwrap(); - let mut ok = self.blocks.pop().unwrap(); - if result.ok.is_none() { - ok.clear(); - } else { - ok = format!("std::move({ok})"); - } - if result.err.is_none() { - err.clear(); - } else { - err = format!("std::move({err})"); - } - let ok_type = print_to_result(self, resolve, |gen| { - gen.print_optional_ty(result.ok.as_ref(), TypeMode::Owned) - }); - let err_type = print_to_result(self, resolve, |gen| { - gen.print_optional_ty(result.err.as_ref(), TypeMode::Owned) - }); - let type_name = format!("std::expected<{ok_type}, {err_type}>",); - let err_type = "std::unexpected"; - let operand = &operands[0]; - results.push(format!( - "{operand}==0 \n? {type_name}({ok}) \n: {type_name}({err_type}({err}))" - )); - } - - Instruction::EnumLower { enum_: _, ty, .. } => { - let name = self.typename_lower(*ty); - let op0 = &operands[0]; - let result = format!("({name}){op0}"); - results.push(result); - } - - Instruction::EnumLift { - enum_: _, - ty: _, - name, - } => { - results.push(format!("({name}){}", &operands[0])); - } - - Instruction::ListCanonLower { realloc, .. } => { - let tmp = self.tmp(); - let val = format!("vec{}", tmp); - let ptr = format!("ptr{}", tmp); - let len = format!("len{}", tmp); - // if realloc.is_none() { - self.push_str(&format!("auto& {} = {};\n", val, operands[0])); - // } else { - // let op0 = operands.pop().unwrap(); - // self.push_str(&format!("auto {} = ({}).into_boxed_slice();\n", val, op0)); - // } - self.push_str(&format!("auto {} = (int32_t)({}.data());\n", ptr, val)); - self.push_str(&format!("auto {} = (int32_t)({}.size());\n", len, val)); - if realloc.is_some() { - todo!(); - // self.push_str(&format!("::core::mem::forget({});\n", val)); - } - results.push(ptr); - results.push(len); - } - - Instruction::ListCanonLift { .. } => { - let tmp = self.tmp(); - let len = format!("len{}", tmp); - self.push_str(&format!("auto {} = {};\n", len, operands[1])); - let result = format!("std::vector((?*)({}), {len})", operands[0]); - results.push(result); - } - - Instruction::StringLower { realloc } => { - let tmp = self.tmp(); - let val = format!("vec{}", tmp); - let ptr = format!("ptr{}", tmp); - let len = format!("len{}", tmp); - if realloc.is_none() { - self.push_str(&format!("auto {} = {};\n", val, operands[0])); - } else { - todo!(); - // let op0 = format!("{}.into_bytes()", operands[0]); - // self.push_str(&format!("let {} = ({}).into_boxed_slice();\n", val, op0)); - } - self.push_str(&format!("auto {} = (int32_t)({}.data());\n", ptr, val)); - self.push_str(&format!("auto {} = (int32_t)({}.size());\n", len, val)); - if realloc.is_some() { - todo!(); - // self.push_str(&format!("::core::mem::forget({});\n", val)); - } - results.push(ptr); - results.push(len); - } - - Instruction::StringLift => { - let tmp = self.tmp(); - let len = format!("len{}", tmp); - self.push_str(&format!("auto {} = {};\n", len, operands[1])); - let result = format!("std::string((char const*)({}), {len})", operands[0]); - results.push(result); - } - - Instruction::ListLower { element, realloc } => { - let body = self.blocks.pop().unwrap(); - let tmp = self.tmp(); - let vec = format!("vec{tmp}"); - let result = format!("result{tmp}"); - let layout = format!("layout{tmp}"); - let len = format!("len{tmp}"); - self.push_str(&format!( - "let {vec} = {operand0};\n", - operand0 = operands[0] - )); - self.push_str(&format!("let {len} = {vec}.len() as i32;\n")); - let size = self.gen.sizes.size(element); - let align = self.gen.sizes.align(element); - self.push_str(&format!( - "let {layout} = alloc::Layout::from_size_align_unchecked({vec}.len() * {size}, {align});\n", - )); - self.push_str(&format!( - "let {result} = if {layout}.size() != 0\n{{\nlet ptr = alloc::alloc({layout});\n", - )); - self.push_str(&format!( - "if ptr.is_null()\n{{\nalloc::handle_alloc_error({layout});\n}}\nptr\n}}", - )); - self.push_str(&format!("else {{\n::core::ptr::null_mut()\n}};\n",)); - self.push_str(&format!("for (i, e) in {vec}.into_iter().enumerate() {{\n",)); - self.push_str(&format!( - "let base = {result} as i32 + (i as i32) * {size};\n", - )); - self.push_str(&body); - self.push_str("}\n"); - results.push(format!("{result} as i32")); - results.push(len); - - if realloc.is_none() { - // If an allocator isn't requested then we must clean up the - // allocation ourselves since our callee isn't taking - // ownership. - self.cleanup.push((result, layout)); - } - } - - 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 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] - )); - let elemtype = - print_to_result(self, resolve, |gen| gen.print_ty(element, TypeMode::Owned)); - self.push_str(&format!("auto {result} = std::vector<{elemtype}>();\n")); - self.push_str(&format!("{result}.reserve({len});\n")); - self.push_str(&format!("for (unsigned i=0;i<{len};++i) {{\n")); - self.push_str(&format!("auto base = {base} + i * {size};\n")); - self.push_str(&format!("{result}.push_back({body});\n")); - self.push_str("}\n"); - results.push(result); - self.push_str(&format!("free((void*){base});\n")); - } - - Instruction::IterElem { .. } => results.push("e".to_string()), - - Instruction::IterBasePointer => results.push("base".to_string()), - - Instruction::CallWasm { name, sig, .. } => { - let func = self.declare_import( - self.gen.wasm_import_module.unwrap(), - name, - &sig.params, - &sig.results, - ); - - // ... then call the function with all our operands - if sig.results.len() > 0 { - self.push_str("auto ret = "); - results.push("ret".to_string()); - } - self.push_str(&func); - self.push_str("("); - self.push_str(&operands.join(", ")); - self.push_str(");\n"); - } - - Instruction::CallInterface { func, .. } => { - self.let_results(func.results.len(), results); - match &func.kind { - FunctionKind::Freestanding => { - self.push_str(&format!( - "<{0}Impl as {0}>::{1}", - self.trait_name.unwrap(), - to_rust_ident(&func.name) - )); - } - FunctionKind::Method(ty) | FunctionKind::Static(ty) => { - self.push_str(&format!( - "::{1}", - resolve.types[*ty] - .name - .as_deref() - .unwrap() - .to_upper_camel_case(), - to_rust_ident(func.item_name()) - )); - } - FunctionKind::Constructor(ty) => { - self.push_str(&format!( - "Own{0}::new(::new", - resolve.types[*ty] - .name - .as_deref() - .unwrap() - .to_upper_camel_case() - )); - } - } - self.push_str("("); - self.push_str(&operands.join(", ")); - self.push_str(")"); - if let FunctionKind::Constructor(_) = &func.kind { - self.push_str(")"); - } - self.push_str(";\n"); - } - - Instruction::Return { amt, func, .. } => { - self.emit_cleanup(); - match amt { - 0 => {} - 1 => { - match &func.kind { - FunctionKind::Constructor(_) => { - // strange but works - self.push_str("this->handle = "); - } - _ => self.push_str("return "), - } - self.push_str(&operands[0]); - self.push_str(";\n"); - } - _ => todo!(), - } - } - - Instruction::I32Load { offset } => { - results.push(format!("*((int32_t const*)({} + {}))", operands[0], offset)); - } - Instruction::I32Load8U { offset } => { - results.push(format!( - "(int32_t)(*((uint8_t const*)({} + {})))", - operands[0], offset - )); - } - Instruction::I32Load8S { offset } => { - results.push(format!( - "(int32_t)(*((int8_t const*)({} + {})))", - operands[0], offset - )); - } - Instruction::I32Load16U { offset } => { - results.push(format!( - "(int32_t)(*((uint16_t const*)({} + {})))", - operands[0], offset - )); - } - Instruction::I32Load16S { offset } => { - results.push(format!( - "(int32_t)(*((int16_t const*)({} + {})))", - operands[0], offset - )); - } - Instruction::I64Load { offset } => { - results.push(format!("*((int64_t const*)({} + {}))", operands[0], offset)); - } - Instruction::F32Load { offset } => { - results.push(format!("*((float const*)({} + {}))", operands[0], offset)); - } - Instruction::F64Load { offset } => { - results.push(format!("*((double const*)({} + {}))", operands[0], offset)); - } - Instruction::I32Store { offset } => { - self.push_str(&format!( - "*((int32_t*)({} + {})) = {};\n", - operands[1], offset, operands[0] - )); - } - Instruction::I32Store8 { offset } => { - self.push_str(&format!( - "*((int8_t*)({} + {})) = int8_t({});\n", - operands[1], offset, operands[0] - )); - } - Instruction::I32Store16 { offset } => { - self.push_str(&format!( - "*((uint16_t*)({} + {})) = uint16_t({});\n", - operands[1], offset, operands[0] - )); - } - Instruction::I64Store { offset } => { - self.push_str(&format!( - "*((int64_t*)({} + {})) = {};\n", - operands[1], offset, operands[0] - )); - } - Instruction::F32Store { offset } => { - self.push_str(&format!( - "*((float*)({} + {})) = {};\n", - operands[1], offset, operands[0] - )); - } - Instruction::F64Store { offset } => { - self.push_str(&format!( - "*((double*)({} + {})) = {};\n", - operands[1], offset, operands[0] - )); - } - - Instruction::Malloc { .. } => unimplemented!(), - - Instruction::GuestDeallocate { size, align } => { - self.push_str(&format!( - "wit_bindgen::rt::dealloc({}, {}, {});\n", - operands[0], size, align - )); - } - - Instruction::GuestDeallocateString => { - self.push_str(&format!( - "wit_bindgen::rt::dealloc({}, ({}) as usize, 1);\n", - operands[0], operands[1], - )); - } - - Instruction::GuestDeallocateVariant { blocks } => { - let max = blocks - 1; - let blocks = self - .blocks - .drain(self.blocks.len() - blocks..) - .collect::>(); - let op0 = &operands[0]; - self.src.push_str(&format!("match {op0} {{\n")); - for (i, block) in blocks.into_iter().enumerate() { - let pat = if i == max { - String::from("_") - } else { - i.to_string() - }; - self.src.push_str(&format!("{pat} => {block},\n")); - } - self.src.push_str("}\n"); - } - - Instruction::GuestDeallocateList { 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 len = format!("len{tmp}"); - let base = format!("base{tmp}"); - self.push_str(&format!( - "let {base} = {operand0};\n", - operand0 = operands[0] - )); - self.push_str(&format!( - "let {len} = {operand1};\n", - operand1 = operands[1] - )); - - if body != "()" { - self.push_str("for i in 0.."); - self.push_str(&len); - self.push_str(" {\n"); - self.push_str("let base = "); - self.push_str(&base); - self.push_str(" + i *"); - self.push_str(&size.to_string()); - self.push_str(";\n"); - self.push_str(&body); - self.push_str("\n}\n"); - } - self.push_str(&format!( - "wit_bindgen::rt::dealloc({base}, ({len} as usize) * {size}, {align});\n", - )); - } - } - } -} - -fn group_by_resource<'a>( - funcs: impl Iterator, -) -> BTreeMap, Vec<&'a Function>> { - let mut by_resource = BTreeMap::<_, Vec<_>>::new(); - for func in funcs { - match &func.kind { - FunctionKind::Freestanding => by_resource.entry(None).or_default().push(func), - FunctionKind::Method(ty) | FunctionKind::Static(ty) | FunctionKind::Constructor(ty) => { - by_resource.entry(Some(*ty)).or_default().push(func); - } - } - } - by_resource -} - -fn to_rust_ident(name: &str) -> String { - match name { - // Escape C++ keywords. - // Source: https://doc.rust-lang.org/reference/keywords.html - "this" => "this_".into(), - _ => wit_bindgen_c::to_c_ident(name), - } -} diff --git a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp index 547714e0f..0ac6756d3 100644 --- a/crates/cpp/tests/meshless_strings/component_a/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_a/the_world.cpp @@ -69,10 +69,8 @@ extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void a_fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { auto len0 = arg1; - auto string0 = - std::string_view((char const *)(arg0), len0); - - comp_a::exports::foo::foo::strings::A(string0); + 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) { @@ -90,16 +88,11 @@ a_fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3 uint8_t *arg4) { auto len0 = arg1; - auto string0 = - std::string_view((char const *)(arg0), len0); - auto len1 = arg3; - auto string1 = - std::string_view((char const *)(arg2), len1); - - auto result2 = comp_a::exports::foo::foo::strings::C(string0, - string1); + 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()); From e9d9c4ee309fa13aeeec544db0d1f270cc32fdd9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 28 Aug 2024 23:53:30 +0200 Subject: [PATCH 317/672] regenerate --- .../component_b/the_world.cpp | 6 ++-- .../component_b/the_world_cpp.h | 32 ++++++++++++++++++- 2 files changed, 33 insertions(+), 5 deletions(-) mode change 120000 => 100644 crates/cpp/tests/meshless_strings/component_b/the_world_cpp.h diff --git a/crates/cpp/tests/meshless_strings/component_b/the_world.cpp b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp index b3c848c71..c33d57120 100644 --- a/crates/cpp/tests/meshless_strings/component_b/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp @@ -47,8 +47,7 @@ wit::string foo::foo::strings::B() { a_fooX3AfooX2FstringsX00b(ptr0); auto len1 = *((size_t *)(ptr0 + sizeof(void *))); - auto string1 = wit::string((char const *)(*((uint8_t **)(ptr0 + 0))), len1); - return string1; + 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; @@ -63,8 +62,7 @@ wit::string foo::foo::strings::C(std::string_view a, std::string_view b) { a_fooX3AfooX2FstringsX00c(ptr0, len0, ptr1, len1, ptr2); auto len3 = *((size_t *)(ptr2 + sizeof(void *))); - auto string3 = wit::string((char const *)(*((uint8_t **)(ptr2 + 0))), len3); - return string3; + 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) { 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 deleted file mode 120000 index b8347c33c..000000000 --- a/crates/cpp/tests/meshless_strings/component_b/the_world_cpp.h +++ /dev/null @@ -1 +0,0 @@ -../../native_strings/the_world_cpp.h \ No newline at end of file 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..4a5792d27 --- /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 +#include +#include +#include +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 From 5f0ec6c681c5ab93ce7f561075d062a8ef6da83e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 29 Aug 2024 00:00:40 +0200 Subject: [PATCH 318/672] symmetric API --- .../tests/meshless_strings/component_b/Makefile | 2 +- .../tests/meshless_strings/component_b/guest.cpp | 14 +++++++++++++- .../meshless_strings/component_b/the_world.cpp | 16 ++++------------ .../meshless_strings/component_b/the_world_cpp.h | 4 ++-- 4 files changed, 20 insertions(+), 16 deletions(-) mode change 120000 => 100644 crates/cpp/tests/meshless_strings/component_b/guest.cpp diff --git a/crates/cpp/tests/meshless_strings/component_b/Makefile b/crates/cpp/tests/meshless_strings/component_b/Makefile index 9d9dea504..917578e1e 100644 --- a/crates/cpp/tests/meshless_strings/component_b/Makefile +++ b/crates/cpp/tests/meshless_strings/component_b/Makefile @@ -6,7 +6,7 @@ libcomponent_b.a: the_world.o guest.o ar rcvs $@ $^ bindgen: - ../../../../../target/debug/wit-bindgen cpp ../wit --symmetric --wasm64 --format + ../../../../../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 deleted file mode 120000 index f9b4d9b20..000000000 --- a/crates/cpp/tests/meshless_strings/component_b/guest.cpp +++ /dev/null @@ -1 +0,0 @@ -../../native_strings/guest.cpp \ No newline at end of file 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 index c33d57120..713810a61 100644 --- a/crates/cpp/tests/meshless_strings/component_b/the_world.cpp +++ b/crates/cpp/tests/meshless_strings/component_b/the_world.cpp @@ -68,10 +68,7 @@ extern "C" __attribute__((__export_name__("foo:foo/strings#a"))) void fooX3AfooX2FstringsX00a(uint8_t *arg0, size_t arg1) { auto len0 = arg1; - auto string0 = - wit::string::from_view(std::string_view((char const *)(arg0), len0)); - - exports::foo::foo::strings::A(std::move(string0)); + 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) { @@ -89,16 +86,11 @@ fooX3AfooX2FstringsX00c(uint8_t *arg0, size_t arg1, uint8_t *arg2, size_t arg3, uint8_t *arg4) { auto len0 = arg1; - auto string0 = - wit::string::from_view(std::string_view((char const *)(arg0), len0)); - auto len1 = arg3; - auto string1 = - wit::string::from_view(std::string_view((char const *)(arg2), len1)); - - auto result2 = - exports::foo::foo::strings::C(std::move(string0), std::move(string1)); + 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()); 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 index 4a5792d27..718f3ddfd 100644 --- a/crates/cpp/tests/meshless_strings/component_b/the_world_cpp.h +++ b/crates/cpp/tests/meshless_strings/component_b/the_world_cpp.h @@ -20,9 +20,9 @@ namespace exports { namespace foo { namespace foo { namespace strings { -void A(wit::string &&x); +void A(std::string_view x); wit::string B(); -wit::string C(wit::string &&a, wit::string &&b); +wit::string C(std::string_view a, std::string_view b); } // namespace strings } // namespace foo } // namespace foo From 3d79f546f2db513c2def981fc2570cb913b5e5d6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 31 Aug 2024 14:58:13 +0200 Subject: [PATCH 319/672] Initial (failing) symmetric smoke test --- Cargo.lock | 14 +-- crates/cpp/src/lib.rs | 8 +- crates/cpp/tests/symmetric.rs | 141 ++++++++++++++++++++++ crates/cpp/tests/symmetric_tests/smoke.rs | 25 ++++ crates/guest-rust/macro/src/lib.rs | 29 ++++- crates/rust/src/lib.rs | 4 + 6 files changed, 210 insertions(+), 11 deletions(-) create mode 100644 crates/cpp/tests/symmetric.rs create mode 100644 crates/cpp/tests/symmetric_tests/smoke.rs diff --git a/Cargo.lock b/Cargo.lock index bfab67b88..6e1270e9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1729,7 +1729,7 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0280d4131316fff27a42d928360190c1a855e31b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b1cba2792095d69370f2e94484f278e0d92e5ead" dependencies = [ "leb128", "wasmparser 0.216.0", @@ -1738,7 +1738,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0280d4131316fff27a42d928360190c1a855e31b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b1cba2792095d69370f2e94484f278e0d92e5ead" dependencies = [ "anyhow", "indexmap", @@ -1767,7 +1767,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0280d4131316fff27a42d928360190c1a855e31b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b1cba2792095d69370f2e94484f278e0d92e5ead" dependencies = [ "ahash", "bitflags", @@ -2100,7 +2100,7 @@ dependencies = [ [[package]] name = "wast" version = "216.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0280d4131316fff27a42d928360190c1a855e31b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b1cba2792095d69370f2e94484f278e0d92e5ead" dependencies = [ "bumpalo", "leb128", @@ -2121,7 +2121,7 @@ dependencies = [ [[package]] name = "wat" version = "1.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0280d4131316fff27a42d928360190c1a855e31b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b1cba2792095d69370f2e94484f278e0d92e5ead" dependencies = [ "wast 216.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] @@ -2519,7 +2519,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0280d4131316fff27a42d928360190c1a855e31b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b1cba2792095d69370f2e94484f278e0d92e5ead" dependencies = [ "anyhow", "bitflags", @@ -2556,7 +2556,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0280d4131316fff27a42d928360190c1a855e31b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b1cba2792095d69370f2e94484f278e0d92e5ead" dependencies = [ "anyhow", "id-arena", diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index c32af2076..04232b156 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2799,7 +2799,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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 + 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]); @@ -2811,7 +2812,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } 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)) { + } else if self.gen.gen.opts.short_cut + || (self.gen.gen.opts.new_api + && matches!(self.variant, AbiVariant::GuestExport)) + { format!("std::string_view((char const*)({}), {len})", operands[0]) } else { format!("wit::string((char const*)({}), {len})", operands[0]) diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs new file mode 100644 index 000000000..c3becac1c --- /dev/null +++ b/crates/cpp/tests/symmetric.rs @@ -0,0 +1,141 @@ +use std::{ + fs::{self, File}, + io::{self, Write}, + path::PathBuf, + process::Command, +}; + +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 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:?}"); + + 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\ + \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())?; + 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); + drop(testee_cargo); + + let tester_cargo = format!( + "[workspace]\n\ + members = [ \"rust\" ]\n\ + resolver = \"2\"\n\ + \n\ + [package]\n\ + name = \"tester\"\n\ + publish = false\n\ + edition = \"2021\"\n\ + \n\ + [dependencies]\n\ + wit-bindgen = {{ path = \"{toplevel}/crates/guest-rust\" }}\n\ + {dir_name} = {{ path = \"rust\" }}\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(); + filename.push("src"); + std::fs::create_dir(&filename)?; + filename.push(format!("main.rs")); + let mut original = tester_source_dir.clone(); + original.push(&format!("{dir_name}.rs")); + std::os::unix::fs::symlink(original, &filename); + + let mut cmd = Command::new("cargo"); + cmd.arg("build") + .current_dir(testee_dir) + .env("SYMMETRIC_ABI", "1") + .env("WIT_BINDGEN_DEBUG", "1"); + let status = cmd.status().unwrap(); + assert!(status.success()); + + Ok(()) +} + +#[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"); + + let mut 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 !fs::exists(&test_link)? { + 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"); + + tests( + "smoke", + &out_dir, + &toplevel, + &source_files, + &tester_source_dir, + )?; + Ok(()) +} diff --git a/crates/cpp/tests/symmetric_tests/smoke.rs b/crates/cpp/tests/symmetric_tests/smoke.rs new file mode 100644 index 000000000..0cec6539e --- /dev/null +++ b/crates/cpp/tests/symmetric_tests/smoke.rs @@ -0,0 +1,25 @@ +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)); +} diff --git a/crates/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs index 31c7d92b7..644c6938d 100644 --- a/crates/guest-rust/macro/src/lib.rs +++ b/crates/guest-rust/macro/src/lib.rs @@ -156,6 +156,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(); + } } } } else { @@ -166,10 +172,13 @@ impl Parse for Config { )])); } } - let (resolve, pkgs, files) = + let (mut resolve, pkgs, files) = parse_source(&source, &features).map_err(|err| anyhow_to_syn(call_site, err))?; let world = select_world(&resolve, &pkgs, world.as_deref()) .map_err(|e| anyhow_to_syn(call_site, e))?; + if opts.invert_direction { + resolve.invert_direction(world); + } Ok(Config { opts, resolve, @@ -260,8 +269,12 @@ fn parse_source( } impl Config { - fn expand(self) -> Result { + fn expand(mut self) -> Result { 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 .generate(&self.resolve, self.world, &mut files) @@ -335,6 +348,8 @@ mod kw { syn::custom_keyword!(imports); syn::custom_keyword!(debug); syn::custom_keyword!(disable_custom_section_link_helpers); + syn::custom_keyword!(symmetric); + syn::custom_keyword!(invert_direction); } #[derive(Clone)] @@ -396,6 +411,8 @@ enum Opt { Async(AsyncConfig, Span), Debug(syn::LitBool), DisableCustomSectionLinkHelpers(syn::LitBool), + Symmetric(syn::LitBool), + InvertDirection(syn::LitBool), } impl Parse for Opt { @@ -574,6 +591,14 @@ impl Parse for Opt { input.parse::()?; input.parse::()?; Ok(Opt::DisableCustomSectionLinkHelpers(input.parse()?)) + } else if l.peek(kw::symmetric) { + input.parse::()?; + input.parse::()?; + Ok(Opt::Symmetric(input.parse()?)) + } else if l.peek(kw::invert_direction) { + input.parse::()?; + input.parse::()?; + Ok(Opt::InvertDirection(input.parse()?)) } else { Err(l.error()) } diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index d9207aa51..4123b592c 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -267,6 +267,10 @@ pub struct Opts { #[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, + /// Determines which functions to lift or lower `async`, if any. #[cfg_attr(feature = "clap", arg(long = "async", value_parser = parse_async, default_value_t = Default::default()))] pub async_: AsyncConfig, From e724874f7ea398858a23f4cd4acfad33380a75c6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 31 Aug 2024 16:29:23 +0200 Subject: [PATCH 320/672] correct root function export/import for smoke test in symmetric --- crates/core/src/lib.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 50bc16267..3dd999703 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -485,12 +485,16 @@ pub fn make_external_component(input: &str) -> String { /// encode symbol as alphanumeric by hex-encoding special characters pub fn make_external_symbol(module_name: &str, name: &str, variant: abi::AbiVariant) -> String { - let mut res = make_external_component(module_name); - res.push_str(if matches!(variant, abi::AbiVariant::GuestExport) { - "X23" // Hash character + if module_name.is_empty() || module_name=="$root" { + make_external_component(name) } else { - "X00" // NUL character (some tools use '.' for display) - }); - res.push_str(&make_external_component(name)); - res + 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 + } } From 461093597eb69db71baad1f689c73afa765c27ab Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 31 Aug 2024 16:50:01 +0200 Subject: [PATCH 321/672] force linking, needs RUSTFLAGS as well --- crates/cpp/tests/symmetric_tests/smoke.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/cpp/tests/symmetric_tests/smoke.rs b/crates/cpp/tests/symmetric_tests/smoke.rs index 0cec6539e..1cf3e06d2 100644 --- a/crates/cpp/tests/symmetric_tests/smoke.rs +++ b/crates/cpp/tests/symmetric_tests/smoke.rs @@ -22,4 +22,13 @@ impl exports::test::smoke::imports::Guest for MyExports { pub fn main() { thunk(); assert!(HIT.load(std::sync::atomic::Ordering::SeqCst)); + { + #[link(name = "smoke")] + extern "C" { + fn thunk(); + } + let _ = || { + unsafe { thunk() }; + }; + } } From 05da87b6b48059c8b96cf4aa58fb57456f640917 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 31 Aug 2024 22:04:53 +0200 Subject: [PATCH 322/672] fully operational symmetric smoke test --- crates/core/src/lib.rs | 2 +- crates/cpp/tests/symmetric.rs | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 3dd999703..ff741763f 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -485,7 +485,7 @@ pub fn make_external_component(input: &str) -> String { /// 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" { + if module_name.is_empty() || module_name == "$root" { make_external_component(name) } else { let mut res = make_external_component(module_name); diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index c3becac1c..466b6b20c 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -92,6 +92,16 @@ fn tests( 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(); From b9ede9515082e4c302d4fe9e6e90a6e1858fc967 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 31 Aug 2024 23:03:45 +0200 Subject: [PATCH 323/672] symmetric c++ test --- crates/cpp/tests/symmetric.rs | 85 ++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index 466b6b20c..8d076b6e5 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -5,6 +5,8 @@ use std::{ process::Command, }; +use wit_bindgen_core::wit_parser::{Resolve, WorldId}; + fn tests( dir_name: &str, out_dir: &PathBuf, @@ -100,16 +102,97 @@ fn tests( let mut cmd = Command::new("cargo"); cmd.arg("run") - .current_dir(out_dir) + .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 (resolve, 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; + opts.build().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(out_dir.join(format!("{snake}_component_type.o"))) + .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.push("target"); + tester.push("debug"); + tester.push("tester"); + 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()?; From 96887710f903c4c807646b722187085bef5e801d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 31 Aug 2024 23:13:41 +0200 Subject: [PATCH 324/672] report more errors --- crates/cpp/tests/symmetric.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index 8d076b6e5..c55a7e8eb 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -15,6 +15,13 @@ fn tests( tester_source_dir: &PathBuf, ) -> io::Result<()> { // modelled after wit-bindgen/tests/runtime/main.rs + let mut tester_source_file = tester_source_dir.clone(); + tester_source_file.push(&format!("{dir_name}.rs")); + if !std::fs::exists(&tester_source_file) { + println!("Skipping {}", dir_name); + return Ok(()) + } + let mut dir = source_files.clone(); dir.push(dir_name); @@ -61,7 +68,7 @@ fn tests( filename.push(format!("lib.rs")); let mut original = dir.clone(); original.push("wasm.rs"); - std::os::unix::fs::symlink(original, filename); + std::os::unix::fs::symlink(original, filename)?; drop(testee_cargo); let tester_cargo = format!( @@ -87,9 +94,7 @@ fn tests( filename.push("src"); std::fs::create_dir(&filename)?; filename.push(format!("main.rs")); - let mut original = tester_source_dir.clone(); - original.push(&format!("{dir_name}.rs")); - std::os::unix::fs::symlink(original, &filename); + std::os::unix::fs::symlink(tester_source_file, &filename)?; let mut cmd = Command::new("cargo"); cmd.arg("build") @@ -201,7 +206,7 @@ fn symmetric_integration() -> io::Result<()> { out_dir.pop(); out_dir.push("symmetric-tests"); - let mut manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut toplevel = manifest_dir.clone(); toplevel.pop(); @@ -212,7 +217,7 @@ fn symmetric_integration() -> io::Result<()> { if !fs::exists(&test_link)? { let mut original = toplevel.clone(); original.push("tests"); - std::os::unix::fs::symlink(original, &test_link); + std::os::unix::fs::symlink(original, &test_link)?; } let mut source_files = toplevel.clone(); From a4c7bd2771bcc3f66107858672df6a26288e79d1 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 31 Aug 2024 23:55:55 +0200 Subject: [PATCH 325/672] failing strings example --- crates/cpp/tests/symmetric.rs | 23 ++++++++----- crates/cpp/tests/symmetric_tests/strings.rs | 38 +++++++++++++++++++++ tests/runtime/strings/wasm.rs | 25 ++++++++++++++ 3 files changed, 77 insertions(+), 9 deletions(-) create mode 100644 crates/cpp/tests/symmetric_tests/strings.rs create mode 100644 tests/runtime/strings/wasm.rs diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index c55a7e8eb..6ee66e807 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -17,9 +17,9 @@ fn tests( // modelled after wit-bindgen/tests/runtime/main.rs let mut tester_source_file = tester_source_dir.clone(); tester_source_file.push(&format!("{dir_name}.rs")); - if !std::fs::exists(&tester_source_file) { + if !std::fs::exists(&tester_source_file)? { println!("Skipping {}", dir_name); - return Ok(()) + return Ok(()); } let mut dir = source_files.clone(); @@ -228,12 +228,17 @@ fn symmetric_integration() -> io::Result<()> { tester_source_dir.push("tests"); tester_source_dir.push("symmetric_tests"); - tests( - "smoke", - &out_dir, - &toplevel, - &source_files, - &tester_source_dir, - )?; + let testcases = vec!["smoke", "strings", "numbers"]; + + for dir_name in testcases { + tests( + dir_name, + &out_dir, + &toplevel, + &source_files, + &tester_source_dir, + )?; + } + Ok(()) } diff --git a/crates/cpp/tests/symmetric_tests/strings.rs b/crates/cpp/tests/symmetric_tests/strings.rs new file mode 100644 index 000000000..ae5aa5e4d --- /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(); + } + let _ = || { + unsafe { roundtrip() }; + }; + } +} 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 + } +} From 068eb7c6a2f08743ba8a5747acea82704efc634f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 1 Sep 2024 00:14:36 +0200 Subject: [PATCH 326/672] numbers test --- crates/cpp/tests/symmetric_tests/numbers.rs | 131 ++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 crates/cpp/tests/symmetric_tests/numbers.rs 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() }; + }; + } +} From ca5b35778a1d5ce6b1336e841c3b3c142e3801bb Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 1 Sep 2024 13:55:38 +0200 Subject: [PATCH 327/672] fix lifting of empty strings (symmetric) --- crates/rust/src/bindgen.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index d86497504..32009416d 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -771,11 +771,22 @@ impl Bindgen for FunctionBindgen<'_, '_> { ); results.push(format!("string{tmp}")); } else { - uwriteln!( - self.src, - "let bytes{tmp} = {vec}::from_raw_parts({}.cast(), {len}, {len});", - operands[0], - ); + if self.gen.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.gen.gen.opts.raw_strings { results.push(format!("bytes{tmp}")); } else { From 7fde68112a32173ad7f5b54963e913709d7b4ead Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 1 Sep 2024 14:15:59 +0200 Subject: [PATCH 328/672] fix warning in test case --- Cargo.lock | 14 +++++++------- crates/cpp/tests/symmetric_tests/strings.rs | 4 ++-- crates/rust/src/interface.rs | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6e1270e9a..181e9fb6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1729,7 +1729,7 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b1cba2792095d69370f2e94484f278e0d92e5ead" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#41f1598243d03aed203c6d7b3252b1282fc5ab1c" dependencies = [ "leb128", "wasmparser 0.216.0", @@ -1738,7 +1738,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b1cba2792095d69370f2e94484f278e0d92e5ead" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#41f1598243d03aed203c6d7b3252b1282fc5ab1c" dependencies = [ "anyhow", "indexmap", @@ -1767,7 +1767,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b1cba2792095d69370f2e94484f278e0d92e5ead" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#41f1598243d03aed203c6d7b3252b1282fc5ab1c" dependencies = [ "ahash", "bitflags", @@ -2100,7 +2100,7 @@ dependencies = [ [[package]] name = "wast" version = "216.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b1cba2792095d69370f2e94484f278e0d92e5ead" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#41f1598243d03aed203c6d7b3252b1282fc5ab1c" dependencies = [ "bumpalo", "leb128", @@ -2121,7 +2121,7 @@ dependencies = [ [[package]] name = "wat" version = "1.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b1cba2792095d69370f2e94484f278e0d92e5ead" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#41f1598243d03aed203c6d7b3252b1282fc5ab1c" dependencies = [ "wast 216.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] @@ -2519,7 +2519,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b1cba2792095d69370f2e94484f278e0d92e5ead" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#41f1598243d03aed203c6d7b3252b1282fc5ab1c" dependencies = [ "anyhow", "bitflags", @@ -2556,7 +2556,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b1cba2792095d69370f2e94484f278e0d92e5ead" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#41f1598243d03aed203c6d7b3252b1282fc5ab1c" dependencies = [ "anyhow", "id-arena", diff --git a/crates/cpp/tests/symmetric_tests/strings.rs b/crates/cpp/tests/symmetric_tests/strings.rs index ae5aa5e4d..74b49861c 100644 --- a/crates/cpp/tests/symmetric_tests/strings.rs +++ b/crates/cpp/tests/symmetric_tests/strings.rs @@ -29,10 +29,10 @@ pub fn main() { { #[link(name = "strings")] extern "C" { - fn roundtrip(); + fn roundtrip(_: *mut u8, _: usize, _: *mut u8); } let _ = || { - unsafe { roundtrip() }; + unsafe { roundtrip(core::ptr::null_mut(), 0, core::ptr::null_mut()) }; }; } } diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 762ae2d18..36102c88e 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -456,7 +456,7 @@ macro_rules! {macro_name} {{ "struct _RetArea([::core::mem::MaybeUninit::; {size}]); static mut _RET_AREA: _RetArea = _RetArea([::core::mem::MaybeUninit::uninit(); {size}]); ", - size = self.return_pointer_area_size.format(POINTER_SIZE_EXPRESSION), + size = self.return_pointer_area_size.format_term(POINTER_SIZE_EXPRESSION, true), ); } @@ -923,7 +923,7 @@ impl {async_support}::StreamPayload for {name} {{ self.src, "struct RetArea([::core::mem::MaybeUninit::; {import_return_pointer_area_size}]); let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); {import_return_pointer_area_size}]); -", import_return_pointer_area_size = import_return_pointer_area_size.format(POINTER_SIZE_EXPRESSION) +", import_return_pointer_area_size = import_return_pointer_area_size.format_term(POINTER_SIZE_EXPRESSION, true) ); } self.src.push_str(&String::from(src)); From 84286db939ddc7be6aeb7e99b5012964eb436d76 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 1 Sep 2024 16:13:13 +0200 Subject: [PATCH 329/672] lists symmetric test --- crates/cpp/tests/symmetric.rs | 3 +- crates/cpp/tests/symmetric_tests/lists.rs | 134 ++++++++++++++++++ .../symmetric_tests/test-rust-wasm/Cargo.toml | 6 + .../symmetric_tests/test-rust-wasm/src/lib.rs | 1 + crates/rust/src/bindgen.rs | 38 ++--- 5 files changed, 162 insertions(+), 20 deletions(-) create mode 100644 crates/cpp/tests/symmetric_tests/lists.rs create mode 100644 crates/cpp/tests/symmetric_tests/test-rust-wasm/Cargo.toml create mode 120000 crates/cpp/tests/symmetric_tests/test-rust-wasm/src/lib.rs diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index 6ee66e807..5b7af9e85 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -53,6 +53,7 @@ fn tests( \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\ \n\ [lib]\n\ crate-type = [\"cdylib\"]\n\ @@ -228,7 +229,7 @@ fn symmetric_integration() -> io::Result<()> { tester_source_dir.push("tests"); tester_source_dir.push("symmetric_tests"); - let testcases = vec!["smoke", "strings", "numbers"]; + let testcases = vec!["smoke", "strings", "numbers", "lists"]; for dir_name in testcases { tests( 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) { + assert!(a.is_empty()); + } + + fn empty_string_param(a: String) { + assert_eq!(a, ""); + } + + fn empty_list_result() -> Vec { + Vec::new() + } + + fn empty_string_result() -> String { + String::new() + } + + fn list_param(list: Vec) { + assert_eq!(list, [1, 2, 3, 4]); + } + + fn list_param2(ptr: String) { + assert_eq!(ptr, "foo"); + } + + fn list_param3(ptr: Vec) { + 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>) { + 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 { + vec![1, 2, 3, 4, 5] + } + + fn list_result2() -> String { + "hello!".to_string() + } + + fn list_result3() -> Vec { + vec!["hello,".to_string(), "world!".to_string()] + } + + fn list_roundtrip(list: Vec) -> Vec { + list.to_vec() + } + + fn string_roundtrip(s: String) -> String { + s.to_string() + } + + fn list_minmax8(u: Vec, s: Vec) -> (Vec, Vec) { + assert_eq!(u, [u8::MIN, u8::MAX]); + assert_eq!(s, [i8::MIN, i8::MAX]); + (u, s) + } + + fn list_minmax16(u: Vec, s: Vec) -> (Vec, Vec) { + assert_eq!(u, [u16::MIN, u16::MAX]); + assert_eq!(s, [i16::MIN, i16::MAX]); + (u, s) + } + + fn list_minmax32(u: Vec, s: Vec) -> (Vec, Vec) { + assert_eq!(u, [u32::MIN, u32::MAX]); + assert_eq!(s, [i32::MIN, i32::MAX]); + (u, s) + } + + fn list_minmax64(u: Vec, s: Vec) -> (Vec, Vec) { + assert_eq!(u, [u64::MIN, u64::MAX]); + assert_eq!(s, [i64::MIN, i64::MAX]); + (u, s) + } + + fn list_minmax_float(u: Vec, s: Vec) -> (Vec, Vec) { + 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/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 120000 index 000000000..3f12b4ccb --- /dev/null +++ b/crates/cpp/tests/symmetric_tests/test-rust-wasm/src/lib.rs @@ -0,0 +1 @@ +../../../../../../crates/test-rust-wasm/src/lib.rs \ No newline at end of file diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 32009416d..fb8158bcf 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -1064,7 +1064,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src, "let l{tmp} = *{}.add({offset}).cast::();", operands[0], - offset = offset.format(POINTER_SIZE_EXPRESSION) + offset = offset.format_term(POINTER_SIZE_EXPRESSION, true) ); results.push(format!("l{tmp}")); } @@ -1074,7 +1074,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src, "let l{tmp} = i32::from(*{0}.add({offset}).cast::());", operands[0], - offset = offset.format(POINTER_SIZE_EXPRESSION) + offset = offset.format_term(POINTER_SIZE_EXPRESSION, true) ); results.push(format!("l{tmp}")); } @@ -1084,7 +1084,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src, "let l{tmp} = i32::from(*{}.add({offset}).cast::());", operands[0], - offset = offset.format(POINTER_SIZE_EXPRESSION) + offset = offset.format_term(POINTER_SIZE_EXPRESSION, true) ); results.push(format!("l{tmp}")); } @@ -1094,7 +1094,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src, "let l{tmp} = i32::from(*{}.add({offset}).cast::());", operands[0], - offset = offset.format(POINTER_SIZE_EXPRESSION) + offset = offset.format_term(POINTER_SIZE_EXPRESSION, true) ); results.push(format!("l{tmp}")); } @@ -1104,7 +1104,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src, "let l{tmp} = i32::from(*{}.add({offset}).cast::());", operands[0], - offset = offset.format(POINTER_SIZE_EXPRESSION) + offset = offset.format_term(POINTER_SIZE_EXPRESSION, true) ); results.push(format!("l{tmp}")); } @@ -1114,7 +1114,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src, "let l{tmp} = *{}.add({offset}).cast::();", operands[0], - offset = offset.format(POINTER_SIZE_EXPRESSION) + offset = offset.format_term(POINTER_SIZE_EXPRESSION, true) ); results.push(format!("l{tmp}")); } @@ -1124,7 +1124,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src, "let l{tmp} = *{}.add({offset}).cast::();", operands[0], - offset = offset.format(POINTER_SIZE_EXPRESSION) + offset = offset.format_term(POINTER_SIZE_EXPRESSION, true) ); results.push(format!("l{tmp}")); } @@ -1134,7 +1134,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src, "let l{tmp} = *{}.add({offset}).cast::();", operands[0], - offset = offset.format(POINTER_SIZE_EXPRESSION) + offset = offset.format_term(POINTER_SIZE_EXPRESSION, true) ); results.push(format!("l{tmp}")); } @@ -1145,7 +1145,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src, "let l{tmp} = *{}.add({offset}).cast::<*mut u8>();", operands[0], - offset = offset.format(POINTER_SIZE_EXPRESSION) + offset = offset.format_term(POINTER_SIZE_EXPRESSION, true) ); results.push(format!("l{tmp}")); } @@ -1155,7 +1155,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src, "let l{tmp} = *{}.add({}).cast::();", operands[0], - offset.format(POINTER_SIZE_EXPRESSION) + offset.format_term(POINTER_SIZE_EXPRESSION, true) ); results.push(format!("l{tmp}")); } @@ -1164,7 +1164,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(&format!( "*{}.add({}).cast::() = {};\n", operands[1], - offset.format(POINTER_SIZE_EXPRESSION), + offset.format_term(POINTER_SIZE_EXPRESSION, true), operands[0] )); } @@ -1172,7 +1172,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(&format!( "*{}.add({}).cast::() = ({}) as u8;\n", operands[1], - offset.format(POINTER_SIZE_EXPRESSION), + offset.format_term(POINTER_SIZE_EXPRESSION, true), operands[0] )); } @@ -1180,7 +1180,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(&format!( "*{}.add({}).cast::() = ({}) as u16;\n", operands[1], - offset.format(POINTER_SIZE_EXPRESSION), + offset.format_term(POINTER_SIZE_EXPRESSION, true), operands[0] )); } @@ -1188,7 +1188,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(&format!( "*{}.add({}).cast::() = {};\n", operands[1], - offset.format(POINTER_SIZE_EXPRESSION), + offset.format_term(POINTER_SIZE_EXPRESSION, true), operands[0] )); } @@ -1196,7 +1196,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(&format!( "*{}.add({}).cast::() = {};\n", operands[1], - offset.format(POINTER_SIZE_EXPRESSION), + offset.format_term(POINTER_SIZE_EXPRESSION, true), operands[0] )); } @@ -1204,7 +1204,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(&format!( "*{}.add({}).cast::() = {};\n", operands[1], - offset.format(POINTER_SIZE_EXPRESSION), + offset.format_term(POINTER_SIZE_EXPRESSION, true), operands[0] )); } @@ -1213,7 +1213,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(&format!( "*{}.add({}).cast::<*mut u8>() = {};\n", operands[1], - offset.format(POINTER_SIZE_EXPRESSION), + offset.format_term(POINTER_SIZE_EXPRESSION, true), operands[0] )); } @@ -1221,7 +1221,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(&format!( "*{}.add({}).cast::() = {};\n", operands[1], - offset.format(POINTER_SIZE_EXPRESSION), + offset.format_term(POINTER_SIZE_EXPRESSION, true), operands[0] )); } @@ -1233,7 +1233,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(&format!( "{dealloc}({op}, {size}, {align});\n", op = operands[0], - size = size.format(POINTER_SIZE_EXPRESSION), + size = size.format_term(POINTER_SIZE_EXPRESSION, true), align = align.format(POINTER_SIZE_EXPRESSION) )); } From a2f5cd71ff90107c9555ce3eb59b5101cfa6d88e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 1 Sep 2024 17:24:13 +0200 Subject: [PATCH 330/672] oh, the errors for symmetric lists cancelled each other out --- .../tests/symmetric_lists/rust_a/Cargo.toml | 10 ++ .../cpp/tests/symmetric_lists/rust_a/Makefile | 3 + .../tests/symmetric_lists/rust_a/src/main.rs | 19 +++ .../cpp/tests/symmetric_lists/rust_a/src/x.rs | 125 +++++++++++++++ .../tests/symmetric_lists/rust_b/Cargo.toml | 12 ++ .../cpp/tests/symmetric_lists/rust_b/Makefile | 3 + .../tests/symmetric_lists/rust_b/src/lib.rs | 12 ++ .../cpp/tests/symmetric_lists/rust_b/src/w.rs | 148 ++++++++++++++++++ .../cpp/tests/symmetric_lists/wit/lists.wit | 13 ++ 9 files changed, 345 insertions(+) create mode 100644 crates/cpp/tests/symmetric_lists/rust_a/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_lists/rust_a/Makefile create mode 100644 crates/cpp/tests/symmetric_lists/rust_a/src/main.rs create mode 100644 crates/cpp/tests/symmetric_lists/rust_a/src/x.rs create mode 100644 crates/cpp/tests/symmetric_lists/rust_b/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_lists/rust_b/Makefile create mode 100644 crates/cpp/tests/symmetric_lists/rust_b/src/lib.rs create mode 100644 crates/cpp/tests/symmetric_lists/rust_b/src/w.rs create mode 100644 crates/cpp/tests/symmetric_lists/wit/lists.wit 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..193f5d1cf --- /dev/null +++ b/crates/cpp/tests/symmetric_lists/rust_a/src/main.rs @@ -0,0 +1,19 @@ +// 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:?}"); +} 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..4a46bcfcb --- /dev/null +++ b/crates/cpp/tests/symmetric_lists/rust_a/src/x.rs @@ -0,0 +1,125 @@ +// 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; 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::(); + 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::(); + let len0 = vec0.len(); + *base.add(core::mem::size_of::<*const u8>()).cast::() = len0; + *base.add(0).cast::<*mut u8>() = ptr0.cast_mut(); + } + } + let ptr2 = ret_area.0.as_mut_ptr().cast::(); + #[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::(); + 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::(); + 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 + } + } + } + } +} +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) -> 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; 177] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07:\x01A\x02\x01A\x02\x01\ +B\x03\x01ps\x01@\x01\x01a\0\0\0\x04\0\x01f\x01\x01\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\x0cproc\ +essed-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..1f863ea10 --- /dev/null +++ b/crates/cpp/tests/symmetric_lists/rust_b/src/lib.rs @@ -0,0 +1,12 @@ + +mod w; + +struct MyImpl; + +impl w::exports::test::test::i::Guest for MyImpl { + fn f(a: Vec::,) -> Vec:: { + 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..3ac41f90b --- /dev/null +++ b/crates/cpp/tests/symmetric_lists/rust_b/src/w.rs @@ -0,0 +1,148 @@ +// Generated by `wit-bindgen` 0.30.0. DO NOT EDIT! +// Options used: +#[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(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::(); + let len2 = l1; + let string2 = String::from(std::str::from_utf8(std::slice::from_raw_parts(l0, len2)).unwrap()); + + string2 + }; + result3.push(e3); + } + //_rt::cabi_dealloc(base3, len3 * (2*core::mem::size_of::<*const u8>()), core::mem::size_of::<*const u8>()); + 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::(); + 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::(); + let len5 = vec5.len(); + ::core::mem::forget(vec5); + *base.add(core::mem::size_of::<*const u8>()).cast::() = len5; + *base.add(0).cast::<*mut u8>() = ptr5.cast_mut(); + } + } + *arg2.add(core::mem::size_of::<*const u8>()).cast::() = len6; + *arg2.add(0).cast::<*mut u8>() = result6; + } + pub trait Guest { + fn f(a: _rt::Vec::<_rt::String>,) -> _rt::Vec::<_rt::String>; + } + #[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) + } + };); + } + #[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::vec::Vec; + 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); + } + pub use alloc_crate::alloc; + pub use alloc_crate::string::String; + 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; 177] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07:\x01A\x02\x01A\x02\x01\ +B\x03\x01ps\x01@\x01\x01a\0\0\0\x04\0\x01f\x01\x01\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\x0cproc\ +essed-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..3bf276bc7 --- /dev/null +++ b/crates/cpp/tests/symmetric_lists/wit/lists.wit @@ -0,0 +1,13 @@ +package test:test; + +interface i { + f: func(a: list) -> list; +} + +world w { + export i; +} + +world x { + import i; +} From 166460f15857aec11e9d524318d1c667b1397acd Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 1 Sep 2024 17:42:26 +0200 Subject: [PATCH 331/672] test both canonical and non-canonical lists --- .../tests/symmetric_lists/rust_a/src/main.rs | 3 + .../cpp/tests/symmetric_lists/rust_a/src/x.rs | 38 +++- .../tests/symmetric_lists/rust_b/src/lib.rs | 7 +- .../cpp/tests/symmetric_lists/rust_b/src/w.rs | 215 ++++++++++-------- .../cpp/tests/symmetric_lists/wit/lists.wit | 1 + 5 files changed, 162 insertions(+), 102 deletions(-) diff --git a/crates/cpp/tests/symmetric_lists/rust_a/src/main.rs b/crates/cpp/tests/symmetric_lists/rust_a/src/main.rs index 193f5d1cf..4f5f0a6b8 100644 --- a/crates/cpp/tests/symmetric_lists/rust_a/src/main.rs +++ b/crates/cpp/tests/symmetric_lists/rust_a/src/main.rs @@ -16,4 +16,7 @@ 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 index 4a46bcfcb..bd8902872 100644 --- a/crates/cpp/tests/symmetric_lists/rust_a/src/x.rs +++ b/crates/cpp/tests/symmetric_lists/rust_a/src/x.rs @@ -85,6 +85,33 @@ pub mod test { result8 } } + #[allow(unused_unsafe, clippy::all)] + pub fn g(a: &[u8]) -> _rt::Vec { + unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit; 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::(); + let len0 = vec0.len(); + let ptr1 = ret_area.0.as_mut_ptr().cast::(); + #[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::(); + let len4 = l3; + _rt::Vec::from_raw_parts(l2.cast(), len4, len4) + } + } } } } @@ -112,11 +139,12 @@ mod _rt { #[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; 177] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07:\x01A\x02\x01A\x02\x01\ -B\x03\x01ps\x01@\x01\x01a\0\0\0\x04\0\x01f\x01\x01\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\x0cproc\ -essed-by\x02\x0dwit-component\x070.216.0\x10wit-bindgen-rust\x060.30.0"; +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)] diff --git a/crates/cpp/tests/symmetric_lists/rust_b/src/lib.rs b/crates/cpp/tests/symmetric_lists/rust_b/src/lib.rs index 1f863ea10..ebecc3949 100644 --- a/crates/cpp/tests/symmetric_lists/rust_b/src/lib.rs +++ b/crates/cpp/tests/symmetric_lists/rust_b/src/lib.rs @@ -1,10 +1,13 @@ - mod w; struct MyImpl; impl w::exports::test::test::i::Guest for MyImpl { - fn f(a: Vec::,) -> Vec:: { + fn f(a: Vec) -> Vec { + a + } + + fn g(a: Vec) -> Vec { a } } diff --git a/crates/cpp/tests/symmetric_lists/rust_b/src/w.rs b/crates/cpp/tests/symmetric_lists/rust_b/src/w.rs index 3ac41f90b..97804d38b 100644 --- a/crates/cpp/tests/symmetric_lists/rust_b/src/w.rs +++ b/crates/cpp/tests/symmetric_lists/rust_b/src/w.rs @@ -2,105 +2,130 @@ // Options used: #[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(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::(); - let len2 = l1; - let string2 = String::from(std::str::from_utf8(std::slice::from_raw_parts(l0, len2)).unwrap()); + #[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; - string2 - }; - result3.push(e3); - } - //_rt::cabi_dealloc(base3, len3 * (2*core::mem::size_of::<*const u8>()), core::mem::size_of::<*const u8>()); - 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::(); - 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::(); - let len5 = vec5.len(); - ::core::mem::forget(vec5); - *base.add(core::mem::size_of::<*const u8>()).cast::() = len5; - *base.add(0).cast::<*mut u8>() = ptr5.cast_mut(); - } - } - *arg2.add(core::mem::size_of::<*const u8>()).cast::() = len6; - *arg2.add(0).cast::<*mut u8>() = result6; - } - pub trait Guest { - fn f(a: _rt::Vec::<_rt::String>,) -> _rt::Vec::<_rt::String>; - } - #[doc(hidden)] + use super::super::super::super::_rt; + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_f_cabi(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::(); + let len2 = l1; + let string2 = String::from( + std::str::from_utf8(std::slice::from_raw_parts(l0, len2)).unwrap(), + ); - macro_rules! __export_test_test_i_cabi{ - ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = { + string2 + }; + result3.push(e3); + } + //_rt::cabi_dealloc(base3, len3 * (2*core::mem::size_of::<*const u8>()), core::mem::size_of::<*const u8>()); + 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::(); + 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::(); + let len5 = vec5.len(); + ::core::mem::forget(vec5); + *base.add(core::mem::size_of::<*const u8>()).cast::() = len5; + *base.add(0).cast::<*mut u8>() = ptr5.cast_mut(); + } + } + *arg2.add(core::mem::size_of::<*const u8>()).cast::() = len6; + *arg2.add(0).cast::<*mut u8>() = result6; + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_g_cabi(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; + let ptr2 = vec2.as_ptr().cast::(); + let len2 = vec2.len(); + vec2.leak(); + *arg2.add(core::mem::size_of::<*const u8>()).cast::() = 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) -> _rt::Vec; + } + #[doc(hidden)] - #[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) - } - };); - } - #[doc(hidden)] - pub(crate) use __export_test_test_i_cabi; + 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::vec::Vec; - pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { - if size == 0 { - return; + #[cfg(target_arch = "wasm32")] + pub fn run_ctors_once() { + wit_bindgen::rt::run_ctors_once(); } - let layout = alloc::Layout::from_size_align_unchecked(size, align); - alloc::dealloc(ptr, layout); - } - pub use alloc_crate::alloc; - pub use alloc_crate::string::String; - extern crate alloc as alloc_crate; + pub use alloc_crate::vec::Vec; + 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); + } + pub use alloc_crate::alloc; + pub use alloc_crate::string::String; + extern crate alloc as alloc_crate; } /// Generates `#[no_mangle]` functions to export the specified type as the @@ -134,15 +159,15 @@ 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; 177] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07:\x01A\x02\x01A\x02\x01\ -B\x03\x01ps\x01@\x01\x01a\0\0\0\x04\0\x01f\x01\x01\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\x0cproc\ -essed-by\x02\x0dwit-component\x070.216.0\x10wit-bindgen-rust\x060.30.0"; +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(); + 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 index 3bf276bc7..05185805c 100644 --- a/crates/cpp/tests/symmetric_lists/wit/lists.wit +++ b/crates/cpp/tests/symmetric_lists/wit/lists.wit @@ -2,6 +2,7 @@ package test:test; interface i { f: func(a: list) -> list; + g: func(a: list) -> list; } world w { From 034e7c677f2958f6377d4a9ecf371b1518636637 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 1 Sep 2024 21:33:45 +0200 Subject: [PATCH 332/672] symmetric list memory fixes --- .../cpp/tests/symmetric_lists/rust_b/src/w.rs | 65 +++++++------------ crates/rust/src/bindgen.rs | 33 ++++++---- 2 files changed, 45 insertions(+), 53 deletions(-) diff --git a/crates/cpp/tests/symmetric_lists/rust_b/src/w.rs b/crates/cpp/tests/symmetric_lists/rust_b/src/w.rs index 97804d38b..e660ea1d6 100644 --- a/crates/cpp/tests/symmetric_lists/rust_b/src/w.rs +++ b/crates/cpp/tests/symmetric_lists/rust_b/src/w.rs @@ -1,5 +1,3 @@ -// Generated by `wit-bindgen` 0.30.0. DO NOT EDIT! -// Options used: #[allow(dead_code)] pub mod exports { #[allow(dead_code)] @@ -12,7 +10,6 @@ pub mod exports { #[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)] @@ -31,12 +28,10 @@ pub mod exports { let string2 = String::from( std::str::from_utf8(std::slice::from_raw_parts(l0, len2)).unwrap(), ); - string2 }; result3.push(e3); } - //_rt::cabi_dealloc(base3, len3 * (2*core::mem::size_of::<*const u8>()), core::mem::size_of::<*const u8>()); let result4 = T::f(result3); let vec6 = result4; let len6 = vec6.len(); @@ -74,11 +69,11 @@ pub mod exports { _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; + 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::(); let len2 = vec2.len(); - vec2.leak(); + ::core::mem::forget(vec2); *arg2.add(core::mem::size_of::<*const u8>()).cast::() = len2; *arg2.add(0).cast::<*mut u8>() = ptr2.cast_mut(); } @@ -87,22 +82,19 @@ pub mod exports { fn g(a: _rt::Vec) -> _rt::Vec; } #[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) - } - };); - } + 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; } @@ -110,24 +102,15 @@ pub mod exports { } } mod _rt { - #[cfg(target_arch = "wasm32")] pub fn run_ctors_once() { wit_bindgen::rt::run_ctors_once(); } - pub use alloc_crate::vec::Vec; - 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); - } 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. /// @@ -146,16 +129,17 @@ mod _rt { /// ``` #[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); - ) + ($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)] @@ -165,7 +149,6 @@ 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() { diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index fb8158bcf..f26adc7c4 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -711,7 +711,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { let val = format!("vec{}", tmp); let ptr = format!("ptr{}", tmp); let len = format!("len{}", tmp); - if realloc.is_none() || self.gen.gen.opts.symmetric { + if realloc.is_none() || (self.gen.in_import && self.gen.gen.opts.symmetric) { self.push_str(&format!("let {} = {};\n", val, operands[0])); } else { let op0 = operands.pop().unwrap(); @@ -719,7 +719,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } self.push_str(&format!("let {} = {}.as_ptr().cast::();\n", ptr, val)); self.push_str(&format!("let {} = {}.len();\n", len, val)); - if realloc.is_some() && !self.gen.gen.opts.symmetric { + if realloc.is_some() && !(self.gen.in_import && self.gen.gen.opts.symmetric) { self.push_str(&format!("::core::mem::forget({});\n", val)); } results.push(format!("{ptr}.cast_mut()")); @@ -731,10 +731,17 @@ impl Bindgen for FunctionBindgen<'_, '_> { let len = format!("len{}", tmp); self.push_str(&format!("let {} = {};\n", len, operands[1])); let vec = self.gen.path_to_vec(); - let result = format!( - "{vec}::from_raw_parts({}.cast(), {1}, {1})", - operands[0], len - ); + let result = if !self.gen.gen.opts.symmetric || self.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); } @@ -871,12 +878,14 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!(self.src, "{result}.push(e{tmp});"); uwriteln!(self.src, "}}"); results.push(result); - let dealloc = self.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.gen.gen.opts.symmetric || self.gen.in_import { + let dealloc = self.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()), From 6061442c1f0754765d1bceb9f6b888a926f9dafc Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 2 Sep 2024 13:41:59 +0200 Subject: [PATCH 333/672] emergency fix --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 181e9fb6d..0b1724615 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1729,7 +1729,7 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#41f1598243d03aed203c6d7b3252b1282fc5ab1c" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#ed7d0e57208c910ccf7974fa664fe1e305aea1cc" dependencies = [ "leb128", "wasmparser 0.216.0", @@ -1738,7 +1738,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#41f1598243d03aed203c6d7b3252b1282fc5ab1c" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#ed7d0e57208c910ccf7974fa664fe1e305aea1cc" dependencies = [ "anyhow", "indexmap", @@ -1767,7 +1767,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#41f1598243d03aed203c6d7b3252b1282fc5ab1c" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#ed7d0e57208c910ccf7974fa664fe1e305aea1cc" dependencies = [ "ahash", "bitflags", @@ -2100,7 +2100,7 @@ dependencies = [ [[package]] name = "wast" version = "216.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#41f1598243d03aed203c6d7b3252b1282fc5ab1c" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#ed7d0e57208c910ccf7974fa664fe1e305aea1cc" dependencies = [ "bumpalo", "leb128", @@ -2121,7 +2121,7 @@ dependencies = [ [[package]] name = "wat" version = "1.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#41f1598243d03aed203c6d7b3252b1282fc5ab1c" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#ed7d0e57208c910ccf7974fa664fe1e305aea1cc" dependencies = [ "wast 216.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] @@ -2519,7 +2519,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#41f1598243d03aed203c6d7b3252b1282fc5ab1c" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#ed7d0e57208c910ccf7974fa664fe1e305aea1cc" dependencies = [ "anyhow", "bitflags", @@ -2556,7 +2556,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.216.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#41f1598243d03aed203c6d7b3252b1282fc5ab1c" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#ed7d0e57208c910ccf7974fa664fe1e305aea1cc" dependencies = [ "anyhow", "id-arena", From ea8ba2d2c298452525a7738bee83357816246b84 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 2 Sep 2024 20:15:12 +0200 Subject: [PATCH 334/672] require less than Rust 1.81 --- crates/cpp/tests/symmetric.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index 5b7af9e85..30d3f4e35 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -138,7 +138,6 @@ fn tests( let out_name = cpp_dir.join(format!("lib{}.so", dir_name)); cmd.arg(path) .arg(cpp_dir.join(format!("{snake}.cpp"))) - // .arg(out_dir.join(format!("{snake}_component_type.o"))) .arg("-shared") .arg("-fPIC") .arg("-I") @@ -215,7 +214,7 @@ fn symmetric_integration() -> io::Result<()> { let mut test_link = out_dir.clone(); test_link.push("tests"); - if !fs::exists(&test_link)? { + if !test_link.try_exists().unwrap_or(false) { let mut original = toplevel.clone(); original.push("tests"); std::os::unix::fs::symlink(original, &test_link)?; @@ -229,7 +228,7 @@ fn symmetric_integration() -> io::Result<()> { tester_source_dir.push("tests"); tester_source_dir.push("symmetric_tests"); - let testcases = vec!["smoke", "strings", "numbers", "lists"]; + let testcases = vec![/*"smoke", "strings", "numbers",*/ "lists"]; for dir_name in testcases { tests( From 2fbe97910d5ffa3acd09957118c6fd4776df4d24 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 2 Sep 2024 20:23:46 +0200 Subject: [PATCH 335/672] fix creation of the directory --- crates/cpp/tests/symmetric.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index 30d3f4e35..21139d6d0 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -205,6 +205,9 @@ fn symmetric_integration() -> io::Result<()> { 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")); @@ -228,7 +231,12 @@ fn symmetric_integration() -> io::Result<()> { tester_source_dir.push("tests"); tester_source_dir.push("symmetric_tests"); - let testcases = vec![/*"smoke", "strings", "numbers",*/ "lists"]; + let testcases = vec![ + "smoke", + "strings", + "numbers", + "lists", + ]; for dir_name in testcases { tests( From a0864a6f785f68a2b1e444bda829959d498b3c6c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 2 Sep 2024 21:08:56 +0200 Subject: [PATCH 336/672] more elaborate explanation --- crates/cpp/DESIGN.md | 64 +++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md index c541d65b2..c750a906c 100644 --- a/crates/cpp/DESIGN.md +++ b/crates/cpp/DESIGN.md @@ -16,29 +16,39 @@ | 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 | (&str, &[T]) | std::tuple<...> | a,l,a,l | -| | | record{string, list} | &T | T const& | a,l,a,l | +| | | tuple | (&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 | Result<&str, &[]> | std::expected | d,a,l | | | | option\ | Option\<&str> | optional const& | d,a,l| | | | list\ | &[\&Resrc]? | vector const& | a,l| -| GIR | t | string | String | wit::string[^2] | &(addr, len) | | +| GIR | t | string | String | wit::string[^2] | &(addr, len) [^8] | | | | | list | Vec | wit::vector | &(a,l) | | | | result | Result | std::expected | &(d,a,l) | -| GEA | t | string | String | wit::string&& | addr, len | -| | | result | Result | std::expected&& | d,a,l | -| GER | p | string | String | wit::string (or std?) | -> &(a,l) cabi_post_N:P/I#F | -| | | result | Result | std::expected | -> &(d,a,l) cabi_post | +| GEA | t | string | String | 🕸 wit::string&& | addr, len | +| | | | | 💎 string_view | | +| | | result | Result | 🕸 std::expected&& | d,a,l | +| | | | | 💎 std::expected | | +| GER | p | string | String | wit::string (or std?) | 📘 -> &(a,l) cabi_post_N:P/I#F [^7] | +| | | | | | 🪞 &(a,l) | +| | | result | Result | std::expected | 📘 -> &(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 | -| HER | p | string | | wit::guest_owned | -> &(a,l) | +| HEA | t | string | | 🕸 wit::string[^4] | a,l | +| | | | | 💎 string_view [^6] | | +| HER | p | string | | 🕸 wit::guest_owned | 📘 -> &(a,l) | +| | | | | 💎 wit::string [^6] | 🪞 &(a,l) | [^1]: The host never frees memory (is never passed ownership)! @@ -52,31 +62,37 @@ Complex (non-POD) struct elements on the host will need exec_env to decode or co [^5]: std::span requires C++-20, this alias should give minimal functionality with older compiler targets. -# Symmetric ABI +[^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 following convention is used in both directions: +For now for functions the guest import convention is used in both directions: - - The imported function ABI is used with the following properties +- 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) 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) 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) 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. + - (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: +- The imported resource ABI is used also for exporting + with one modification: - Resource IDs are usize, so you can optimize the resource table away. + Resource IDs become usize, so you can optimize the resource table away. From fe95f9db8bf23d27c4012133db34826e2975fa76 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 2 Sep 2024 21:34:24 +0200 Subject: [PATCH 337/672] test the new API --- tests/runtime/main.rs | 7 ++++++- tests/runtime/strings/wasm.new.cpp | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 tests/runtime/strings/wasm.new.cpp diff --git a/tests/runtime/main.rs b/tests/runtime/main.rs index 89fccbe5f..3047170a4 100644 --- a/tests/runtime/main.rs +++ b/tests/runtime/main.rs @@ -278,7 +278,12 @@ fn tests(name: &str, dir_name: &str) -> Result> { let snake = world_name.replace("-", "_"); let mut files = Default::default(); - let opts = wit_bindgen_cpp::Opts::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() { 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 +#include +#include +#include +#include + +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); +} From b9ee2fa49986af1b433fc1e9f8bb78108d2a7e27 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 2 Sep 2024 23:08:10 +0200 Subject: [PATCH 338/672] fix the memory leak with new API and canonical ABI --- crates/cpp/src/lib.rs | 88 +++++++++++++++++++++++++++++++++++++++++++ tests/runtime/main.rs | 2 + 2 files changed, 90 insertions(+) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 04232b156..368b65495 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1421,6 +1421,60 @@ impl CppInterfaceGenerator<'_> { } } + // 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| Self::needs_dealloc2(resolve, &f.ty)), + TypeDefKind::Resource => false, + TypeDefKind::Handle(_) => false, + TypeDefKind::Flags(_) => false, + TypeDefKind::Tuple(t) => t.types.iter().any(|f| Self::needs_dealloc2(resolve, f)), + TypeDefKind::Variant(_) => todo!(), + TypeDefKind::Option(_) => todo!(), + TypeDefKind::Result(r) => { + r.ok.as_ref() + .map_or(false, |tp| Self::needs_dealloc2(resolve, tp)) + || r.err + .as_ref() + .map_or(false, |tp| Self::needs_dealloc2(resolve, tp)) + } + TypeDefKind::List(_l) => true, + TypeDefKind::Future(_) => todo!(), + TypeDefKind::Stream(_) => todo!(), + TypeDefKind::Error => false, + TypeDefKind::Type(tp) => Self::needs_dealloc2(resolve, tp), + TypeDefKind::Unknown => false, + }, + } + } + + fn needs_dealloc(resolve: &Resolve, args: &[(String, Type)]) -> bool { + for (_n, t) in args { + if Self::needs_dealloc2(resolve, t) { + return true; + } + } + return false; + } + fn generate_function( &mut self, func: &Function, @@ -1457,6 +1511,19 @@ impl CppInterfaceGenerator<'_> { 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 + && !self.gen.opts.symmetric + && matches!(variant, AbiVariant::GuestExport) + && Self::needs_dealloc(self.resolve, &func.params) + { + self.gen + .c_src + .src + .push_str("std::vector _deallocate;\n"); + true + } else { + false + }; let lift_lower = if self.gen.opts.symmetric { LiftLower::Symmetric } else if export { @@ -1672,6 +1739,7 @@ impl CppInterfaceGenerator<'_> { 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) @@ -2446,6 +2514,7 @@ struct FunctionBindgen<'a, 'b> { wamr_signature: Option, variant: AbiVariant, cabi_post: Option, + needs_dealloc: bool, } impl<'a, 'b> FunctionBindgen<'a, 'b> { @@ -2464,6 +2533,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { wamr_signature: None, variant: AbiVariant::GuestImport, cabi_post: None, + needs_dealloc: false, } } @@ -2816,6 +2886,17 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { || (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]) @@ -3503,6 +3584,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } 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); diff --git a/tests/runtime/main.rs b/tests/runtime/main.rs index 3047170a4..a939ec18a 100644 --- a/tests/runtime/main.rs +++ b/tests/runtime/main.rs @@ -315,6 +315,8 @@ fn tests(name: &str, dir_name: &str) -> Result> { // .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") From 797ca6c106cdb215f41b0d5d9fdeb2ff2126d4f6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 2 Sep 2024 23:34:37 +0200 Subject: [PATCH 339/672] register the Rust strings test and exercise new API symmetric tests --- crates/cpp/tests/symmetric.rs | 5 +++++ crates/test-rust-wasm/src/bin/strings.rs | 3 +++ 2 files changed, 8 insertions(+) create mode 100644 crates/test-rust-wasm/src/bin/strings.rs diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index 21139d6d0..e19e48a13 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -126,6 +126,11 @@ fn tests( 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; + } + } opts.build().generate(&resolve, world, &mut files).unwrap(); for (file, contents) in files.iter() { 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() {} From f661f88be672ef6f0e92a822fe9f2be475e85af9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 3 Sep 2024 09:46:18 +0200 Subject: [PATCH 340/672] quick fix to remove the leak from symmetric calls --- crates/rust/src/bindgen.rs | 8 ++++++++ crates/rust/src/interface.rs | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index f26adc7c4..8bc47bfde 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -825,6 +825,11 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(&format!( "let ptr = {alloc}::alloc({layout}).cast::();\n", )); + if self.gen.gen.opts.symmetric && self.gen.in_import { + 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}}", )); @@ -909,6 +914,9 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str("("); self.push_str(&operands.join(", ")); self.push_str(");\n"); + if self.gen.gen.opts.symmetric && self.gen.in_import { + self.push_str(&format!("for (ptr,layout) in _deallocate.drain(..) {{ _rt::alloc::dealloc(ptr, layout); }}\n")); + } } Instruction::AsyncCallWasm { name, size, align } => { diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 36102c88e..21ddf709b 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -867,6 +867,12 @@ impl {async_support}::StreamPayload for {name} {{ self.src.push_str("#[allow(unused_unsafe, clippy::all)]\n"); let params = self.print_signature(func, false, &sig, true); self.src.push_str("{\n"); + if self.gen.opts.symmetric { + 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_); From fc5839c3650c25c4d26ce517f19cd54d114ff56d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 6 Sep 2024 01:01:36 +0200 Subject: [PATCH 341/672] failing flavorful test --- crates/cpp/tests/symmetric.rs | 1 + crates/cpp/tests/symmetric_tests/flavorful.rs | 150 ++++++++++++++++++ 2 files changed, 151 insertions(+) create mode 100644 crates/cpp/tests/symmetric_tests/flavorful.rs diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index e19e48a13..3d306b901 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -240,6 +240,7 @@ fn symmetric_integration() -> io::Result<()> { "smoke", "strings", "numbers", + "flavorful", "lists", ]; 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 { + Some("list_in_variant2".to_string()) + } + + fn f_list_in_variant3(a: test_imports::ListInVariant3) -> Option { + 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() {} + assert_error::(); + 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, + results: Vec>, + enums: Vec, + ) -> (Vec, Vec>, Vec) { + 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() {} + assert_error::(); + + 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() }; + }; + } +} From 8c2033fab303959853fb034d2434f86b6fa1d868 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Sep 2024 16:22:51 +0200 Subject: [PATCH 342/672] C++ vector example (in progress) --- crates/cpp/helper-types/wit-common.h | 1 + crates/cpp/helper-types/wit-guest.h | 3 + tests/runtime/lists/wasm.new.cpp | 150 +++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) create mode 100644 tests/runtime/lists/wasm.new.cpp diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h index d0a57d0c5..771f2b8d4 100644 --- a/crates/cpp/helper-types/wit-common.h +++ b/crates/cpp/helper-types/wit-common.h @@ -28,6 +28,7 @@ template class span { const_iterator begin() const { return address; } const_iterator end() const { return address + length; } + bool empty() const { return !length; } T const &operator[](size_t index) { return address[index]; } // create from any compatible vector (borrows data!) template diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 18af90ee4..8ee598666 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -32,6 +32,7 @@ class string { 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_) { free(const_cast(data_)); @@ -76,11 +77,13 @@ template class vector { return *this; } vector(T *d, size_t l) : data_(d), length(l) {} + vector() : data_(nullptr), 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_) { free(data_); diff --git a/tests/runtime/lists/wasm.new.cpp b/tests/runtime/lists/wasm.new.cpp new file mode 100644 index 000000000..9e383be40 --- /dev/null +++ b/tests/runtime/lists/wasm.new.cpp @@ -0,0 +1,150 @@ +#include +#include + +uint32_t exports::lists::AllocatedBytes() { + return 0; +} + +template +static bool equal(wit::vector const&a, wit::span const& b); +template +static bool equal(wit::vector const&a, std::vector const& b); +static bool equal(wit::string const&a, std::string_view b) { + return a.get_view() == b; +} +static bool equal(wit::vector const&a, std::vector const& b); + +void exports::lists::TestImports() { + //let _guard = testRust_wasm::guard(); + + test::lists::test::EmptyListParam(wit::span(std::vector())); + test::lists::test::EmptyStringParam(""); + assert(test::lists::test::EmptyListResult().empty()); + assert(test::lists::test::EmptyStringResult().empty()); + + test::lists::test::ListParam(std::vector{1, 2, 3, 4}); + test::lists::test::ListParam2("foo"); + test::lists::test::ListParam3(std::vector{"foo", "bar", "baz"}); + test::lists::test::ListParam4(std::vector>{ + std::vector{"foo", "bar"}, + std::vector{"baz"}, + }); + assert(equal(test::lists::test::ListResult(), std::vector{1, 2, 3, 4, 5})); + assert(equal(test::lists::test::ListResult2(), "hello!")); + assert(equal(test::lists::test::ListResult3(), std::vector{"hello,", "world!"})); + + assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector())), std::vector())); + assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'x'})), std::vector{'x'})); + assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'h', 'e', 'l', 'l', 'o'})), std::vector{'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{0, UINT8_MAX}, std::vector{INT8_MIN, INT8_MAX}), + std::make_tuple(std::vector{0, UINT8_MAX}, std::vector{INT8_MIN, INT8_MAX}) + )); + // assert(equal( + // test::lists::test::ListMinmax16(&[u16::MIN, u16::MAX], &[i16::MIN, i16::MAX]), + // (vec![u16::MIN, u16::MAX], vec![i16::MIN, i16::MAX]), + // )); + // assert(equal( + // test::lists::test::ListMinmax32(&[u32::MIN, u32::MAX], &[i32::MIN, i32::MAX]), + // (vec![u32::MIN, u32::MAX], vec![i32::MIN, i32::MAX]), + // )); + // assert(equal( + // test::lists::test::ListMinmax64(&[u64::MIN, u64::MAX], &[i64::MIN, i64::MAX]), + // (vec![u64::MIN, u64::MAX], vec![i64::MIN, i64::MAX]), + // )); + // assert(equal( + // test::lists::test::ListMinmaxFloat( + // &[f32::MIN, f32::MAX, f32::NEG_INFINITY, f32::INFINITY], + // &[f64::MIN, f64::MAX, f64::NEG_INFINITY, f64::INFINITY] + // ), + // ( + // vec![f32::MIN, f32::MAX, f32::NEG_INFINITY, f32::INFINITY], + // vec![f64::MIN, f64::MAX, f64::NEG_INFINITY, f64::INFINITY], + // ), + // )); +} + + +void exports::test::lists::test::EmptyListParam(wit::span a) { + assert(a.empty()); +} + +void exports::test::lists::test::EmptyStringParam(std::string_view a) { + assert(a.empty()); +} + +wit::vector exports::test::lists::test::EmptyListResult() { + return wit::vector(); +} + +wit::string exports::test::lists::test::EmptyStringResult() { + return wit::string::from_view(std::string_view()); +} + +void exports::test::lists::test::ListParam(wit::span list) { + assert(equal(list, std::vector{1, 2, 3, 4})); +} + +void exports::test::lists::test::ListParam2(std::string_view ptr) { + assert(equal(ptr, "foo")); +} + +void exports::test::lists::test::ListParam3(wit::span ptr) { + assert(equal(ptr.size(), 3)); + assert(equal(ptr[0], "foo")); + assert(equal(ptr[1], "bar")); + assert(equal(ptr[2], "baz")); +} + +void exports::test::lists::test::ListParam4(wit::span> ptr) { + assert(equal(ptr.size(), 2)); + assert(equal(ptr[0][0], "foo")); + assert(equal(ptr[0][1], "bar")); + assert(equal(ptr[1][0], "baz")); +} + +wit::vector exports::test::lists::test::ListResult() { + return std::vector{1, 2, 3, 4, 5}; +} + +wit::string exports::test::lists::test::ListResult2() { + return wit::string::from_view("hello!"); +} + +wit::vector exports::test::lists::test::ListResult3() { + return std::vector{wit::string::from_view("hello,"), wit::string::from_view("world!")}; +} + +wit::vector exports::test::lists::test::ListRoundtrip(wit::span x) { + return wit::vector::from_span(x); +} + +wit::string exports::test::lists::test::StringRoundtrip(std::string_view x) { + return wit::string::from_view(x); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmax8(wit::span a, wit::span b) { + return std::make_tuple(a, b); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmax16(wit::span a, wit::span b) { + return std::make_tuple(a, b); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmax32(wit::span a, wit::span b) { + return std::make_tuple(a, b); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmax64(wit::span a, wit::span b) { + return std::make_tuple(a, b); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmaxFloat(wit::span a, wit::span b) { + return std::make_tuple(a, b); +} From 3f127a4ae596c9586d919e1498846775d4daa2e5 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 9 Sep 2024 00:30:18 +0200 Subject: [PATCH 343/672] more C++ functionality --- crates/cpp/helper-types/wit-common.h | 2 +- crates/cpp/helper-types/wit-guest.h | 2 ++ tests/runtime/lists/wasm.new.cpp | 48 +++++++++++++++++----------- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h index 771f2b8d4..014b9e288 100644 --- a/crates/cpp/helper-types/wit-common.h +++ b/crates/cpp/helper-types/wit-common.h @@ -29,7 +29,7 @@ template class span { const_iterator begin() const { return address; } const_iterator end() const { return address + length; } bool empty() const { return !length; } - T const &operator[](size_t index) { return address[index]; } + T const &operator[](size_t index) const { return address[index]; } // create from any compatible vector (borrows data!) template span(std::vector const &vec) : address(vec.data()), length(vec.size()) {} diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 8ee598666..fdfb9cfc9 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -94,6 +94,8 @@ template class vector { // typically called by post static void drop_raw(void *ptr) { free(ptr); } wit::span get_view() const { return wit::span(data_, length); } + template static vector from_view(wit::span const& a); +// static vector from_view(wit::span const& a); }; /// @brief A Resource defined within the guest (guest side) diff --git a/tests/runtime/lists/wasm.new.cpp b/tests/runtime/lists/wasm.new.cpp index 9e383be40..68dd99e8c 100644 --- a/tests/runtime/lists/wasm.new.cpp +++ b/tests/runtime/lists/wasm.new.cpp @@ -8,11 +8,21 @@ uint32_t exports::lists::AllocatedBytes() { template static bool equal(wit::vector const&a, wit::span const& b); template +static bool equal(wit::span const&a, wit::vector const& b); +template +static bool equal(wit::span const&a, std::vector const& b); +template static bool equal(wit::vector const&a, std::vector const& b); static bool equal(wit::string const&a, std::string_view b) { return a.get_view() == b; } +template +static bool equal(T const&a, T const& b) { + return a == b; +} static bool equal(wit::vector const&a, std::vector const& b); +template +static bool equal(std::tuple const&a, std::tuple const& b); void exports::lists::TestImports() { //let _guard = testRust_wasm::guard(); @@ -34,8 +44,8 @@ void exports::lists::TestImports() { assert(equal(test::lists::test::ListResult3(), std::vector{"hello,", "world!"})); assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector())), std::vector())); - assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'x'})), std::vector{'x'})); - assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'h', 'e', 'l', 'l', 'o'})), std::vector{'h', 'e', 'l', 'l', 'o'})); + assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'x'})), std::vector{'x'})); + assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'h', 'e', 'l', 'l', 'o'})), std::vector{'h', 'e', 'l', 'l', 'o'})); assert(equal(test::lists::test::StringRoundtrip("x"), "x")); assert(equal(test::lists::test::StringRoundtrip(""), "")); @@ -92,25 +102,25 @@ void exports::test::lists::test::ListParam(wit::span list) { } void exports::test::lists::test::ListParam2(std::string_view ptr) { - assert(equal(ptr, "foo")); + assert(equal(ptr, std::string_view("foo"))); } void exports::test::lists::test::ListParam3(wit::span ptr) { - assert(equal(ptr.size(), 3)); - assert(equal(ptr[0], "foo")); - assert(equal(ptr[1], "bar")); - assert(equal(ptr[2], "baz")); + 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> ptr) { - assert(equal(ptr.size(), 2)); - assert(equal(ptr[0][0], "foo")); - assert(equal(ptr[0][1], "bar")); - assert(equal(ptr[1][0], "baz")); + 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 exports::test::lists::test::ListResult() { - return std::vector{1, 2, 3, 4, 5}; + return wit::vector::from_view(wit::span(std::vector{1, 2, 3, 4, 5})); } wit::string exports::test::lists::test::ListResult2() { @@ -118,11 +128,11 @@ wit::string exports::test::lists::test::ListResult2() { } wit::vector exports::test::lists::test::ListResult3() { - return std::vector{wit::string::from_view("hello,"), wit::string::from_view("world!")}; + return wit::vector::from_view(wit::span(std::vector{wit::string::from_view("hello,"), wit::string::from_view("world!")})); } wit::vector exports::test::lists::test::ListRoundtrip(wit::span x) { - return wit::vector::from_span(x); + return wit::vector::from_view(x); } wit::string exports::test::lists::test::StringRoundtrip(std::string_view x) { @@ -130,21 +140,21 @@ wit::string exports::test::lists::test::StringRoundtrip(std::string_view x) { } std::tuple, wit::vector> exports::test::lists::test::ListMinmax8(wit::span a, wit::span b) { - return std::make_tuple(a, b); + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); } std::tuple, wit::vector> exports::test::lists::test::ListMinmax16(wit::span a, wit::span b) { - return std::make_tuple(a, b); + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); } std::tuple, wit::vector> exports::test::lists::test::ListMinmax32(wit::span a, wit::span b) { - return std::make_tuple(a, b); + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); } std::tuple, wit::vector> exports::test::lists::test::ListMinmax64(wit::span a, wit::span b) { - return std::make_tuple(a, b); + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); } std::tuple, wit::vector> exports::test::lists::test::ListMinmaxFloat(wit::span a, wit::span b) { - return std::make_tuple(a, b); + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); } From 75754fb965e769821e69a02095ad4db86a0465f3 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 10 Sep 2024 01:04:24 +0200 Subject: [PATCH 344/672] more testcode --- tests/runtime/lists/wasm.new.cpp | 46 +++++++++++++++++--------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/tests/runtime/lists/wasm.new.cpp b/tests/runtime/lists/wasm.new.cpp index 68dd99e8c..c8f0b058d 100644 --- a/tests/runtime/lists/wasm.new.cpp +++ b/tests/runtime/lists/wasm.new.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include uint32_t exports::lists::AllocatedBytes() { return 0; @@ -56,28 +58,28 @@ void exports::lists::TestImports() { test::lists::test::ListMinmax8(std::vector{0, UINT8_MAX}, std::vector{INT8_MIN, INT8_MAX}), std::make_tuple(std::vector{0, UINT8_MAX}, std::vector{INT8_MIN, INT8_MAX}) )); - // assert(equal( - // test::lists::test::ListMinmax16(&[u16::MIN, u16::MAX], &[i16::MIN, i16::MAX]), - // (vec![u16::MIN, u16::MAX], vec![i16::MIN, i16::MAX]), - // )); - // assert(equal( - // test::lists::test::ListMinmax32(&[u32::MIN, u32::MAX], &[i32::MIN, i32::MAX]), - // (vec![u32::MIN, u32::MAX], vec![i32::MIN, i32::MAX]), - // )); - // assert(equal( - // test::lists::test::ListMinmax64(&[u64::MIN, u64::MAX], &[i64::MIN, i64::MAX]), - // (vec![u64::MIN, u64::MAX], vec![i64::MIN, i64::MAX]), - // )); - // assert(equal( - // test::lists::test::ListMinmaxFloat( - // &[f32::MIN, f32::MAX, f32::NEG_INFINITY, f32::INFINITY], - // &[f64::MIN, f64::MAX, f64::NEG_INFINITY, f64::INFINITY] - // ), - // ( - // vec![f32::MIN, f32::MAX, f32::NEG_INFINITY, f32::INFINITY], - // vec![f64::MIN, f64::MAX, f64::NEG_INFINITY, f64::INFINITY], - // ), - // )); + assert(equal( + test::lists::test::ListMinmax16(std::vector{0, UINT16_MAX}, std::vector{INT16_MIN, INT16_MAX}), + std::make_tuple(std::vector{0, UINT16_MAX}, std::vector{INT16_MIN, INT16_MAX}) + )); + assert(equal( + test::lists::test::ListMinmax32(std::vector{0, UINT32_MAX}, std::vector{INT32_MIN, INT32_MAX}), + std::make_tuple(std::vector{0, UINT32_MAX}, std::vector{INT32_MIN, INT32_MAX}) + )); + assert(equal( + test::lists::test::ListMinmax64(std::vector{0, UINT64_MAX}, std::vector{INT64_MIN, INT64_MAX}), + std::make_tuple(std::vector{0, UINT64_MAX}, std::vector{INT64_MIN, INT64_MAX}) + )); + assert(equal( + test::lists::test::ListMinmaxFloat( + std::vector{FLT_MIN, FLT_MAX, -HUGE_VALF, HUGE_VALF}, + std::vector{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL} + ), + std::make_tuple( + std::vector{FLT_MIN, FLT_MAX, -HUGE_VALF, HUGE_VALF}, + std::vector{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL} + ) + )); } From 66b86f2588125a15a7c0266e9f9cbc41165ac020 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 11 Sep 2024 21:57:54 +0200 Subject: [PATCH 345/672] more vector preparation --- crates/cpp/helper-types/wit-guest.h | 3 ++- tests/runtime/lists/wasm.new.cpp | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index fdfb9cfc9..1d0bef282 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -17,7 +17,8 @@ class string { size_t length; public: - string(string const &) = delete; + // this constructor is helpful for creating vector + 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) { diff --git a/tests/runtime/lists/wasm.new.cpp b/tests/runtime/lists/wasm.new.cpp index c8f0b058d..db6450ea9 100644 --- a/tests/runtime/lists/wasm.new.cpp +++ b/tests/runtime/lists/wasm.new.cpp @@ -37,9 +37,9 @@ void exports::lists::TestImports() { test::lists::test::ListParam(std::vector{1, 2, 3, 4}); test::lists::test::ListParam2("foo"); test::lists::test::ListParam3(std::vector{"foo", "bar", "baz"}); - test::lists::test::ListParam4(std::vector>{ - std::vector{"foo", "bar"}, - std::vector{"baz"}, + test::lists::test::ListParam4(std::vector>{ + std::vector{"foo", "bar"}, + std::vector{"baz"}, }); assert(equal(test::lists::test::ListResult(), std::vector{1, 2, 3, 4, 5})); assert(equal(test::lists::test::ListResult2(), "hello!")); From efe2007494524e510ede2f93851a7b41f315550c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 11 Sep 2024 21:58:22 +0200 Subject: [PATCH 346/672] wit vector construction in memory --- crates/cpp/helper-types/wit-common.h | 1 + crates/cpp/helper-types/wit-guest.h | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h index 014b9e288..722f32c54 100644 --- a/crates/cpp/helper-types/wit-common.h +++ b/crates/cpp/helper-types/wit-common.h @@ -30,6 +30,7 @@ template class span { 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 span(std::vector const &vec) : address(vec.data()), length(vec.size()) {} diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 1d0bef282..de2db979e 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -90,6 +90,13 @@ template class vector { free(data_); } } + // WARNING: vector contains uninitialized elements + static vector allocate(size_t len) { + return vector((T*)malloc(sizeof(T)*len), len); + } + void initialize(size_t n, T&& elem) { + new (data_+n) T(std::move(elem)); + } // leak the memory void leak() { data_ = nullptr; } // typically called by post From 816d99bacbb617f61407d653ece463f6a16fcb12 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 11 Sep 2024 23:58:56 +0200 Subject: [PATCH 347/672] comparison implementations --- tests/runtime/lists/wasm.new.cpp | 45 ++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/tests/runtime/lists/wasm.new.cpp b/tests/runtime/lists/wasm.new.cpp index db6450ea9..91130fa1e 100644 --- a/tests/runtime/lists/wasm.new.cpp +++ b/tests/runtime/lists/wasm.new.cpp @@ -7,24 +7,47 @@ 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 +static bool equal(T const&a, S const& b) { + return a == b; +} +template +static bool equal(wit::span const&a, wit::span const& b) { + if (a.size() != b.size()) { return false; } + for (uint32_t i = 0; i -static bool equal(wit::vector const&a, wit::span const& b); +static bool equal(wit::vector const&a, wit::span const& b) { + return equal(a.get_view(), b); +} template -static bool equal(wit::span const&a, wit::vector const& b); +static bool equal(wit::span const&a, wit::vector const& b) { + return equal(b, a); +} template -static bool equal(wit::span const&a, std::vector const& b); +static bool equal(wit::span const&a, std::vector const& b) { + return equal(a, wit::span(b)); +} template -static bool equal(wit::vector const&a, std::vector const& b); -static bool equal(wit::string const&a, std::string_view b) { - return a.get_view() == b; +static bool equal(wit::vector const&a, std::vector const& b) { + return equal(a.get_view(), wit::span(b)); } -template -static bool equal(T const&a, T const& b) { - return a == b; +static bool equal(wit::vector const&a, std::vector const& b) { + return equal(a.get_view(), wit::span(b)); } -static bool equal(wit::vector const&a, std::vector const& b); template -static bool equal(std::tuple const&a, std::tuple const& b); +static bool equal(std::tuple const&a, std::tuple 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(); From f6405c5b0431b3d54e27afdec607326f58a219f0 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 12 Sep 2024 00:03:48 +0200 Subject: [PATCH 348/672] fix compilation with newest wasm-tools --- crates/c/src/component_type_object.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/c/src/component_type_object.rs b/crates/c/src/component_type_object.rs index d3397550c..b38ce7ad8 100644 --- a/crates/c/src/component_type_object.rs +++ b/crates/c/src/component_type_object.rs @@ -23,7 +23,7 @@ pub fn object( // Build a module with one function that's a "dummy function" let mut types = TypeSection::new(); - types.function([], []); + types.ty().function([], []); module.section(&types); let mut funcs = FunctionSection::new(); funcs.function(0); From 5e9d57380dd676aefc616b1051667b71e5bac2f3 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 12 Sep 2024 00:18:57 +0200 Subject: [PATCH 349/672] first round of C++ vector fixes --- crates/cpp/src/lib.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 368b65495..f03083d76 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2923,8 +2923,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { operand1 = operands[1] )); self.push_str(&format!( - r#"auto {result} = wit::vector<{vtype}>(); - {result}.allocate({len}); + r#"auto {result} = wit::vector<{vtype}>::allocate({len}); "#, )); @@ -2936,9 +2935,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { ); uwriteln!(self.src, "auto e{tmp} = todo();"); // inplace construct - uwriteln!(self.src, "{result}.push_back(e{tmp});"); + uwriteln!(self.src, "{result}.initialize(i, std::move(e{tmp}));"); uwriteln!(self.src, "}}"); - results.push(result); + results.push(format!("std::move({result})")); // self.push_str(&format!( // "{rt}::dealloc({base}, ({len} as usize) * {size}, {align});\n", // rt = self.gen.gen.runtime_path(), @@ -3099,7 +3098,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src.push_str(">("); self.src.push_str(&operands.join(", ")); self.src.push_str(");\n"); - results.push(name); + results.push(format!("std::move({name})")); } abi::Instruction::FlagsLower { flags, ty, .. } => { match wit_bindgen_c::flags_repr(flags) { From 068a9181664d64d40f12b43c5bc2e80400def284 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 12 Sep 2024 00:27:08 +0200 Subject: [PATCH 350/672] arguments as view --- crates/cpp/src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index f03083d76..636becf26 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2860,6 +2860,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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) + { + format!( + "wit::vector<{inner} const>(({inner}*)({}), {len}).get_view()", + operands[0] + ) } else { format!("wit::vector<{inner}>(({inner}*)({}), {len})", operands[0]) }; From b94280a6f06ad37358f5b5dd02195ddd2181633b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 12 Sep 2024 00:42:43 +0200 Subject: [PATCH 351/672] promising vector element reconstruction --- crates/cpp/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 636becf26..467101e90 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2911,7 +2911,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { results.push(result); } abi::Instruction::ListLift { element, .. } => { - // let body = self.blocks.pop().unwrap(); + let body = self.blocks.pop().unwrap(); let tmp = self.tmp(); let size = self.gen.sizes.size(element); let _align = self.gen.sizes.align(element); @@ -2940,7 +2940,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { "auto base = {base} + i * {size};", size = size.format(POINTER_SIZE_EXPRESSION) ); - uwriteln!(self.src, "auto e{tmp} = todo();"); + uwrite!(self.src, "{}", body.0); + uwriteln!(self.src, "auto e{tmp} = {};", body.1[0]); // inplace construct uwriteln!(self.src, "{result}.initialize(i, std::move(e{tmp}));"); uwriteln!(self.src, "}}"); From 16700ddb52913cd31c58e0d038922dff760db132 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 12 Sep 2024 21:19:21 +0200 Subject: [PATCH 352/672] fully linking c++ lists example (with codegen modifications) --- crates/cpp/helper-types/wit-guest.h | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index de2db979e..2c8c77f6a 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -6,6 +6,8 @@ #include #include #include // memcpy +#include // free +#include namespace wit { /// A string in linear memory, freed unconditionally using free @@ -87,7 +89,7 @@ template class vector { bool empty() const { return !length; } ~vector() { if (data_) { - free(data_); + free((void*)data_); } } // WARNING: vector contains uninitialized elements @@ -95,14 +97,21 @@ template class vector { return vector((T*)malloc(sizeof(T)*len), len); } void initialize(size_t n, T&& elem) { - new (data_+n) T(std::move(elem)); + new ((void*)(data_+n)) T(std::move(elem)); } // leak the memory - void leak() { data_ = nullptr; } + T* leak() { T*result = data_; data_ = nullptr; return result; } // typically called by post static void drop_raw(void *ptr) { free(ptr); } wit::span get_view() const { return wit::span(data_, length); } - template static vector from_view(wit::span const& a); + wit::span get_const_view() const { return wit::span(data_, length); } + template static vector from_view(wit::span const& a) { + auto result = vector::allocate(a.size()); + for (uint32_t i=0;i from_view(wit::span const& a); }; From 0c2dd18728fa63ee520f853b862d2f16f9eda97b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 12 Sep 2024 23:04:18 +0200 Subject: [PATCH 353/672] fix cabi_post symbol --- crates/cpp/src/lib.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 467101e90..25688f361 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1775,12 +1775,13 @@ impl CppInterfaceGenerator<'_> { 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) + // 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), }; From 5d2bfb8db2176635b4da715c3a6f778708c60102 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 12 Sep 2024 23:42:19 +0200 Subject: [PATCH 354/672] nearly perfect on lists --- crates/cpp/src/lib.rs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 25688f361..e94acf9cd 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -35,6 +35,7 @@ enum Flavor { Argument(AbiVariant), Result(AbiVariant), InStruct, + BorrowedArgument, } impl Flavor { @@ -42,7 +43,7 @@ impl Flavor { match self { Flavor::Argument(var) => matches!(var, AbiVariant::GuestExport), Flavor::Result(var) => matches!(var, AbiVariant::GuestExport), - Flavor::InStruct => false, + Flavor::InStruct | Flavor::BorrowedArgument => false, } } } @@ -1931,6 +1932,10 @@ impl CppInterfaceGenerator<'_> { 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 => { @@ -2016,6 +2021,10 @@ impl CppInterfaceGenerator<'_> { 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 => @@ -2916,9 +2925,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let tmp = self.tmp(); let size = self.gen.sizes.size(element); let _align = self.gen.sizes.align(element); - let vtype = self - .gen - .type_name(element, &self.namespace, Flavor::InStruct); + 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}"); @@ -2946,7 +2960,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // inplace construct uwriteln!(self.src, "{result}.initialize(i, std::move(e{tmp}));"); uwriteln!(self.src, "}}"); - results.push(format!("std::move({result})")); + if self.gen.gen.opts.new_api && matches!(self.variant, AbiVariant::GuestExport) { + results.push(format!("{result}.get_const_view()")); + } else { + results.push(format!("std::move({result})")); + } // self.push_str(&format!( // "{rt}::dealloc({base}, ({len} as usize) * {size}, {align});\n", // rt = self.gen.gen.runtime_path(), From ccaf0ca5944e0cc3ba9935b2083a62950a40fd87 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 13 Sep 2024 00:01:27 +0200 Subject: [PATCH 355/672] deallocate arguments in canonical ABI --- crates/cpp/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index e94acf9cd..985ff7d75 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2948,6 +2948,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { r#"auto {result} = wit::vector<{vtype}>::allocate({len}); "#, )); + if self.gen.gen.opts.new_api && matches!(self.variant, AbiVariant::GuestExport) { + self.push_str(&format!("if ({len}>0) _deallocate.push_back({base});\n")); + } uwriteln!(self.src, "for (unsigned i=0; i<{len}; ++i) {{"); uwriteln!( From da061599d7da09fad41f06ea4091cdc2c9dffecf Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 13 Sep 2024 22:46:40 +0200 Subject: [PATCH 356/672] properly free vector contents --- crates/cpp/helper-types/wit-guest.h | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index 2c8c77f6a..a89e24d12 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -89,6 +89,7 @@ template class vector { bool empty() const { return !length; } ~vector() { if (data_) { + for (unsigned i=0;i Date: Fri, 13 Sep 2024 22:48:02 +0200 Subject: [PATCH 357/672] fix native compilation --- tests/runtime/lists/wasm.new.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/runtime/lists/wasm.new.cpp b/tests/runtime/lists/wasm.new.cpp index 91130fa1e..0e4481a6d 100644 --- a/tests/runtime/lists/wasm.new.cpp +++ b/tests/runtime/lists/wasm.new.cpp @@ -52,7 +52,7 @@ static bool equal(std::tuple const&a, std::tuple const& b) { void exports::lists::TestImports() { //let _guard = testRust_wasm::guard(); - test::lists::test::EmptyListParam(wit::span(std::vector())); + test::lists::test::EmptyListParam(wit::span(std::vector())); test::lists::test::EmptyStringParam(""); assert(test::lists::test::EmptyListResult().empty()); assert(test::lists::test::EmptyStringResult().empty()); @@ -60,17 +60,17 @@ void exports::lists::TestImports() { test::lists::test::ListParam(std::vector{1, 2, 3, 4}); test::lists::test::ListParam2("foo"); test::lists::test::ListParam3(std::vector{"foo", "bar", "baz"}); - test::lists::test::ListParam4(std::vector>{ - std::vector{"foo", "bar"}, - std::vector{"baz"}, + test::lists::test::ListParam4(std::vector>{ + std::vector{"foo", "bar"}, + std::vector{"baz"}, }); assert(equal(test::lists::test::ListResult(), std::vector{1, 2, 3, 4, 5})); assert(equal(test::lists::test::ListResult2(), "hello!")); assert(equal(test::lists::test::ListResult3(), std::vector{"hello,", "world!"})); - assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector())), std::vector())); - assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'x'})), std::vector{'x'})); - assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'h', 'e', 'l', 'l', 'o'})), std::vector{'h', 'e', 'l', 'l', 'o'})); + assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector())), std::vector())); + assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'x'})), std::vector{'x'})); + assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'h', 'e', 'l', 'l', 'o'})), std::vector{'h', 'e', 'l', 'l', 'o'})); assert(equal(test::lists::test::StringRoundtrip("x"), "x")); assert(equal(test::lists::test::StringRoundtrip(""), "")); From 28e88c009cb1059ebe67bfa821622a2684777818 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 13 Sep 2024 23:28:27 +0200 Subject: [PATCH 358/672] correct symmetric new-api lists --- crates/cpp/src/lib.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 985ff7d75..1394a13ff 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2830,7 +2830,6 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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)); @@ -2847,7 +2846,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if realloc.is_none() { results.push(ptr); } else { - if !self.gen.gen.opts.host_side() { + 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); @@ -2861,12 +2863,6 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { .gen .type_name(element, &self.namespace, Flavor::InStruct); self.push_str(&format!("auto {} = {};\n", len, operands[1])); - // let typecast = if self.gen.gen.opts.host { - // String::new() - // } else { - // format!("({inner} const *)") - // }; - // let result = format!("wit::vector<{inner}>({typecast}{}, {len})", operands[0]); 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) @@ -2891,11 +2887,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { && matches!(self.variant, AbiVariant::GuestExport) { uwriteln!(self.src, "auto string{tmp} = wit::string::from_view(std::string_view((char const *)({}), {len}));\n", operands[0]); - // if matches!(self.variant, AbiVariant::GuestExport) { format!("std::move(string{tmp})") - // } else { - // format!("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) @@ -2948,7 +2940,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { r#"auto {result} = wit::vector<{vtype}>::allocate({len}); "#, )); - if 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 + { self.push_str(&format!("if ({len}>0) _deallocate.push_back({base});\n")); } @@ -2963,15 +2958,15 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // 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()")); } else { results.push(format!("std::move({result})")); } - // self.push_str(&format!( - // "{rt}::dealloc({base}, ({len} as usize) * {size}, {align});\n", - // rt = self.gen.gen.runtime_path(), - // )); } abi::Instruction::IterElem { .. } => results.push("IterElem".to_string()), abi::Instruction::IterBasePointer => results.push("base".to_string()), @@ -3651,7 +3646,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src.push_str(">("); } if *amt == 1 { - self.src.push_str(&operands[0]); + 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 { self.src.push_str("std::tuple<"); if let Results::Named(params) = &func.results { From 93784350396a8a8f7fdfe61ff7af40fab425a1b4 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 13 Sep 2024 23:42:11 +0200 Subject: [PATCH 359/672] ignore more --- crates/cpp/tests/.gitignore | 3 +++ crates/cpp/tests/meshless_resources/.gitignore | 2 ++ crates/cpp/tests/meshless_strings/.gitignore | 2 ++ crates/cpp/tests/native_mesh/component_b/.gitignore | 1 + 4 files changed, 8 insertions(+) create mode 100644 crates/cpp/tests/.gitignore create mode 100644 crates/cpp/tests/meshless_resources/.gitignore create mode 100644 crates/cpp/tests/meshless_strings/.gitignore create mode 100644 crates/cpp/tests/native_mesh/component_b/.gitignore 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/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_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/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 From e6b0b688a8a07b3e5b45c2d3f32b81df1ad4c77d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Sep 2024 00:23:27 +0200 Subject: [PATCH 360/672] leak if necessary --- crates/cpp/src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 1394a13ff..e9d745d63 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2525,6 +2525,7 @@ struct FunctionBindgen<'a, 'b> { variant: AbiVariant, cabi_post: Option, needs_dealloc: bool, + leak_on_insertion: Option, } impl<'a, 'b> FunctionBindgen<'a, 'b> { @@ -2544,6 +2545,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { variant: AbiVariant::GuestImport, cabi_post: None, needs_dealloc: false, + leak_on_insertion: None, } } @@ -2955,6 +2957,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { ); uwrite!(self.src, "{}", body.0); uwriteln!(self.src, "auto e{tmp} = {};", body.1[0]); + if let Some(code) = self.leak_on_insertion.take() { + uwriteln!(self.src, "{code}"); + } // inplace construct uwriteln!(self.src, "{result}.initialize(i, std::move(e{tmp}));"); uwriteln!(self.src, "}}"); @@ -2964,6 +2969,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } 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.leak_on_insertion.replace(format!("_deallocate.push_back((void*){result}.leak());\n")); + } } else { results.push(format!("std::move({result})")); } From 85b7e733f6eb5747f8052c167f6ca52a7cbf4095 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Sep 2024 11:44:28 +0200 Subject: [PATCH 361/672] prefix import and export if both used in symmetric --- crates/core/src/lib.rs | 2 ++ crates/cpp/src/lib.rs | 8 +++-- crates/guest-rust/macro/src/lib.rs | 4 +-- crates/rust/src/interface.rs | 5 ++- crates/rust/src/lib.rs | 58 ++++++++++++++++++++++++++++++ src/bin/wit-bindgen.rs | 3 +- 6 files changed, 73 insertions(+), 7 deletions(-) diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index ff741763f..25e1f60cc 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -347,6 +347,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 diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index e9d745d63..36e28a3f0 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2963,14 +2963,18 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // 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 { + 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.leak_on_insertion.replace(format!("_deallocate.push_back((void*){result}.leak());\n")); + self.leak_on_insertion + .replace(format!("_deallocate.push_back((void*){result}.leak());\n")); } } else { results.push(format!("std::move({result})")); diff --git a/crates/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs index 644c6938d..9b40695fb 100644 --- a/crates/guest-rust/macro/src/lib.rs +++ b/crates/guest-rust/macro/src/lib.rs @@ -176,9 +176,6 @@ impl Parse for Config { parse_source(&source, &features).map_err(|err| anyhow_to_syn(call_site, err))?; let world = select_world(&resolve, &pkgs, world.as_deref()) .map_err(|e| anyhow_to_syn(call_site, e))?; - if opts.invert_direction { - resolve.invert_direction(world); - } Ok(Config { opts, resolve, @@ -276,6 +273,7 @@ impl Config { 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))?; diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 21ddf709b..e6bf9b11f 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -1062,11 +1062,14 @@ impl {async_support}::StreamPayload for {name} {{ let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); let (export_name, external_name) = if self.gen.opts.symmetric { let export_name = func.name.clone(); // item_name().to_owned(); - let external_name = make_external_symbol( + let mut external_name = make_external_symbol( &wasm_module_export_name.unwrap_or_default(), &func.name, AbiVariant::GuestImport, ); + if let Some(export_prefix) = self.gen.opts.export_prefix.as_ref() { + external_name.insert_str(0, export_prefix); + } (export_name, external_name) } else { let export_name = func.core_export_name(wasm_module_export_name.as_deref()); diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 4123b592c..cc8d9c282 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -24,6 +24,12 @@ struct InterfaceName { path: String, } +#[derive(Eq, Hash, PartialEq, Clone, Copy, Debug)] +enum Direction { + Import, + Export, +} + #[derive(Default)] struct RustWasm { types: Types, @@ -51,6 +57,9 @@ struct RustWasm { payload_results: HashMap, future_payloads_emitted: HashSet, stream_payloads_emitted: HashSet, + + // needed for symmetric disambiguation + interface_prefixes: HashMap<(Direction, WorldKey), String>, } #[derive(Default)] @@ -1022,6 +1031,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())); + } self.interface_last_seen_as_import.insert(id, false); let wasm_import_module = format!("[export]{}", resolve.name_world_key(name)); let mut gen = self.interface( @@ -1053,6 +1070,7 @@ impl WorldGenerator for RustWasm { let stub = gen.finish(); self.src.push_str(&stub); } + self.opts.export_prefix = old_prefix; Ok(()) } @@ -1206,6 +1224,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 { diff --git a/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs index 7637a244d..9f9e0a345 100644 --- a/src/bin/wit-bindgen.rs +++ b/src/bin/wit-bindgen.rs @@ -230,7 +230,8 @@ fn gen_world( } let (pkg, _files) = resolve.push_path(&opts.wit)?; resolve.add_future_and_stream_results(); - 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(()) From 5ccecf86b4e77b50a4785958a3c780d377d02195 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Sep 2024 13:22:30 +0200 Subject: [PATCH 362/672] implement import prefix --- .../symmetric_tests/test-rust-wasm/src/lib.rs | 16 +++++++++++++++- crates/guest-rust/macro/src/lib.rs | 2 +- crates/rust/src/bindgen.rs | 9 ++++++++- crates/rust/src/lib.rs | 9 ++++++++- 4 files changed, 32 insertions(+), 4 deletions(-) mode change 120000 => 100644 crates/cpp/tests/symmetric_tests/test-rust-wasm/src/lib.rs 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 deleted file mode 120000 index 3f12b4ccb..000000000 --- a/crates/cpp/tests/symmetric_tests/test-rust-wasm/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../crates/test-rust-wasm/src/lib.rs \ No newline at end of file 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/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs index 9b40695fb..b1ad116a6 100644 --- a/crates/guest-rust/macro/src/lib.rs +++ b/crates/guest-rust/macro/src/lib.rs @@ -172,7 +172,7 @@ impl Parse for Config { )])); } } - let (mut resolve, pkgs, files) = + let (resolve, pkgs, files) = parse_source(&source, &features).map_err(|err| anyhow_to_syn(call_site, err))?; let world = select_world(&resolve, &pkgs, world.as_deref()) .map_err(|e| anyhow_to_syn(call_site, e))?; diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 8bc47bfde..6c31edb19 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -903,7 +903,14 @@ impl Bindgen for FunctionBindgen<'_, '_> { module_prefix, .. } => { - let func = self.declare_import(module_prefix, name, &sig.params, &sig.results); + let module_prefix = if let Some(prefix) = self.gen.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() { diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index cc8d9c282..e52720fc0 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -60,6 +60,7 @@ struct RustWasm { // needed for symmetric disambiguation interface_prefixes: HashMap<(Direction, WorldKey), String>, + import_prefix: Option, } #[derive(Default)] @@ -986,6 +987,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()); + } self.interface_last_seen_as_import.insert(id, true); let wasm_import_module = resolve.name_world_key(name); let mut gen = self.interface( @@ -1003,7 +1010,7 @@ impl WorldGenerator for RustWasm { gen.generate_imports(resolve.interfaces[id].functions.values(), Some(name)); gen.finish_append_submodule(&snake, module_path); - + let _ = self.import_prefix.take(); Ok(()) } From fb670b161cc936cbee669dfba7c626e0305aa35e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Sep 2024 14:12:13 +0200 Subject: [PATCH 363/672] implement direction disambiguation for C++, fixes #28 --- crates/cpp/src/lib.rs | 77 ++++++++++++++++++++++++++++++++--- crates/cpp/tests/symmetric.rs | 6 ++- 2 files changed, 76 insertions(+), 7 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 36e28a3f0..bf40e24d3 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -96,6 +96,12 @@ struct SourceWithState { namespace: Vec, } +#[derive(Eq, Hash, PartialEq, Clone, Copy, Debug)] +enum Direction { + Import, + Export, +} + #[derive(Default)] struct Cpp { opts: Opts, @@ -113,6 +119,10 @@ struct Cpp { imported_interfaces: HashSet, user_class_files: HashMap, defined_types: HashSet<(Vec, String)>, + + // needed for symmetric disambiguation + interface_prefixes: HashMap<(Direction, WorldKey), String>, + import_prefix: Option, } #[derive(Default, Debug, Clone, Copy)] @@ -493,6 +503,13 @@ impl WorldGenerator for Cpp { 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); @@ -509,6 +526,7 @@ impl WorldGenerator for Cpp { } } self.finish_file(&namespace, store); + let _ = self.import_prefix.take(); Ok(()) } @@ -519,6 +537,14 @@ impl WorldGenerator for Cpp { 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 @@ -538,6 +564,7 @@ impl WorldGenerator for Cpp { } } self.finish_file(&namespace, store); + self.opts.export_prefix = old_prefix; Ok(()) } @@ -787,6 +814,30 @@ impl WorldGenerator for Cpp { ); 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) @@ -1072,10 +1123,14 @@ impl CppInterfaceGenerator<'_> { res.push('#'); res }); - uwriteln!( - self.gen.c_src.src, - r#"extern "C" __attribute__((__export_name__("{module_prefix}{func_name}")))"# - ); + 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 @@ -1091,6 +1146,9 @@ impl CppInterfaceGenerator<'_> { 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; @@ -3475,7 +3533,16 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { .gen .wasm_import_module .as_ref() - .map(|e| String::from(*module_prefix) + e) + .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\ diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index 3d306b901..aea67bfbb 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -116,7 +116,7 @@ fn tests( assert!(status.success()); for path in cpp.iter() { - let (resolve, world) = resolve_wit_dir(&dir); + 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)); @@ -131,7 +131,9 @@ fn tests( opts.new_api = true; } } - opts.build().generate(&resolve, world, &mut files).unwrap(); + 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); From c922f58cabc4ded100d0616d0969befb8dfb57ea Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Sep 2024 16:15:09 +0200 Subject: [PATCH 364/672] more correct interaction with Rust --- crates/cpp/helper-types/wit-guest.h | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/crates/cpp/helper-types/wit-guest.h b/crates/cpp/helper-types/wit-guest.h index a89e24d12..e1d44f8fa 100644 --- a/crates/cpp/helper-types/wit-guest.h +++ b/crates/cpp/helper-types/wit-guest.h @@ -17,6 +17,9 @@ namespace wit { 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 @@ -24,7 +27,7 @@ class string { string(string &&b) : data_(b.data_), length(b.length) { b.data_ = nullptr; } string &operator=(string const &) = delete; string &operator=(string &&b) { - if (data_) { + if (data_ && data_!=empty_ptr()) { free(const_cast(data_)); } data_ = b.data_; @@ -37,7 +40,7 @@ class string { size_t size() const { return length; } bool empty() const { return !length; } ~string() { - if (data_) { + if (data_ && data_!=empty_ptr()) { free(const_cast(data_)); } } @@ -52,6 +55,7 @@ class string { 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()); @@ -66,12 +70,14 @@ template 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_) { + if (data_ && length>0) { free(const_cast(data_)); } data_ = b.data_; @@ -80,7 +86,8 @@ template class vector { return *this; } vector(T *d, size_t l) : data_(d), length(l) {} - vector() : data_(nullptr), length() {} + // 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]; } @@ -88,13 +95,14 @@ template class vector { size_t size() const { return length; } bool empty() const { return !length; } ~vector() { - if (data_) { + if (data_ && length>0) { for (unsigned i=0;i allocate(size_t len) { + if (!len) return vector(empty_ptr(), 0); return vector((T*)malloc(sizeof(T)*len), len); } void initialize(size_t n, T&& elem) { @@ -103,7 +111,7 @@ template class vector { // leak the memory T* leak() { T*result = data_; data_ = nullptr; return result; } // typically called by post - static void drop_raw(void *ptr) { free(ptr); } + static void drop_raw(void *ptr) { if (ptr!=empty_ptr()) free(ptr); } wit::span get_view() const { return wit::span(data_, length); } wit::span get_const_view() const { return wit::span(data_, length); } template static vector from_view(wit::span const& a) { @@ -113,7 +121,6 @@ template class vector { } return result; } -// static vector from_view(wit::span const& a); }; /// @brief A Resource defined within the guest (guest side) From 05a6951b56a9a7a92b20a46953fea45756649339 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Sep 2024 16:48:49 +0200 Subject: [PATCH 365/672] half complete list fix --- crates/cpp/src/lib.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index bf40e24d3..78878f338 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2843,7 +2843,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if realloc.is_none() { results.push(ptr); } else { - if !self.gen.gen.opts.host_side() && !self.gen.gen.opts.symmetric { + 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); @@ -2929,10 +2932,17 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } else if self.gen.gen.opts.new_api && matches!(self.variant, AbiVariant::GuestExport) { - format!( - "wit::vector<{inner} const>(({inner}*)({}), {len}).get_view()", - operands[0] - ) + 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]) }; From 53d0579054162b9b26de7aa6d53e2d47586c254f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Sep 2024 23:14:38 +0200 Subject: [PATCH 366/672] fix lists in symmetric mode --- crates/cpp/src/lib.rs | 93 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 7 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 78878f338..1678da96e 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1534,6 +1534,73 @@ impl CppInterfaceGenerator<'_> { 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| Self::has_non_canonical_list2(resolve, &field.ty, maybe)), + TypeDefKind::Resource | TypeDefKind::Handle(_) | TypeDefKind::Flags(_) => false, + TypeDefKind::Tuple(t) => t + .types + .iter() + .any(|ty| Self::has_non_canonical_list2(resolve, ty, maybe)), + TypeDefKind::Variant(var) => var.cases.iter().any(|case| { + case.ty.as_ref().map_or(false, |ty| { + Self::has_non_canonical_list2(resolve, ty, maybe) + }) + }), + TypeDefKind::Enum(_) => false, + TypeDefKind::Option(ty) => Self::has_non_canonical_list2(resolve, ty, maybe), + TypeDefKind::Result(res) => { + res.ok.as_ref().map_or(false, |ty| { + Self::has_non_canonical_list2(resolve, ty, maybe) + }) || res.err.as_ref().map_or(false, |ty| { + Self::has_non_canonical_list2(resolve, ty, maybe) + }) + } + TypeDefKind::List(ty) => { + if maybe { + true + } else { + Self::has_non_canonical_list2(resolve, ty, true) + } + } + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Error => false, + TypeDefKind::Type(ty) => Self::has_non_canonical_list2(resolve, ty, maybe), + TypeDefKind::Unknown => false, + }, + } + } + + // fn has_non_canonical_list(resolve: &Resolve, results: &Results) -> bool { + // match results { + // Results::Named(vec) => vec + // .iter() + // .any(|(_, ty)| Self::has_non_canonical_list2(resolve, ty, false)), + // Results::Anon(one) => Self::has_non_canonical_list2(resolve, &one, false), + // } + // } + + fn has_non_canonical_list(resolve: &Resolve, args: &[(String, Type)]) -> bool { + args.iter() + .any(|(_, ty)| Self::has_non_canonical_list2(resolve, ty, false)) + } + fn generate_function( &mut self, func: &Function, @@ -1571,9 +1638,10 @@ impl CppInterfaceGenerator<'_> { if !matches!(special, SpecialMethod::Allocate) { self.gen.c_src.src.push_str("{\n"); let needs_dealloc = if self.gen.opts.new_api - && !self.gen.opts.symmetric && matches!(variant, AbiVariant::GuestExport) - && Self::needs_dealloc(self.resolve, &func.params) + && ((!self.gen.opts.symmetric && Self::needs_dealloc(self.resolve, &func.params)) + || (self.gen.opts.symmetric + && Self::has_non_canonical_list(self.resolve, &func.params))) { self.gen .c_src @@ -2114,6 +2182,7 @@ impl CppInterfaceGenerator<'_> { } fn declare_import2( + &self, module_name: &str, name: &str, args: &str, @@ -2121,7 +2190,11 @@ impl CppInterfaceGenerator<'_> { variant: AbiVariant, ) -> (String, String) { let extern_name = make_external_symbol(module_name, name, variant); - let import = format!("extern \"C\" __attribute__((import_module(\"{module_name}\")))\n __attribute__((import_name(\"{name}\")))\n {result} {extern_name}({args});\n"); + 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) } @@ -2149,7 +2222,7 @@ impl CppInterfaceGenerator<'_> { } else { AbiVariant::GuestImport }; - let (name, code) = Self::declare_import2(module_name, name, &args, result, variant); + let (name, code) = self.declare_import2(module_name, name, &args, result, variant); self.gen.extern_c_decls.push_str(&code); name } @@ -3014,6 +3087,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { && 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")); } @@ -3026,6 +3100,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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 @@ -3040,9 +3115,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } 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.leak_on_insertion - .replace(format!("_deallocate.push_back((void*){result}.leak());\n")); + 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})")); From e5e1f9f4d6afbe66c5c1a1c76c8aca34878096f4 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 15 Sep 2024 00:01:06 +0200 Subject: [PATCH 367/672] fix many symmetric tests --- crates/core/src/lib.rs | 1 + crates/core/src/symmetric.rs | 123 ++++++++++++++++++++++++++++++++ crates/cpp/src/lib.rs | 128 ++-------------------------------- crates/cpp/tests/symmetric.rs | 25 ++++--- crates/rust/src/bindgen.rs | 4 +- crates/rust/src/interface.rs | 9 ++- crates/rust/src/lib.rs | 1 + 7 files changed, 155 insertions(+), 136 deletions(-) create mode 100644 crates/core/src/symmetric.rs diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 25e1f60cc..f1f0eb66a 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 { diff --git a/crates/core/src/symmetric.rs b/crates/core/src/symmetric.rs new file mode 100644 index 000000000..1d60f0b2f --- /dev/null +++ b/crates/core/src/symmetric.rs @@ -0,0 +1,123 @@ +// 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(_) => todo!(), + 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::Error => false, + TypeDefKind::Type(tp) => needs_dealloc2(resolve, tp), + TypeDefKind::Unknown => false, + }, + } +} + +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(_) | TypeDefKind::Error => false, + TypeDefKind::Type(ty) => has_non_canonical_list2(resolve, ty, maybe), + TypeDefKind::Unknown => false, + }, + } +} + +// 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)) +} diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 1678da96e..362b8b2e9 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -9,7 +9,7 @@ use std::{ 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, uwrite, uwriteln, + make_external_component, make_external_symbol, symmetric, uwrite, uwriteln, wit_parser::{ Alignment, ArchitectureSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, Resolve, Results, SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, @@ -1480,127 +1480,6 @@ impl CppInterfaceGenerator<'_> { } } - // 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| Self::needs_dealloc2(resolve, &f.ty)), - TypeDefKind::Resource => false, - TypeDefKind::Handle(_) => false, - TypeDefKind::Flags(_) => false, - TypeDefKind::Tuple(t) => t.types.iter().any(|f| Self::needs_dealloc2(resolve, f)), - TypeDefKind::Variant(_) => todo!(), - TypeDefKind::Option(_) => todo!(), - TypeDefKind::Result(r) => { - r.ok.as_ref() - .map_or(false, |tp| Self::needs_dealloc2(resolve, tp)) - || r.err - .as_ref() - .map_or(false, |tp| Self::needs_dealloc2(resolve, tp)) - } - TypeDefKind::List(_l) => true, - TypeDefKind::Future(_) => todo!(), - TypeDefKind::Stream(_) => todo!(), - TypeDefKind::Error => false, - TypeDefKind::Type(tp) => Self::needs_dealloc2(resolve, tp), - TypeDefKind::Unknown => false, - }, - } - } - - fn needs_dealloc(resolve: &Resolve, args: &[(String, Type)]) -> bool { - for (_n, t) in args { - if Self::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| Self::has_non_canonical_list2(resolve, &field.ty, maybe)), - TypeDefKind::Resource | TypeDefKind::Handle(_) | TypeDefKind::Flags(_) => false, - TypeDefKind::Tuple(t) => t - .types - .iter() - .any(|ty| Self::has_non_canonical_list2(resolve, ty, maybe)), - TypeDefKind::Variant(var) => var.cases.iter().any(|case| { - case.ty.as_ref().map_or(false, |ty| { - Self::has_non_canonical_list2(resolve, ty, maybe) - }) - }), - TypeDefKind::Enum(_) => false, - TypeDefKind::Option(ty) => Self::has_non_canonical_list2(resolve, ty, maybe), - TypeDefKind::Result(res) => { - res.ok.as_ref().map_or(false, |ty| { - Self::has_non_canonical_list2(resolve, ty, maybe) - }) || res.err.as_ref().map_or(false, |ty| { - Self::has_non_canonical_list2(resolve, ty, maybe) - }) - } - TypeDefKind::List(ty) => { - if maybe { - true - } else { - Self::has_non_canonical_list2(resolve, ty, true) - } - } - TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Error => false, - TypeDefKind::Type(ty) => Self::has_non_canonical_list2(resolve, ty, maybe), - TypeDefKind::Unknown => false, - }, - } - } - - // fn has_non_canonical_list(resolve: &Resolve, results: &Results) -> bool { - // match results { - // Results::Named(vec) => vec - // .iter() - // .any(|(_, ty)| Self::has_non_canonical_list2(resolve, ty, false)), - // Results::Anon(one) => Self::has_non_canonical_list2(resolve, &one, false), - // } - // } - - fn has_non_canonical_list(resolve: &Resolve, args: &[(String, Type)]) -> bool { - args.iter() - .any(|(_, ty)| Self::has_non_canonical_list2(resolve, ty, false)) - } - fn generate_function( &mut self, func: &Function, @@ -1639,9 +1518,10 @@ impl CppInterfaceGenerator<'_> { 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 && Self::needs_dealloc(self.resolve, &func.params)) + && ((!self.gen.opts.symmetric + && symmetric::needs_dealloc(self.resolve, &func.params)) || (self.gen.opts.symmetric - && Self::has_non_canonical_list(self.resolve, &func.params))) + && symmetric::has_non_canonical_list(self.resolve, &func.params))) { self.gen .c_src diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index aea67bfbb..a021f9a67 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -238,17 +238,26 @@ fn symmetric_integration() -> io::Result<()> { tester_source_dir.push("tests"); tester_source_dir.push("symmetric_tests"); - let testcases = vec![ - "smoke", - "strings", - "numbers", - "flavorful", - "lists", - ]; + let default_testcases = vec!["smoke", "strings", "numbers", "flavorful", "lists"]; + let testcases: Vec = std::env::var_os("SYMMETRIC_TESTS").map_or_else( + || { + default_testcases + .iter() + .map(|s| s.to_string()) + .collect::>() + }, + |var| { + var.into_string() + .expect("UTF8 expected") + .split(',') + .map(|s| s.to_string()) + .collect() + }, + ); for dir_name in testcases { tests( - dir_name, + &dir_name, &out_dir, &toplevel, &source_files, diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 6c31edb19..e95ef033a 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -826,6 +826,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { "let ptr = {alloc}::alloc({layout}).cast::();\n", )); if self.gen.gen.opts.symmetric && self.gen.in_import { + assert!(self.gen.needs_deallocate); self.push_str(&format!( "if !ptr.is_null() {{ _deallocate.push((ptr, {layout})); }}\n" )); @@ -921,8 +922,9 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str("("); self.push_str(&operands.join(", ")); self.push_str(");\n"); - if self.gen.gen.opts.symmetric && self.gen.in_import { + if self.gen.needs_deallocate { self.push_str(&format!("for (ptr,layout) in _deallocate.drain(..) {{ _rt::alloc::dealloc(ptr, layout); }}\n")); + self.gen.needs_deallocate = false; } } diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index e6bf9b11f..34ae680b7 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -10,8 +10,8 @@ use std::fmt::Write as _; use std::mem; use wit_bindgen_core::abi::{self, AbiVariant, LiftLower}; use wit_bindgen_core::{ - dealias, make_external_component, make_external_symbol, 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 +25,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. @@ -867,7 +868,9 @@ impl {async_support}::StreamPayload for {name} {{ self.src.push_str("#[allow(unused_unsafe, clippy::all)]\n"); let params = self.print_signature(func, false, &sig, true); self.src.push_str("{\n"); - if self.gen.opts.symmetric { + if self.gen.opts.symmetric && symmetric::has_non_canonical_list(self.resolve, &func.params) + { + self.needs_deallocate = true; uwriteln!( self.src, "let mut _deallocate: Vec<(*mut u8, _rt::alloc::Layout)> = Vec::new();" diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index e52720fc0..41e9b5612 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -333,6 +333,7 @@ impl RustWasm { return_pointer_area_size: Default::default(), return_pointer_area_align: Default::default(), needs_runtime_module: false, + needs_deallocate: false, } } From 1935b688cd80edbaed28737478e1ebd7d14c7bf0 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 15 Sep 2024 00:44:11 +0200 Subject: [PATCH 368/672] flavorful example works --- crates/core/src/symmetric.rs | 54 ++++++++++++++++++++++++++++++++++++ crates/rust/src/bindgen.rs | 4 +++ crates/rust/src/interface.rs | 3 +- 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/crates/core/src/symmetric.rs b/crates/core/src/symmetric.rs index 1d60f0b2f..b15cdbaaa 100644 --- a/crates/core/src/symmetric.rs +++ b/crates/core/src/symmetric.rs @@ -121,3 +121,57 @@ pub fn has_non_canonical_list(resolve: &Resolve, args: &[(String, Type)]) -> boo 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(_) | TypeDefKind::Error => false, + TypeDefKind::Type(ty) => has_non_canonical_list_rust2(resolve, ty), + TypeDefKind::Unknown => false, + }, + } +} + +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/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index e95ef033a..81d6d03a1 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -826,7 +826,11 @@ impl Bindgen for FunctionBindgen<'_, '_> { "let ptr = {alloc}::alloc({layout}).cast::();\n", )); if self.gen.gen.opts.symmetric && self.gen.in_import { + //if !self.gen.needs_deallocate { + // self.push_str("// "); + //} else { assert!(self.gen.needs_deallocate); + //} self.push_str(&format!( "if !ptr.is_null() {{ _deallocate.push((ptr, {layout})); }}\n" )); diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 34ae680b7..135ee617e 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -868,7 +868,8 @@ impl {async_support}::StreamPayload for {name} {{ self.src.push_str("#[allow(unused_unsafe, clippy::all)]\n"); let params = self.print_signature(func, false, &sig, true); self.src.push_str("{\n"); - if self.gen.opts.symmetric && symmetric::has_non_canonical_list(self.resolve, &func.params) + if self.gen.opts.symmetric + && symmetric::has_non_canonical_list_rust(self.resolve, &func.params) { self.needs_deallocate = true; uwriteln!( From 2ad3cc33c76641ec16aee46068b15f678e29b05c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 15 Sep 2024 23:19:45 +0200 Subject: [PATCH 369/672] options test (work in progress) --- crates/core/src/symmetric.rs | 2 +- tests/runtime/options/wasm.new.cpp | 56 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/runtime/options/wasm.new.cpp diff --git a/crates/core/src/symmetric.rs b/crates/core/src/symmetric.rs index b15cdbaaa..3cc7c52e8 100644 --- a/crates/core/src/symmetric.rs +++ b/crates/core/src/symmetric.rs @@ -26,7 +26,7 @@ fn needs_dealloc2(resolve: &Resolve, tp: &Type) -> bool { TypeDefKind::Flags(_) => false, TypeDefKind::Tuple(t) => t.types.iter().any(|f| needs_dealloc2(resolve, f)), TypeDefKind::Variant(_) => todo!(), - TypeDefKind::Option(_) => todo!(), + TypeDefKind::Option(tp) => needs_dealloc2(resolve, tp), TypeDefKind::Result(r) => { r.ok.as_ref() .map_or(false, |tp| needs_dealloc2(resolve, tp)) diff --git a/tests/runtime/options/wasm.new.cpp b/tests/runtime/options/wasm.new.cpp new file mode 100644 index 000000000..372fb144d --- /dev/null +++ b/tests/runtime/options/wasm.new.cpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include + +template +static bool equal(T const& a, T const& b) { + return a==b; +} +//template +static bool equal(std::optional const& a, std::optional const& b) { + if (a.has_value() != b.has_value()) return false; + if (a.has_value()) { + return equal(*a, *b); + } + return true; +} + +void exports::options::TestImports() { + using namespace test::options::test; + + OptionNoneParam(std::optional()); + OptionSomeParam(std::optional("foo")); + assert(!OptionNoneResult()); + assert(equal(OptionSomeResult(), std::optional("foo"))); + assert(equal(OptionRoundtrip(std::optional("foo")), std::optional("foo"))); + assert(equal(DoubleOptionRoundtrip(std::optional>(std::optional(42))), std::optional>(std::optional(42)))); + assert(equal(DoubleOptionRoundtrip(std::optional>(std::optional())), std::optional>(std::optional()))); + assert(equal(DoubleOptionRoundtrip(std::optional>()), std::optional>())); +} + +void exports::test::options::test::OptionNoneParam(std::optional a) +{ + assert(!a.has_value()); +} + +std::optional exports::test::options::test::OptionNoneResult() { + return std::optional(); +} + +void exports::test::options::test::OptionSomeParam(std::optional a) { + assert(equal(a, std::optional("foo"))); +} + +std::optional exports::test::options::test::OptionSomeResult() { + return std::optional(wit::string::from_view("foo")); +} + +std::optional exports::test::options::test::OptionRoundtrip(std::optional a) { + if (!a.has_value()) return std::optional(); + return std::optional(wit::string::from_view(*a)); +} + +std::optional> exports::test::options::test::DoubleOptionRoundtrip(std::optional> a) { + return a; +} From fa5993fa3f620429e1ea7e520b1368b4591f7f38 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 15 Sep 2024 23:41:13 +0200 Subject: [PATCH 370/672] options test --- crates/cpp/tests/symmetric.rs | 2 +- crates/cpp/tests/symmetric_tests/options.rs | 57 +++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 crates/cpp/tests/symmetric_tests/options.rs diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index a021f9a67..9b709b12f 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -238,7 +238,7 @@ fn symmetric_integration() -> io::Result<()> { tester_source_dir.push("tests"); tester_source_dir.push("symmetric_tests"); - let default_testcases = vec!["smoke", "strings", "numbers", "flavorful", "lists"]; + let default_testcases = vec!["smoke", "strings", "numbers", "flavorful", "lists", "options"]; let testcases: Vec = std::env::var_os("SYMMETRIC_TESTS").map_or_else( || { default_testcases 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) { + assert!(a.is_none()); + } + + fn option_none_result() -> Option { + None + } + + fn option_some_param(a: Option) { + assert_eq!(a, Some("foo".to_string())); + } + + fn option_some_result() -> Option { + Some("foo".to_string()) + } + + fn option_roundtrip(a: Option) -> Option { + a + } + + fn double_option_roundtrip(a: Option>) -> Option> { + 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() }; + }; + } +} From 6f8cd9bec1fd96473d3ed74b3133b221769c4506 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 17 Sep 2024 21:54:52 +0200 Subject: [PATCH 371/672] fix option test --- crates/cpp/src/lib.rs | 26 +++++++++++++++++++------- crates/cpp/tests/symmetric.rs | 9 ++++++++- tests/runtime/options/wasm.new.cpp | 6 ++++-- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 362b8b2e9..a1d89cd49 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -3202,7 +3202,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } abi::Instruction::VariantPayloadName => { let name = format!("payload{}", self.tmp()); - results.push(name.clone()); //format!("*{}", name)); + results.push(name.clone()); self.payloads.push(name); } abi::Instruction::VariantLower { @@ -3352,9 +3352,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } let op0 = &operands[0]; - let ty = self - .gen - .type_name(payload, &self.namespace, Flavor::InStruct); + 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!( @@ -3373,9 +3378,16 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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::InStruct); + .type_name(*payload, &self.namespace, flavor); let full_type = format!("std::optional<{type_name}>"); let op0 = &operands[0]; @@ -3386,11 +3398,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { "{full_type} {resultname}; if ({op0}) {{ {some} - {resultname}.emplace(std::move({})); + {resultname}.emplace({}); }}", some_results[0] ); - results.push(resultname); + results.push(format!("std::move({resultname})")); } abi::Instruction::ResultLower { results: result_types, diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index 9b709b12f..cf038fbc6 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -238,7 +238,14 @@ fn symmetric_integration() -> io::Result<()> { tester_source_dir.push("tests"); tester_source_dir.push("symmetric_tests"); - let default_testcases = vec!["smoke", "strings", "numbers", "flavorful", "lists", "options"]; + let default_testcases = vec![ + "smoke", + "strings", + "numbers", + "flavorful", + "lists", + "options", + ]; let testcases: Vec = std::env::var_os("SYMMETRIC_TESTS").map_or_else( || { default_testcases diff --git a/tests/runtime/options/wasm.new.cpp b/tests/runtime/options/wasm.new.cpp index 372fb144d..8925a3f74 100644 --- a/tests/runtime/options/wasm.new.cpp +++ b/tests/runtime/options/wasm.new.cpp @@ -7,11 +7,13 @@ template static bool equal(T const& a, T const& b) { return a==b; } -//template +static bool equal(wit::string const& a, std::string const& b) { + return a.get_view() == std::string_view(b); +} static bool equal(std::optional const& a, std::optional const& b) { if (a.has_value() != b.has_value()) return false; if (a.has_value()) { - return equal(*a, *b); + return equal(a.value(), b.value()); } return true; } From 844f7fa790964069b1a6677103bbe09d2c778157 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 17 Sep 2024 22:33:05 +0200 Subject: [PATCH 372/672] the new API works better --- crates/cpp/src/lib.rs | 21 ++++++++++++++++++++- crates/cpp/tests/codegen.rs | 7 ++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index a1d89cd49..622cdfe40 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1527,6 +1527,7 @@ impl CppInterfaceGenerator<'_> { .c_src .src .push_str("std::vector _deallocate;\n"); + self.gen.dependencies.needs_vector = true; true } else { false @@ -2648,13 +2649,31 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } + 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, // correct? + Type::Id(id) => self.has_resources(id), + } + } 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(_) => todo!(), + TypeDefKind::Tuple(t) => t.types.iter().any(|ty| self.has_resources2(ty)), TypeDefKind::Variant(_) => todo!(), TypeDefKind::Enum(_) => false, TypeDefKind::Option(_) => todo!(), diff --git a/crates/cpp/tests/codegen.rs b/crates/cpp/tests/codegen.rs index 91f876738..398003216 100644 --- a/crates/cpp/tests/codegen.rs +++ b/crates/cpp/tests/codegen.rs @@ -50,7 +50,7 @@ macro_rules! codegen_test { "return-resource-from-export", "same-names5", "simple-http", - "simple-lists", + // "simple-lists", "use-across-interfaces", "variants", "variants-unioning-types", @@ -68,8 +68,9 @@ macro_rules! codegen_test { "cpp", $test.as_ref(), |resolve, world, files| { - wit_bindgen_cpp::Opts::default() - .build() + let mut opts = wit_bindgen_cpp::Opts::default(); + opts.new_api = true; + opts.build() .generate(resolve, world, files) .unwrap() }, From c126746c4f03cc2b0f876363a3386b31e30052e3 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 17 Sep 2024 23:09:08 +0200 Subject: [PATCH 373/672] more tests --- crates/cpp/tests/symmetric.rs | 2 + .../tests/symmetric_tests/many_arguments.rs | 92 ++++++++++++++++++ crates/cpp/tests/symmetric_tests/records.rs | 94 +++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 crates/cpp/tests/symmetric_tests/many_arguments.rs create mode 100644 crates/cpp/tests/symmetric_tests/records.rs diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index cf038fbc6..079a31195 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -245,6 +245,8 @@ fn symmetric_integration() -> io::Result<()> { "flavorful", "lists", "options", + "many_arguments", + "records", ]; let testcases: Vec = std::env::var_os("SYMMETRIC_TESTS").map_or_else( || { 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/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() }; + }; + } +} From 47c935b132e6305d9a766736599ec4b8def4b700 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 17 Sep 2024 23:46:35 +0200 Subject: [PATCH 374/672] working records test --- tests/runtime/records/wasm.new.cpp | 75 ++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 tests/runtime/records/wasm.new.cpp 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 +#include + +template +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(4, 5))); + + assert(equal(SwapTuple(std::tuple(1, 2)), std::tuple(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(1)), std::tuple(1))); +} + +std::tuple exports::test::records::test::MultipleResults() { + return std::tuple(100, 200); +} + +std::tuple exports::test::records::test::SwapTuple(std::tuple a) { + return std::tuple(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 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 exports::test::records::test::Tuple1(std::tuple a) { + return std::tuple(std::get<0>(a)); +} From 5eb93d9932d6a1014b079024c3dcf02a3cedadd1 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 17 Sep 2024 23:53:42 +0200 Subject: [PATCH 375/672] another test ready --- tests/runtime/many_arguments/wasm.new.cpp | 46 +++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/runtime/many_arguments/wasm.new.cpp 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 +#include + +template +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 + ); +} From add926f27844f9cce209fe5c7580a82c630691da Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 18 Sep 2024 00:18:38 +0200 Subject: [PATCH 376/672] results test --- crates/cpp/tests/symmetric.rs | 9 +- crates/cpp/tests/symmetric_tests/results.rs | 136 ++++++++++++++++++++ 2 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 crates/cpp/tests/symmetric_tests/results.rs diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index 079a31195..226c60359 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -239,14 +239,15 @@ fn symmetric_integration() -> io::Result<()> { tester_source_dir.push("symmetric_tests"); let default_testcases = vec![ - "smoke", - "strings", - "numbers", "flavorful", "lists", - "options", "many_arguments", + "numbers", + "options", "records", + "results", + "smoke", + "strings", ]; let testcases: Vec = std::env::var_os("SYMMETRIC_TESTS").map_or_else( || { 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 { + if a == 0.0 { + Err("zero".to_owned()) + } else { + Ok(a) + } + } + + fn enum_error(a: f32) -> Result { + if a == 0.0 { + Err(imports::E::A) + } else { + Ok(a) + } + } + + fn record_error(a: f32) -> Result { + 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 { + 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 { + if a == 0 { + Err(()) + } else if a == 1 { + Ok(42) + } else { + Ok(a) + } + } + + fn double_error(a: u32) -> Result, 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()) }; + }; + } +} From 91b69aba325d5e1c8bf78c71b119c7e9ee0e2d67 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 18 Sep 2024 00:54:16 +0200 Subject: [PATCH 377/672] work in progress --- crates/cpp/test_headers/expected | 4 +++ tests/runtime/results/wasm.new.cpp | 56 ++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 tests/runtime/results/wasm.new.cpp diff --git a/crates/cpp/test_headers/expected b/crates/cpp/test_headers/expected index 66b06b147..fc3df0eed 100644 --- a/crates/cpp/test_headers/expected +++ b/crates/cpp/test_headers/expected @@ -1,3 +1,7 @@ +// work around g++ system header limitation +#define unexpected unexpected_old +#include +#undef unexpected #include namespace std { diff --git a/tests/runtime/results/wasm.new.cpp b/tests/runtime/results/wasm.new.cpp new file mode 100644 index 000000000..a432bdbc4 --- /dev/null +++ b/tests/runtime/results/wasm.new.cpp @@ -0,0 +1,56 @@ +#include +#include + +template +bool equal(T const&a, T const&b) { + return a==b; +} + +std::expected exports::test::results::test::StringError(float a) { + return ::test::results::test::StringError(a); +} + +std::expected 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 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 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 exports::test::results::test::EmptyError(uint32_t a) { + return ::test::results::test::EmptyError(a); +} + +std::expected, wit::string> exports::test::results::test::DoubleError(uint32_t a) { + return ::test::results::test::DoubleError(a) +} From 516265b61c294a5068f78495945c8f3a2e13248d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 19 Sep 2024 23:31:00 +0200 Subject: [PATCH 378/672] fix merge mistake --- crates/csharp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/csharp/src/lib.rs b/crates/csharp/src/lib.rs index 44bc8a2a7..c6ca531d8 100644 --- a/crates/csharp/src/lib.rs +++ b/crates/csharp/src/lib.rs @@ -2192,7 +2192,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::F64Load { offset } => results.push(format!("BitConverter.ToDouble(new Span((void*)({} + {offset}), 8))",operands[0],offset=offset.size_wasm32())), Instruction::I32Store { offset } | Instruction::PointerStore { offset } - | Instruction::LengthStore { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 4), unchecked((int){}));", operands[1], operands[0],offset=offset.size_wasm32()), + | Instruction::LengthStore { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 4), {});", operands[1], operands[0],offset=offset.size_wasm32()), Instruction::I32Store8 { offset } => uwriteln!(self.src, "*(byte*)({} + {offset}) = (byte){};", operands[1], operands[0],offset=offset.size_wasm32()), Instruction::I32Store16 { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 2), (short){});", operands[1], operands[0],offset=offset.size_wasm32()), Instruction::I64Store { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 8), unchecked((long){}));", operands[1], operands[0],offset=offset.size_wasm32()), From 57ab9c0e361843179171daa1a6071858ace99d8b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 7 Oct 2024 22:08:33 +0200 Subject: [PATCH 379/672] post merge fixes --- crates/moonbit/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/moonbit/src/lib.rs b/crates/moonbit/src/lib.rs index ec2946ee5..b89370a65 100644 --- a/crates/moonbit/src/lib.rs +++ b/crates/moonbit/src/lib.rs @@ -577,13 +577,13 @@ impl WorldGenerator for MoonBit { }} " ); - if self.return_area_size != 0 { + if !self.return_area_size.is_empty() { uwriteln!( &mut body, " let return_area : Int = {ffi_qualifier}malloc({}) ", - self.return_area_size, + self.return_area_size.size_wasm32(), ); } files.push(&format!("{EXPORT_DIR}/ffi.mbt"), indent(&body).as_bytes()); From 835c2132f3644e349bc285426e3a5094d20c1b1a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 9 Oct 2024 00:16:10 +0200 Subject: [PATCH 380/672] implement symmetric tests as a single workspace no multiple re-downloading of dependencies any more --- Cargo.lock | 135 ++++++++++++++-------------------- crates/cpp/tests/symmetric.rs | 121 +++++++++++++++++++++++------- 2 files changed, 149 insertions(+), 107 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 23ca73989..d84c8fe74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,7 +106,7 @@ dependencies = [ name = "anyhow" version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" dependencies = [ "backtrace", ] @@ -478,7 +478,7 @@ dependencies = [ "itertools", "log", "smallvec", - "wasmparser 0.217.0", + "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmtime-types", ] @@ -1459,7 +1459,7 @@ dependencies = [ "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-core", "wit-component", - "wit-parser 0.218.0", + "wit-parser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -1738,7 +1738,16 @@ version = "0.217.0" source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#f85ac59e0670f0f80f624b5a72effaaa261542ee" dependencies = [ "leb128", - "wasmparser 0.217.0", + "wasmparser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", +] + +[[package]] +name = "wasm-encoder" +version = "0.218.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22b896fa8ceb71091ace9bcb81e853f54043183a1c9667cf93422c40252ffa0a" +dependencies = [ + "leb128", ] [[package]] @@ -1753,18 +1762,18 @@ dependencies = [ "serde_json", "spdx", "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", - "wasmparser 0.217.0", + "wasmparser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] name = "wasmparser" -version = "0.215.0" +version = "0.217.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fbde0881f24199b81cf49b6ff8f9c145ac8eb1b7fc439adb5c099734f7d90e" +checksum = "ca917a21307d3adf2b9857b94dd05ebf8496bdcff4437a9b9fb3899d3e6c74e7" dependencies = [ "ahash", "bitflags", - "hashbrown 0.14.5", + "hashbrown", "indexmap", "semver", "serde", @@ -1783,20 +1792,6 @@ dependencies = [ "serde", ] -[[package]] -name = "wasmparser" -version = "0.218.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09e46c7fceceaa72b2dd1a8a137ea7fd8f93dfaa69806010a709918e496c5dc" -dependencies = [ - "ahash", - "bitflags", - "hashbrown", - "indexmap", - "semver", - "serde", -] - [[package]] name = "wasmprinter" version = "0.217.0" @@ -1805,7 +1800,7 @@ checksum = "50dc568b3e0d47e8f96ea547c90790cfa783f0205160c40de894a427114185ce" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.217.0", + "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1846,8 +1841,8 @@ dependencies = [ "smallvec", "sptr", "target-lexicon", - "wasm-encoder 0.217.0", - "wasmparser 0.217.0", + "wasm-encoder 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmtime-asm-macros", "wasmtime-cache", "wasmtime-component-macro", @@ -1860,7 +1855,7 @@ dependencies = [ "wasmtime-slab", "wasmtime-versioned-export-macros", "wasmtime-winch", - "wat 1.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wat 1.218.0", "windows-sys 0.52.0", ] @@ -1905,7 +1900,7 @@ dependencies = [ "syn", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser 0.217.0", + "wit-parser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1934,7 +1929,7 @@ dependencies = [ "smallvec", "target-lexicon", "thiserror", - "wasmparser 0.217.0", + "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmtime-environ", "wasmtime-versioned-export-macros", ] @@ -1959,8 +1954,8 @@ dependencies = [ "serde", "serde_derive", "target-lexicon", - "wasm-encoder 0.217.0", - "wasmparser 0.217.0", + "wasm-encoder 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmprinter", "wasmtime-component-util", "wasmtime-types", @@ -2022,7 +2017,7 @@ dependencies = [ "serde", "serde_derive", "smallvec", - "wasmparser 0.217.0", + "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2078,7 +2073,7 @@ dependencies = [ "gimli 0.29.0", "object", "target-lexicon", - "wasmparser 0.217.0", + "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmtime-cranelift", "wasmtime-environ", "winch-codegen", @@ -2093,7 +2088,7 @@ dependencies = [ "anyhow", "heck 0.4.1", "indexmap", - "wit-parser 0.217.0", + "wit-parser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2107,44 +2102,44 @@ dependencies = [ [[package]] name = "wast" -version = "218.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a53cd1f0fa505df97557e36a58bddb8296e2fcdcd089529545ebfdb18a1b9d7" +version = "217.0.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#f85ac59e0670f0f80f624b5a72effaaa261542ee" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] name = "wast" -version = "217.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#f85ac59e0670f0f80f624b5a72effaaa261542ee" +version = "218.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a53cd1f0fa505df97557e36a58bddb8296e2fcdcd089529545ebfdb18a1b9d7" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.218.0", ] [[package]] name = "wat" -version = "1.218.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f87f8e14e776762e07927c27c2054d2cf678aab9aae2d431a79b3e31e4dd391" +version = "1.217.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#f85ac59e0670f0f80f624b5a72effaaa261542ee" dependencies = [ - "wast 217.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wast 217.0.0", ] [[package]] name = "wat" -version = "1.217.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#f85ac59e0670f0f80f624b5a72effaaa261542ee" +version = "1.218.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f87f8e14e776762e07927c27c2054d2cf678aab9aae2d431a79b3e31e4dd391" dependencies = [ - "wast 217.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wast 218.0.0", ] [[package]] @@ -2232,7 +2227,7 @@ dependencies = [ "regalloc2", "smallvec", "target-lexicon", - "wasmparser 0.217.0", + "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", "wasmtime-cranelift", "wasmtime-environ", ] @@ -2378,7 +2373,7 @@ dependencies = [ "wasm-metadata", "wit-bindgen-core", "wit-component", - "wit-parser 0.218.0", + "wit-parser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2390,7 +2385,7 @@ dependencies = [ "heck 0.5.0", "test-artifacts", "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", - "wasmparser 0.217.0", + "wasmparser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasmtime", "wasmtime-wasi", "wit-bindgen-bridge", @@ -2404,7 +2399,7 @@ dependencies = [ "wit-bindgen-rust", "wit-bindgen-teavm-java", "wit-component", - "wit-parser 0.218.0", + "wit-parser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2413,7 +2408,7 @@ version = "0.33.0" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser 0.218.0", + "wit-parser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2442,10 +2437,10 @@ dependencies = [ "test-helpers", "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", - "wasmparser 0.218.0", + "wasmparser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-core", "wit-component", - "wit-parser 0.218.0", + "wit-parser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2551,16 +2546,16 @@ dependencies = [ "serde_json", "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", - "wasmparser 0.217.0", - "wat 1.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", - "wit-parser 0.217.0", + "wasmparser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wat 1.217.0", + "wit-parser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] name = "wit-parser" -version = "0.215.0" +version = "0.217.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "935a97eaffd57c3b413aa510f8f0b550a4a9fe7d59e79cd8b89a83dcb860321f" +checksum = "fb893dcd6d370cfdf19a0d9adfcd403efb8e544e1a0ea3a8b81a21fe392eaa78" dependencies = [ "anyhow", "id-arena", @@ -2571,7 +2566,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.215.0", + "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2588,25 +2583,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.217.0", -] - -[[package]] -name = "wit-parser" -version = "0.218.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d3d1066ab761b115f97fef2b191090faabcb0f37b555b758d3caf42d4ed9e55" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser 0.218.0", + "wasmparser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index 226c60359..9842346e1 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -7,39 +7,36 @@ use std::{ use wit_bindgen_core::wit_parser::{Resolve, WorldId}; -fn tests( +fn tester_source_file( + dir_name: &str, + tester_source_dir: &PathBuf, +) -> Option { + 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<()> { - // modelled after wit-bindgen/tests/runtime/main.rs - let mut tester_source_file = tester_source_dir.clone(); - tester_source_file.push(&format!("{dir_name}.rs")); - if !std::fs::exists(&tester_source_file)? { + 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 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(); @@ -63,6 +60,10 @@ fn tests( 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)?; @@ -70,15 +71,10 @@ fn tests( let mut original = dir.clone(); original.push("wasm.rs"); std::os::unix::fs::symlink(original, filename)?; - drop(testee_cargo); let tester_cargo = format!( - "[workspace]\n\ - members = [ \"rust\" ]\n\ - resolver = \"2\"\n\ - \n\ - [package]\n\ - name = \"tester\"\n\ + "[package]\n\ + name = \"tester-{dir_name}\"\n\ publish = false\n\ edition = \"2021\"\n\ \n\ @@ -92,11 +88,62 @@ fn tests( 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) @@ -264,7 +311,25 @@ fn symmetric_integration() -> io::Result<()> { .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, From 3ba55728e4b90f9f6edd79708745406f7314ad86 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 22 Mar 2024 17:33:31 -0600 Subject: [PATCH 381/672] first draft of async lift/lower for Rust generator Signed-off-by: Joel Dice more work on async lift/lower Signed-off-by: Joel Dice emit callback (and no post-return) for async exports Signed-off-by: Joel Dice flesh out async support Signed-off-by: Joel Dice flesh out async_support.rs Signed-off-by: Joel Dice add future and stream support Signed-off-by: Joel Dice support `task.wait` Signed-off-by: Joel Dice compile fixes Signed-off-by: Joel Dice switch back to Git for wasm-tools deps Signed-off-by: Joel Dice snapshot Signed-off-by: Joel Dice snapshot Signed-off-by: Joel Dice snapshot Signed-off-by: Joel Dice snapshot Signed-off-by: Joel Dice remove cargo dep overrides Signed-off-by: Joel Dice snapshot Signed-off-by: Joel Dice snapshot Signed-off-by: Joel Dice snapshot Signed-off-by: Joel Dice snapshot Signed-off-by: Joel Dice snapshot Signed-off-by: Joel Dice snapshot Signed-off-by: Joel Dice snapshot Signed-off-by: Joel Dice snapshot Signed-off-by: Joel Dice --- Cargo.lock | 95 ++- Cargo.toml | 10 +- crates/c/src/lib.rs | 6 +- crates/core/src/abi.rs | 403 ++++++++++--- crates/core/src/lib.rs | 5 +- crates/core/src/types.rs | 8 +- crates/csharp/src/lib.rs | 9 +- crates/guest-rust/macro/src/lib.rs | 81 ++- crates/rust/src/async_support.rs | 918 +++++++++++++++++++++++++++++ crates/rust/src/bindgen.rs | 165 +++++- crates/rust/src/interface.rs | 640 ++++++++++++++++++-- crates/rust/src/lib.rs | 72 ++- crates/teavm-java/src/lib.rs | 9 +- 13 files changed, 2193 insertions(+), 228 deletions(-) create mode 100644 crates/rust/src/async_support.rs diff --git a/Cargo.lock b/Cargo.lock index 6ce2eb66e..7252c1289 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -107,6 +107,9 @@ name = "anyhow" version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" +dependencies = [ + "backtrace", +] [[package]] name = "arbitrary" @@ -1459,10 +1462,10 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.219.0", + "wasm-encoder 0.219.1", "wit-bindgen-core", "wit-component", - "wit-parser 0.219.0", + "wit-parser 0.219.1", ] [[package]] @@ -1630,6 +1633,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -1745,11 +1754,17 @@ dependencies = [ "wasmparser 0.219.0", ] +[[package]] +name = "wasm-encoder" +version = "0.219.1" +dependencies = [ + "leb128", + "wasmparser 0.219.1", +] + [[package]] name = "wasm-metadata" -version = "0.219.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96132fe00dd17d092d2be289eeed5a0a68ad3cf30b68e8875bc953b96f55f0be" +version = "0.219.1" dependencies = [ "anyhow", "indexmap", @@ -1757,8 +1772,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.219.0", - "wasmparser 0.219.0", + "wasm-encoder 0.219.1", + "wasmparser 0.219.1", ] [[package]] @@ -1780,6 +1795,14 @@ name = "wasmparser" version = "0.219.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "324b4e56d24439495b88cd81439dad5e97f3c7b1eedc3c7e10455ed1e045e9a2" +dependencies = [ + "bitflags", + "indexmap", +] + +[[package]] +name = "wasmparser" +version = "0.219.1" dependencies = [ "ahash", "bitflags", @@ -1852,7 +1875,7 @@ dependencies = [ "wasmtime-slab", "wasmtime-versioned-export-macros", "wasmtime-winch", - "wat", + "wat 1.219.0", "windows-sys 0.52.0", ] @@ -2106,10 +2129,21 @@ dependencies = [ "bumpalo", "leb128", "memchr", - "unicode-width", + "unicode-width 0.1.14", "wasm-encoder 0.219.0", ] +[[package]] +name = "wast" +version = "219.0.1" +dependencies = [ + "bumpalo", + "leb128", + "memchr", + "unicode-width 0.2.0", + "wasm-encoder 0.219.1", +] + [[package]] name = "wat" version = "1.219.0" @@ -2119,6 +2153,13 @@ dependencies = [ "wast 219.0.0", ] +[[package]] +name = "wat" +version = "1.219.1" +dependencies = [ + "wast 219.0.1", +] + [[package]] name = "wiggle" version = "25.0.1" @@ -2335,11 +2376,11 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.219.0", + "wasm-encoder 0.219.1", "wasm-metadata", "wit-bindgen-core", "wit-component", - "wit-parser 0.219.0", + "wit-parser 0.219.1", ] [[package]] @@ -2350,8 +2391,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.219.0", - "wasmparser 0.219.0", + "wasm-encoder 0.219.1", + "wasmparser 0.219.1", "wasmtime", "wasmtime-wasi", "wit-bindgen-c", @@ -2363,7 +2404,7 @@ dependencies = [ "wit-bindgen-rust", "wit-bindgen-teavm-java", "wit-component", - "wit-parser 0.219.0", + "wit-parser 0.219.1", ] [[package]] @@ -2372,7 +2413,7 @@ version = "0.34.0" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser 0.219.0", + "wit-parser 0.219.1", ] [[package]] @@ -2384,12 +2425,12 @@ dependencies = [ "heck 0.5.0", "indexmap", "test-helpers", - "wasm-encoder 0.219.0", + "wasm-encoder 0.219.1", "wasm-metadata", - "wasmparser 0.219.0", + "wasmparser 0.219.1", "wit-bindgen-core", "wit-component", - "wit-parser 0.219.0", + "wit-parser 0.219.1", ] [[package]] @@ -2483,9 +2524,7 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.219.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99a76111c20444a814019de20499d30940ecd219b9512ee296f034a5edb18a2d" +version = "0.219.1" dependencies = [ "anyhow", "bitflags", @@ -2494,11 +2533,11 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.219.0", + "wasm-encoder 0.219.1", "wasm-metadata", - "wasmparser 0.219.0", - "wat", - "wit-parser 0.219.0", + "wasmparser 0.219.1", + "wat 1.219.1", + "wit-parser 0.219.1", ] [[package]] @@ -2521,9 +2560,7 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.219.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23102e180c0c464f36e293d31a27b524e3ece930d7b5527d2f33f9d2c963de64" +version = "0.219.1" dependencies = [ "anyhow", "id-arena", @@ -2534,7 +2571,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.219.0", + "wasmparser 0.219.1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 75b172c66..4b7683548 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,11 +32,11 @@ indexmap = "2.0.0" prettyplease = "0.2.20" syn = { version = "2.0", features = ["printing"] } -wasmparser = "0.219.0" -wasm-encoder = "0.219.0" -wasm-metadata = "0.219.0" -wit-parser = "0.219.0" -wit-component = "0.219.0" +wasmparser = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wasm-encoder = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wasm-metadata = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wit-parser = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wit-component = { git = "https://github.com/dicej/wasm-tools", branch = "async" } wit-bindgen-core = { path = 'crates/core', version = '0.34.0' } wit-bindgen-c = { path = 'crates/c', version = '0.34.0' } diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 99eb5c689..e79f389ac 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -1750,6 +1750,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut f, + false, ); let FunctionBindgen { @@ -1822,6 +1823,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut f, + false, ); let FunctionBindgen { src, .. } = f; self.src.c_adapters(&src); @@ -1852,7 +1854,7 @@ impl InterfaceGenerator<'_> { let mut f = FunctionBindgen::new(self, c_sig, &import_name); f.params = params; - abi::post_return(f.gen.resolve, func, &mut f); + abi::post_return(f.gen.resolve, func, &mut f, false); let FunctionBindgen { src, .. } = f; self.src.c_fns(&src); self.src.c_fns("}\n"); @@ -2753,7 +2755,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src.push_str(");\n"); } - Instruction::CallInterface { func } => { + Instruction::CallInterface { func, .. } => { let mut args = String::new(); for (i, (op, (byref, _))) in operands.iter().zip(&self.sig.params).enumerate() { if i > 0 { diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index f0175afaa..17eb1cc10 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -1,7 +1,7 @@ pub use wit_parser::abi::{AbiVariant, WasmSignature, WasmType}; use wit_parser::{ - Enum, Flags, FlagsRepr, Function, Handle, Int, Record, Resolve, Result_, Results, SizeAlign, - Tuple, Type, TypeDefKind, TypeId, Variant, + ElementInfo, Enum, Flags, FlagsRepr, Function, Handle, Int, Record, Resolve, Result_, Results, + SizeAlign, Tuple, Type, TypeDefKind, TypeId, Variant, }; // Helper macro for defining instructions without having to have tons of @@ -350,6 +350,40 @@ def_instruction! { ty: TypeId, } : [1] => [1], + /// Create an `i32` from a future. + FutureLower { + payload: &'a Option, + ty: TypeId, + } : [1] => [1], + + /// Create a future from an `i32`. + FutureLift { + payload: &'a Option, + ty: TypeId, + } : [1] => [1], + + /// Create an `i32` from a stream. + StreamLower { + payload: &'a Type, + ty: TypeId, + } : [1] => [1], + + /// Create a stream from an `i32`. + StreamLift { + payload: &'a Type, + ty: TypeId, + } : [1] => [1], + + /// Create an `i32` from an error. + ErrorLower { + ty: TypeId, + } : [1] => [1], + + /// Create a error from an `i32`. + ErrorLift { + ty: TypeId, + } : [1] => [1], + /// Pops a tuple value off the stack, decomposes the tuple to all of /// its fields, and then pushes the fields onto the stack. TupleLower { @@ -470,7 +504,8 @@ def_instruction! { /// Note that this will be used for async functions. CallInterface { func: &'a Function, - } : [func.params.len()] => [func.results.len()], + async_: bool, + } : [func.params.len()] => [if *async_ { 1 } else { func.results.len() }], /// Returns `amt` values on the stack. This is always the last /// instruction. @@ -519,6 +554,22 @@ def_instruction! { GuestDeallocateVariant { blocks: usize, } : [1] => [0], + + AsyncMalloc { size: usize, align: usize } : [0] => [1], + + AsyncCallWasm { name: &'a str, size: usize, align: usize } : [2] => [0], + + AsyncCallStart { + name: &'a str, + params: &'a [WasmType], + results: &'a [WasmType] + } : [params.len()] => [results.len()], + + AsyncPostCallInterface { func: &'a Function } : [1] => [func.results.len() + 1], + + AsyncCallReturn { name: &'a str, params: &'a [WasmType] } : [params.len()] => [0], + + Flush { amt: usize } : [*amt] => [*amt], } } @@ -683,8 +734,46 @@ pub fn call( lift_lower: LiftLower, func: &Function, bindgen: &mut impl Bindgen, + async_: bool, +) { + Generator::new(resolve, variant, lift_lower, bindgen, async_).call(func); +} + +pub fn lower_to_memory( + resolve: &Resolve, + bindgen: &mut B, + address: B::Operand, + value: B::Operand, + ty: &Type, ) { - Generator::new(resolve, variant, lift_lower, bindgen).call(func); + // TODO: refactor so we don't need to pass in a bunch of unused dummy parameters: + let mut generator = Generator::new( + resolve, + AbiVariant::GuestImport, + LiftLower::LowerArgsLiftResults, + bindgen, + true, + ); + generator.stack.push(value); + generator.write_to_memory(ty, address, 0); +} + +pub fn lift_from_memory( + resolve: &Resolve, + bindgen: &mut B, + address: B::Operand, + ty: &Type, +) -> B::Operand { + // TODO: refactor so we don't need to pass in a bunch of unused dummy parameters: + let mut generator = Generator::new( + resolve, + AbiVariant::GuestImport, + LiftLower::LowerArgsLiftResults, + bindgen, + true, + ); + generator.read_from_memory(ty, address, 0); + generator.stack.pop().unwrap() } /// Used in a similar manner as the `Interface::call` function except is @@ -693,12 +782,13 @@ pub fn call( /// This is only intended to be used in guest generators for exported /// functions and will primarily generate `GuestDeallocate*` instructions, /// plus others used as input to those instructions. -pub fn post_return(resolve: &Resolve, func: &Function, bindgen: &mut impl Bindgen) { +pub fn post_return(resolve: &Resolve, func: &Function, bindgen: &mut impl Bindgen, async_: bool) { Generator::new( resolve, AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, bindgen, + async_, ) .post_return(func); } @@ -734,7 +824,9 @@ fn needs_post_return(resolve: &Resolve, ty: &Type) -> bool { .filter_map(|t| t.as_ref()) .any(|t| needs_post_return(resolve, t)), TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => false, - TypeDefKind::Future(_) | TypeDefKind::Stream(_) => unimplemented!(), + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Error => { + unimplemented!() + } TypeDefKind::Unknown => unreachable!(), }, @@ -757,6 +849,7 @@ struct Generator<'a, B: Bindgen> { variant: AbiVariant, lift_lower: LiftLower, bindgen: &'a mut B, + async_: bool, resolve: &'a Resolve, operands: Vec, results: Vec, @@ -770,12 +863,14 @@ impl<'a, B: Bindgen> Generator<'a, B> { variant: AbiVariant, lift_lower: LiftLower, bindgen: &'a mut B, + async_: bool, ) -> Generator<'a, B> { Generator { resolve, variant, lift_lower, bindgen, + async_, operands: Vec::new(), results: Vec::new(), stack: Vec::new(), @@ -784,74 +879,122 @@ 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); match self.lift_lower { LiftLower::LowerArgsLiftResults => { - if !sig.indirect_params { - // If the parameters for this function aren't indirect - // (there aren't too many) then we simply do a normal lower - // operation for them all. + if let (AbiVariant::GuestExport, true) = (self.variant, self.async_) { + todo!("implement host-side support for async lift/lower"); + } + + let lower_to_memory = |self_: &mut Self, ptr: B::Operand| { + let mut offset = 0usize; for (nth, (_, ty)) in func.params.iter().enumerate() { - self.emit(&Instruction::GetArg { nth }); - self.lower(ty); + self_.emit(&Instruction::GetArg { nth }); + offset = align_to(offset, self_.bindgen.sizes().align(ty).align_wasm32()); + self_.write_to_memory(ty, ptr.clone(), offset as i32); + offset += self_.bindgen.sizes().size(ty).size_wasm32(); } - } else { - // ... otherwise if parameters are indirect space is - // allocated from them and each argument is lowered - // individually into memory. - let info = self + + self_.stack.push(ptr); + }; + + let params_size_align = if self.async_ { + let ElementInfo { size, align } = self .bindgen .sizes() - .record(func.params.iter().map(|t| &t.1)); - let ptr = match self.variant { - // When a wasm module calls an import it will provide - // space that isn't explicitly deallocated. - AbiVariant::GuestImport => self - .bindgen - .return_pointer(info.size.size_wasm32(), info.align.align_wasm32()), - // When calling a wasm module from the outside, though, - // malloc needs to be called. - AbiVariant::GuestExport => { - self.emit(&Instruction::Malloc { - realloc: "cabi_realloc", - size: info.size.size_wasm32(), - align: info.align.align_wasm32(), - }); - self.stack.pop().unwrap() + .record(func.params.iter().map(|(_, ty)| ty)); + self.emit(&Instruction::AsyncMalloc { + size: size.size_wasm32(), + align: align.align_wasm32(), + }); + let ptr = self.stack.pop().unwrap(); + lower_to_memory(self, ptr); + Some((size, align)) + } else { + if !sig.indirect_params { + // If the parameters for this function aren't indirect + // (there aren't too many) then we simply do a normal lower + // operation for them all. + for (nth, (_, ty)) in func.params.iter().enumerate() { + self.emit(&Instruction::GetArg { nth }); + self.lower(ty); } - }; - let mut offset = 0usize; - for (nth, (_, ty)) in func.params.iter().enumerate() { - self.emit(&Instruction::GetArg { nth }); - offset = align_to(offset, self.bindgen.sizes().align(ty).align_wasm32()); - self.write_to_memory(ty, ptr.clone(), offset as i32); - offset += self.bindgen.sizes().size(ty).size_wasm32(); + } else { + // ... otherwise if parameters are indirect space is + // allocated from them and each argument is lowered + // individually into memory. + let info = self + .bindgen + .sizes() + .record(func.params.iter().map(|t| &t.1)); + let ptr = match self.variant { + // When a wasm module calls an import it will provide + // space that isn't explicitly deallocated. + AbiVariant::GuestImport => self + .bindgen + .return_pointer(info.size.size_wasm32(), info.align.align_wasm32()), + // When calling a wasm module from the outside, though, + // malloc needs to be called. + AbiVariant::GuestExport => { + self.emit(&Instruction::Malloc { + realloc: "cabi_realloc", + size: info.size.size_wasm32(), + align: info.align.align_wasm32(), + }); + self.stack.pop().unwrap() + } + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } + }; + lower_to_memory(self, ptr); } - - self.stack.push(ptr); - } + None + }; // If necessary we may need to prepare a return pointer for // this ABI. - if self.variant == AbiVariant::GuestImport && sig.retptr { - let info = self.bindgen.sizes().params(func.results.iter_types()); - let ptr = self - .bindgen - .return_pointer(info.size.size_wasm32(), info.align.align_wasm32()); - self.return_pointer = Some(ptr.clone()); - self.stack.push(ptr); - } + let dealloc_size_align = + if let Some((params_size, params_align)) = params_size_align { + let ElementInfo { size, align } = + self.bindgen.sizes().record(func.results.iter_types()); + self.emit(&Instruction::AsyncMalloc { + size: size.size_wasm32(), + align: align.align_wasm32(), + }); + let ptr = self.stack.pop().unwrap(); + self.return_pointer = Some(ptr.clone()); + self.stack.push(ptr); + + assert_eq!(self.stack.len(), 2); + self.emit(&Instruction::AsyncCallWasm { + name: &format!("[async]{}", func.name), + size: params_size.size_wasm32(), + align: params_align.align_wasm32(), + }); + Some((size, align)) + } else { + if self.variant == AbiVariant::GuestImport && sig.retptr { + let info = self.bindgen.sizes().params(func.results.iter_types()); + let ptr = self + .bindgen + .return_pointer(info.size.size_wasm32(), info.align.align_wasm32()); + self.return_pointer = Some(ptr.clone()); + self.stack.push(ptr); + } - // Now that all the wasm args are prepared we can call the - // actual wasm function. - assert_eq!(self.stack.len(), sig.params.len()); - self.emit(&Instruction::CallWasm { - name: &func.name, - sig: &sig, - }); + assert_eq!(self.stack.len(), sig.params.len()); + self.emit(&Instruction::CallWasm { + name: &func.name, + sig: &sig, + }); + None + }; - if !sig.retptr { + 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. @@ -862,7 +1005,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { let ptr = match self.variant { // imports into guests means it's a wasm module // calling an imported function. We supplied the - // return poitner as the last argument (saved in + // return pointer as the last argument (saved in // `self.return_pointer`) so we use that to read // the result of the function from memory. AbiVariant::GuestImport => { @@ -874,9 +1017,24 @@ impl<'a, B: Bindgen> Generator<'a, B> { // calling wasm so wasm returned a pointer to where // the result is stored AbiVariant::GuestExport => self.stack.pop().unwrap(), + + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } }; - self.read_results_from_memory(&func.results, ptr, 0); + self.read_results_from_memory(&func.results, ptr.clone(), 0); + self.emit(&Instruction::Flush { + amt: func.results.len(), + }); + + if let Some((size, align)) = dealloc_size_align { + self.stack.push(ptr); + self.emit(&Instruction::GuestDeallocate { + size: size.size_wasm32(), + align: align.align_wasm32(), + }); + } } self.emit(&Instruction::Return { @@ -885,6 +1043,20 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); } LiftLower::LiftArgsLowerResults => { + if let (AbiVariant::GuestImport, true) = (self.variant, self.async_) { + todo!("implement host-side support for async lift/lower"); + } + + let read_from_memory = |self_: &mut Self| { + let mut offset = 0usize; + let ptr = self_.stack.pop().unwrap(); + for (_, ty) in func.params.iter() { + offset = align_to(offset, self_.bindgen.sizes().align(ty).align_wasm32()); + self_.read_from_memory(ty, ptr.clone(), offset as i32); + offset += self_.bindgen.sizes().size(ty).size_wasm32(); + } + }; + if !sig.indirect_params { // If parameters are not passed indirectly then we lift each // argument in succession from the component wasm types that @@ -904,23 +1076,33 @@ impl<'a, B: Bindgen> Generator<'a, B> { // ... otherwise argument is read in succession from memory // where the pointer to the arguments is the first argument // to the function. - let mut offset = 0usize; self.emit(&Instruction::GetArg { nth: 0 }); - let ptr = self.stack.pop().unwrap(); - for (_, ty) in func.params.iter() { - offset = align_to(offset, self.bindgen.sizes().align(ty).align_wasm32()); - self.read_from_memory(ty, ptr.clone(), offset as i32); - offset += self.bindgen.sizes().size(ty).size_wasm32(); - } + read_from_memory(self); } // ... and that allows us to call the interface types function - self.emit(&Instruction::CallInterface { func }); + self.emit(&Instruction::CallInterface { + func, + async_: self.async_, + }); + + let (lower_to_memory, async_results) = if self.async_ { + self.emit(&Instruction::AsyncPostCallInterface { func }); - // This was dynamically allocated by the caller so after - // it's been read by the guest we need to deallocate it. + let mut results = Vec::new(); + for ty in func.results.iter_types() { + self.resolve.push_flat(ty, &mut results); + } + (results.len() > MAX_FLAT_PARAMS, Some(results)) + } else { + (sig.retptr, None) + }; + + // This was dynamically allocated by the caller (or async start + // function) so after it's been read by the guest we need to + // deallocate it. if let AbiVariant::GuestExport = self.variant { - if sig.indirect_params { + if sig.indirect_params && !self.async_ { let info = self .bindgen .sizes() @@ -933,7 +1115,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - if !sig.retptr { + if !lower_to_memory { // With no return pointer in use we simply lower the // result(s) and return that directly from the function. let results = self @@ -973,13 +1155,31 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.write_params_to_memory(func.results.iter_types(), ptr.clone(), 0); self.stack.push(ptr); } + + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } } } - self.emit(&Instruction::Return { - func, - amt: sig.results.len(), - }); + if let Some(results) = async_results { + let name = &format!("[task-return]{}", func.name); + + self.emit(&Instruction::AsyncCallReturn { + name, + params: &if results.len() > MAX_FLAT_PARAMS { + vec![WasmType::Pointer] + } else { + results + }, + }); + self.emit(&Instruction::Return { func, amt: 1 }); + } else { + self.emit(&Instruction::Return { + func, + amt: sig.results.len(), + }); + } } } @@ -1177,8 +1377,21 @@ impl<'a, B: Bindgen> Generator<'a, B> { results: &results, }); } - TypeDefKind::Future(_) => todo!("lower future"), - TypeDefKind::Stream(_) => todo!("lower stream"), + TypeDefKind::Future(ty) => { + self.emit(&FutureLower { + payload: ty, + ty: id, + }); + } + TypeDefKind::Stream(ty) => { + self.emit(&StreamLower { + payload: ty, + ty: id, + }); + } + TypeDefKind::Error => { + self.emit(&ErrorLower { ty: id }); + } TypeDefKind::Unknown => unreachable!(), }, } @@ -1362,8 +1575,21 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&ResultLift { result: r, ty: id }); } - TypeDefKind::Future(_) => todo!("lift future"), - TypeDefKind::Stream(_) => todo!("lift stream"), + TypeDefKind::Future(ty) => { + self.emit(&FutureLift { + payload: ty, + ty: id, + }); + } + TypeDefKind::Stream(ty) => { + self.emit(&StreamLift { + payload: ty, + ty: id, + }); + } + TypeDefKind::Error => { + self.emit(&ErrorLift { ty: id }); + } TypeDefKind::Unknown => unreachable!(), }, } @@ -1431,7 +1657,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::Type(t) => self.write_to_memory(t, addr, offset), TypeDefKind::List(_) => self.write_list_to_memory(ty, addr, offset), - TypeDefKind::Handle(_) => self.lower_and_emit(ty, addr, &I32Store { offset }), + TypeDefKind::Future(_) + | TypeDefKind::Stream(_) + | TypeDefKind::Error + | TypeDefKind::Handle(_) => self.lower_and_emit(ty, addr, &I32Store { offset }), // Decompose the record into its components and then write all // the components into memory one-by-one. @@ -1521,8 +1750,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.store_intrepr(offset, e.tag()); } - TypeDefKind::Future(_) => todo!("write future to memory"), - TypeDefKind::Stream(_) => todo!("write stream to memory"), TypeDefKind::Unknown => unreachable!(), }, } @@ -1625,7 +1852,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::List(_) => self.read_list_from_memory(ty, addr, offset), - TypeDefKind::Handle(_) => self.emit_and_lift(ty, addr, &I32Load { offset }), + TypeDefKind::Future(_) + | TypeDefKind::Stream(_) + | TypeDefKind::Error + | TypeDefKind::Handle(_) => self.emit_and_lift(ty, addr, &I32Load { offset }), TypeDefKind::Resource => { todo!(); @@ -1709,8 +1939,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.lift(ty); } - TypeDefKind::Future(_) => todo!("read future from memory"), - TypeDefKind::Stream(_) => todo!("read stream from memory"), TypeDefKind::Unknown => unreachable!(), }, } @@ -1887,6 +2115,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::Future(_) => todo!("read future from memory"), TypeDefKind::Stream(_) => todo!("read stream from memory"), + TypeDefKind::Error => todo!("read error from memory"), TypeDefKind::Unknown => unreachable!(), }, } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index fae3f3b90..3ee91ef74 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -177,6 +177,7 @@ pub trait InterfaceGenerator<'a> { TypeDefKind::Future(_) => todo!("generate for future"), TypeDefKind::Stream(_) => todo!("generate for stream"), TypeDefKind::Handle(_) => todo!("generate for handle"), + TypeDefKind::Error => todo!("generate for error"), TypeDefKind::Unknown => unreachable!(), } } @@ -191,8 +192,9 @@ pub trait AnonymousTypeGenerator<'a> { fn anonymous_type_result(&mut self, id: TypeId, ty: &Result_, docs: &Docs); fn anonymous_type_list(&mut self, id: TypeId, ty: &Type, docs: &Docs); fn anonymous_type_future(&mut self, id: TypeId, ty: &Option, docs: &Docs); - fn anonymous_type_stream(&mut self, id: TypeId, ty: &Stream, docs: &Docs); + fn anonymous_type_stream(&mut self, id: TypeId, ty: &Type, docs: &Docs); fn anonymous_typ_type(&mut self, id: TypeId, ty: &Type, docs: &Docs); + fn anonymous_type_error(&mut self); fn define_anonymous_type(&mut self, id: TypeId) { let ty = &self.resolve().types[id]; @@ -211,6 +213,7 @@ pub trait AnonymousTypeGenerator<'a> { TypeDefKind::List(t) => self.anonymous_type_list(id, t, &ty.docs), TypeDefKind::Future(f) => self.anonymous_type_future(id, f, &ty.docs), TypeDefKind::Stream(s) => self.anonymous_type_stream(id, s, &ty.docs), + TypeDefKind::Error => self.anonymous_type_error(), TypeDefKind::Handle(handle) => self.anonymous_type_handle(id, handle, &ty.docs), TypeDefKind::Unknown => unreachable!(), } diff --git a/crates/core/src/types.rs b/crates/core/src/types.rs index a0862f5e8..94c038cf3 100644 --- a/crates/core/src/types.rs +++ b/crates/core/src/types.rs @@ -193,13 +193,7 @@ impl Types { info = self.optional_type_info(resolve, r.ok.as_ref()); info |= self.optional_type_info(resolve, r.err.as_ref()); } - TypeDefKind::Future(ty) => { - info = self.optional_type_info(resolve, ty.as_ref()); - } - TypeDefKind::Stream(stream) => { - info = self.optional_type_info(resolve, stream.element.as_ref()); - info |= self.optional_type_info(resolve, stream.end.as_ref()); - } + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Error => {} TypeDefKind::Unknown => unreachable!(), } let prev = self.type_info.insert(ty, info); diff --git a/crates/csharp/src/lib.rs b/crates/csharp/src/lib.rs index 6f11ef47b..d7529c9fc 100644 --- a/crates/csharp/src/lib.rs +++ b/crates/csharp/src/lib.rs @@ -1059,6 +1059,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut bindgen, + false, ); let src = bindgen.src; @@ -1178,6 +1179,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut bindgen, + false, ); assert!(!bindgen.needs_cleanup_list); @@ -2694,7 +2696,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { ); } - Instruction::CallInterface { func } => { + Instruction::CallInterface { func, .. } => { let module = self.gen.name; let func_name = self.func_name.to_upper_camel_case(); let interface_name = CSharp::get_class_name_from_qualified_name(module).1; @@ -3005,6 +3007,11 @@ impl Bindgen for FunctionBindgen<'_, '_> { } results.push(resource); } + + Instruction::AsyncMalloc { .. } + | Instruction::AsyncCallStart { .. } + | Instruction::AsyncPostCallInterface { .. } + | Instruction::AsyncCallReturn { .. } => todo!(), } } diff --git a/crates/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs index c6cb439be..25b7763bb 100644 --- a/crates/guest-rust/macro/src/lib.rs +++ b/crates/guest-rust/macro/src/lib.rs @@ -8,7 +8,7 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{braced, token, LitStr, Token}; use wit_bindgen_core::wit_parser::{PackageId, Resolve, UnresolvedPackageGroup, WorldId}; -use wit_bindgen_rust::{Opts, Ownership, WithOption}; +use wit_bindgen_rust::{AsyncConfig, Opts, Ownership, WithOption}; #[proc_macro] pub fn generate(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -46,6 +46,7 @@ struct Config { resolve: Resolve, world: WorldId, files: Vec, + debug: bool, } /// The source of the wit package definition @@ -63,6 +64,8 @@ impl Parse for Config { let mut world = None; let mut source = None; let mut features = Vec::new(); + let mut async_configured = false; + let mut debug = false; if input.peek(token::Brace) { let content; @@ -140,6 +143,16 @@ impl Parse for Config { Opt::DisableCustomSectionLinkHelpers(disable) => { opts.disable_custom_section_link_helpers = disable.value(); } + Opt::Debug(enable) => { + debug = enable.value(); + } + Opt::Async(val, span) => { + if async_configured { + return Err(Error::new(span, "cannot specify second async config")); + } + async_configured = true; + opts.async_ = val; + } } } } else { @@ -159,6 +172,7 @@ impl Parse for Config { resolve, world, files, + debug, }) } } @@ -222,7 +236,7 @@ fn parse_source( }; let (pkg, sources) = resolve.push_path(normalized_path)?; pkgs.push(pkg); - files.extend(sources); + files.extend(sources.package_paths(pkg).unwrap().map(|v| v.to_owned())); } Ok(()) }; @@ -254,7 +268,7 @@ impl Config { // place a formatted version of the expanded code into a file. This file // will then show up in rustc error messages for any codegen issues and can // be inspected manually. - if std::env::var("WIT_BINDGEN_DEBUG").is_ok() { + if std::env::var("WIT_BINDGEN_DEBUG").is_ok() || self.debug { static INVOCATION: AtomicUsize = AtomicUsize::new(0); let root = Path::new(env!("DEBUG_OUTPUT_DIR")); let world_name = &self.resolve.worlds[self.world].name; @@ -313,6 +327,8 @@ mod kw { syn::custom_keyword!(generate_unused_types); syn::custom_keyword!(features); syn::custom_keyword!(disable_custom_section_link_helpers); + syn::custom_keyword!(imports); + syn::custom_keyword!(debug); } #[derive(Clone)] @@ -342,6 +358,11 @@ impl From for wit_bindgen_rust::ExportKey { } } +enum AsyncConfigSomeKind { + Imports, + Exports, +} + enum Opt { World(syn::LitStr), Path(Span, Vec), @@ -366,6 +387,8 @@ enum Opt { GenerateUnusedTypes(syn::LitBool), Features(Vec), DisableCustomSectionLinkHelpers(syn::LitBool), + Async(AsyncConfig, Span), + Debug(syn::LitBool), } impl Parse for Opt { @@ -513,6 +536,34 @@ impl Parse for Opt { input.parse::()?; input.parse::()?; Ok(Opt::DisableCustomSectionLinkHelpers(input.parse()?)) + } else if l.peek(kw::debug) { + input.parse::()?; + input.parse::()?; + Ok(Opt::Debug(input.parse()?)) + } else if l.peek(Token![async]) { + let span = input.parse::()?.span; + input.parse::()?; + if input.peek(syn::LitBool) { + if input.parse::()?.value { + Ok(Opt::Async(AsyncConfig::All, span)) + } else { + Ok(Opt::Async(AsyncConfig::None, span)) + } + } else { + let mut imports = Vec::new(); + let mut exports = Vec::new(); + let contents; + syn::braced!(contents in input); + for (kind, values) in + contents.parse_terminated(parse_async_some_field, Token![,])? + { + match kind { + AsyncConfigSomeKind::Imports => imports = values, + AsyncConfigSomeKind::Exports => exports = values, + } + } + Ok(Opt::Async(AsyncConfig::Some { imports, exports }, span)) + } } else { Err(l.error()) } @@ -571,3 +622,27 @@ fn fmt(input: &str) -> Result { let syntax_tree = syn::parse_file(&input)?; Ok(prettyplease::unparse(&syntax_tree)) } + +fn parse_async_some_field(input: ParseStream<'_>) -> Result<(AsyncConfigSomeKind, Vec)> { + let lookahead = input.lookahead1(); + let kind = if lookahead.peek(kw::imports) { + input.parse::()?; + input.parse::()?; + AsyncConfigSomeKind::Imports + } else if lookahead.peek(kw::exports) { + input.parse::()?; + input.parse::()?; + AsyncConfigSomeKind::Exports + } else { + return Err(lookahead.error()); + }; + + let list; + syn::bracketed!(list in input); + let fields = list.parse_terminated(Parse::parse, Token![,])?; + + Ok(( + kind, + fields.iter().map(|s: &syn::LitStr| s.value()).collect(), + )) +} diff --git a/crates/rust/src/async_support.rs b/crates/rust/src/async_support.rs new file mode 100644 index 000000000..f9bf9ed7c --- /dev/null +++ b/crates/rust/src/async_support.rs @@ -0,0 +1,918 @@ +use { + futures::{ + channel::oneshot, + future::{self, FutureExt}, + sink::Sink, + stream::{FuturesUnordered, Stream, StreamExt}, + }, + once_cell::sync::Lazy, + std::{ + alloc::{self, Layout}, + any::Any, + collections::hash_map::Entry, + collections::HashMap, + convert::Infallible, + fmt::{self, Debug, Display}, + future::{Future, IntoFuture}, + iter, + marker::PhantomData, + mem::{self, ManuallyDrop, MaybeUninit}, + pin::{pin, Pin}, + ptr, + sync::Arc, + task::{Context, Poll, Wake, Waker}, + }, +}; + +type BoxFuture = Pin + 'static>>; + +struct FutureState { + todo: usize, + tasks: Option>, +} + +static mut CURRENT: *mut FutureState = ptr::null_mut(); + +static mut CALLS: Lazy>> = Lazy::new(HashMap::new); + +static mut SPAWNED: Vec = Vec::new(); + +enum Handle { + LocalOpen, + LocalReady(Box, Waker), + LocalWaiting(oneshot::Sender>), + LocalClosed, + Read, + Write, +} + +static mut HANDLES: Lazy> = Lazy::new(HashMap::new); + +fn dummy_waker() -> Waker { + struct DummyWaker; + + impl Wake for DummyWaker { + fn wake(self: Arc) {} + } + + static WAKER: Lazy> = Lazy::new(|| Arc::new(DummyWaker)); + + WAKER.clone().into() +} + +unsafe fn poll(state: *mut FutureState) -> Poll<()> { + loop { + if let Some(futures) = (*state).tasks.as_mut() { + CURRENT = state; + let poll = futures.poll_next_unpin(&mut Context::from_waker(&dummy_waker())); + CURRENT = ptr::null_mut(); + + if SPAWNED.is_empty() { + match poll { + Poll::Ready(Some(())) => (), + Poll::Ready(None) => { + (*state).tasks = None; + break Poll::Ready(()); + } + Poll::Pending => break Poll::Pending, + } + } else { + futures.extend(SPAWNED.drain(..)); + } + } else { + break Poll::Ready(()); + } + } +} + +pub fn first_poll( + future: impl Future + 'static, + fun: impl FnOnce(T) + 'static, +) -> *mut u8 { + let state = Box::into_raw(Box::new(FutureState { + todo: 0, + tasks: Some( + [Box::pin(future.map(fun)) as BoxFuture] + .into_iter() + .collect(), + ), + })); + match unsafe { poll(state) } { + Poll::Ready(()) => ptr::null_mut(), + Poll::Pending => state as _, + } +} + +pub async unsafe fn await_result( + import: unsafe extern "C" fn(*mut u8, *mut u8) -> i32, + params_layout: Layout, + params: *mut u8, + results: *mut u8, +) { + const STATUS_STARTING: u32 = 0; + const STATUS_STARTED: u32 = 1; + const STATUS_RETURNED: u32 = 2; + const STATUS_DONE: u32 = 3; + + let result = import(params, results) as u32; + let status = result >> 30; + let call = (result & !(0b11 << 30)) as i32; + + if status != STATUS_DONE { + assert!(!CURRENT.is_null()); + (*CURRENT).todo += 1; + } + + match status { + STATUS_STARTING => { + let (tx, rx) = oneshot::channel(); + CALLS.insert(call, tx); + rx.await.unwrap(); + alloc::dealloc(params, params_layout); + } + STATUS_STARTED => { + alloc::dealloc(params, params_layout); + let (tx, rx) = oneshot::channel(); + CALLS.insert(call, tx); + rx.await.unwrap(); + } + STATUS_RETURNED | STATUS_DONE => { + alloc::dealloc(params, params_layout); + } + _ => unreachable!(), + } +} + +mod results { + pub const BLOCKED: u32 = 0xffff_ffff; + pub const CLOSED: u32 = 0x8000_0000; + pub const CANCELED: u32 = 0; +} + +pub async unsafe fn await_future_result( + import: unsafe extern "C" fn(u32, *mut u8) -> u32, + future: u32, + address: *mut u8, +) -> bool { + let result = import(future, address); + match result { + results::BLOCKED => { + assert!(!CURRENT.is_null()); + (*CURRENT).todo += 1; + let (tx, rx) = oneshot::channel(); + CALLS.insert(future as _, tx); + let v = rx.await.unwrap(); + v == 1 + } + results::CLOSED | results::CANCELED => false, + 1 => true, + _ => unreachable!(), + } +} + +pub async unsafe fn await_stream_result( + import: unsafe extern "C" fn(u32, *mut u8, u32) -> u32, + stream: u32, + address: *mut u8, + count: u32, +) -> Option { + let result = import(stream, address, count); + match result { + results::BLOCKED => { + assert!(!CURRENT.is_null()); + (*CURRENT).todo += 1; + let (tx, rx) = oneshot::channel(); + CALLS.insert(stream as _, tx); + let v = rx.await.unwrap(); + if let results::CLOSED | results::CANCELED = v { + None + } else { + Some(usize::try_from(v).unwrap()) + } + } + results::CLOSED | results::CANCELED => None, + v => Some(usize::try_from(v).unwrap()), + } +} + +fn subtask_drop(subtask: u32) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[subtask-drop]"] + fn subtask_drop(_: u32); + } + unsafe { + subtask_drop(subtask); + } + } +} + +pub unsafe fn callback(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + const EVENT_CALL_STARTING: i32 = 0; + const EVENT_CALL_STARTED: i32 = 1; + const EVENT_CALL_RETURNED: i32 = 2; + const EVENT_CALL_DONE: i32 = 3; + const EVENT_YIELDED: i32 = 4; + const EVENT_STREAM_READ: i32 = 5; + const EVENT_STREAM_WRITE: i32 = 6; + const EVENT_FUTURE_READ: i32 = 7; + const EVENT_FUTURE_WRITE: i32 = 8; + + match event0 { + EVENT_CALL_STARTED => { + // TODO: could dealloc params here if we attached the pointer to the call + 0 + } + EVENT_CALL_RETURNED | EVENT_CALL_DONE | EVENT_STREAM_READ | EVENT_STREAM_WRITE + | EVENT_FUTURE_READ | EVENT_FUTURE_WRITE => { + if let Some(call) = CALLS.remove(&event1) { + call.send(event2 as _); + } + + let state = ctx as *mut FutureState; + let done = poll(state).is_ready(); + + if event0 == EVENT_CALL_DONE { + subtask_drop(event1 as u32); + } + + if matches!( + event0, + EVENT_CALL_DONE + | EVENT_STREAM_READ + | EVENT_STREAM_WRITE + | EVENT_FUTURE_READ + | EVENT_FUTURE_WRITE + ) { + (*state).todo -= 1; + } + + if done && (*state).todo == 0 { + drop(Box::from_raw(state)); + 1 + } else { + 0 + } + } + _ => unreachable!(), + } +} + +#[doc(hidden)] +pub trait FuturePayload: Sized + 'static { + fn new() -> u32; + async fn write(future: u32, value: Self) -> bool; + async fn read(future: u32) -> Option; + fn drop_writer(future: u32); + fn drop_reader(future: u32); +} + +pub struct FutureWriter { + handle: u32, + _phantom: PhantomData, +} + +impl FutureWriter { + pub async fn write(self, v: T) { + match unsafe { HANDLES.entry(self.handle) } { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::LocalOpen => { + let mut v = Some(v); + future::poll_fn(move |cx| match unsafe { HANDLES.entry(self.handle) } { + 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!() + } + }, + }) + .await + } + Handle::LocalWaiting(_) => { + let Handle::LocalWaiting(tx) = entry.insert(Handle::LocalClosed) else { + unreachable!() + }; + tx.send(Box::new(v)); + } + Handle::LocalClosed => (), + Handle::Read | Handle::LocalReady(..) => unreachable!(), + Handle::Write => { + T::write(self.handle, v).await; + } + }, + } + } +} + +impl Drop for FutureWriter { + fn drop(&mut self) { + match unsafe { HANDLES.entry(self.handle) } { + 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::drop_writer(self.handle); + } + }, + } + } +} + +pub struct FutureReader { + handle: u32, + _phantom: PhantomData, +} + +impl FutureReader { + #[doc(hidden)] + pub fn from_handle(handle: u32) -> Self { + match unsafe { HANDLES.entry(handle) } { + 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 { + match unsafe { HANDLES.entry(self.handle) } { + 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 IntoFuture for FutureReader { + type Output = Option; + type IntoFuture = Pin + 'static>>; + + fn into_future(self) -> Self::IntoFuture { + match unsafe { HANDLES.entry(self.handle) } { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::Write | Handle::LocalWaiting(_) => unreachable!(), + Handle::Read => Box::pin(async move { T::read(self.handle).await }), + 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 Drop for FutureReader { + fn drop(&mut self) { + match unsafe { HANDLES.entry(self.handle) } { + 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::drop_reader(self.handle); + } + Handle::Write => unreachable!(), + }, + } + } +} + +#[doc(hidden)] +pub trait StreamPayload: Unpin + Sized + 'static { + fn new() -> u32; + async fn write(stream: u32, values: &[Self]) -> Option; + async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; + fn drop_writer(future: u32); + fn drop_reader(future: u32); +} + +pub struct StreamWriter { + handle: u32, + future: Option + 'static>>>, + _phantom: PhantomData, +} + +impl Sink> for StreamWriter { + type Error = Infallible; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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) -> Result<(), Self::Error> { + assert!(self.future.is_none()); + match unsafe { HANDLES.entry(self.handle) } { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::LocalOpen => { + let handle = self.handle; + let mut item = Some(item); + self.get_mut().future = Some(Box::pin(future::poll_fn(move |cx| { + match unsafe { HANDLES.entry(handle) } { + 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 { + Poll::Ready(()) + } + } + Handle::LocalReady(..) => Poll::Pending, + Handle::LocalClosed => 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; + 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; + } + } + })); + } + }, + } + Ok(()) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } +} + +impl Drop for StreamWriter { + fn drop(&mut self) { + if self.future.is_some() { + todo!("gracefully handle `StreamWriter::drop` when a write is in progress"); + } + + match unsafe { HANDLES.entry(self.handle) } { + 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::drop_writer(self.handle); + } + }, + } + } +} + +pub struct StreamReader { + handle: u32, + future: Option>> + 'static>>>, + _phantom: PhantomData, +} + +impl StreamReader { + #[doc(hidden)] + pub fn from_handle(handle: u32) -> Self { + match unsafe { HANDLES.entry(handle) } { + Entry::Vacant(mut 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 { + match unsafe { HANDLES.entry(self.handle) } { + 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 Stream for StreamReader { + type Item = Vec; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let me = self.get_mut(); + + if me.future.is_none() { + me.future = Some(match unsafe { HANDLES.entry(me.handle) } { + Entry::Vacant(_) => unreachable!(), + Entry::Occupied(mut entry) => match entry.get() { + Handle::Write | Handle::LocalWaiting(_) => unreachable!(), + Handle::Read => { + let handle = me.handle; + Box::pin(async move { + let mut buffer = iter::repeat_with(MaybeUninit::uninit) + .take(ceiling(64 * 1024, mem::size_of::())) + .collect::>(); + + if let Some(count) = T::read(handle, &mut buffer).await { + buffer.truncate(count); + Some(unsafe { + mem::transmute::>, Vec>(buffer) + }) + } else { + None + } + }) + } + Handle::LocalOpen => { + let (tx, rx) = oneshot::channel(); + entry.insert(Handle::LocalWaiting(tx)); + Box::pin(rx.map(|v| v.ok().map(|v| *v.downcast().unwrap()))) + } + Handle::LocalClosed => return Poll::Ready(None), + Handle::LocalReady(..) => { + let Handle::LocalReady(v, waker) = entry.insert(Handle::LocalOpen) else { + unreachable!() + }; + waker.wake(); + return Poll::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 Drop for StreamReader { + fn drop(&mut self) { + if self.future.is_some() { + todo!("gracefully handle `StreamReader::drop` when a read is in progress"); + } + + match unsafe { HANDLES.entry(self.handle) } { + 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::drop_reader(self.handle); + } + Handle::Write => unreachable!(), + }, + } + } +} + +pub struct Error { + handle: u32, +} + +impl Error { + #[doc(hidden)] + pub fn from_handle(handle: u32) -> Self { + Self { handle } + } + + #[doc(hidden)] + pub fn handle(&self) -> u32 { + self.handle + } +} + +impl Debug for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Error").finish() + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Error") + } +} + +impl std::error::Error for Error {} + +impl Drop for Error { + fn drop(&mut self) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[error-drop]"] + fn error_drop(_: u32); + } + if self.handle != 0 { + unsafe { error_drop(self.handle) } + } + } + } +} + +pub fn new_future() -> (FutureWriter, FutureReader) { + let handle = T::new(); + unsafe { HANDLES.insert(handle, Handle::LocalOpen) }; + ( + FutureWriter { + handle, + _phantom: PhantomData, + }, + FutureReader { + handle, + _phantom: PhantomData, + }, + ) +} + +pub fn new_stream() -> (StreamWriter, StreamReader) { + let handle = T::new(); + unsafe { HANDLES.insert(handle, Handle::LocalOpen) }; + ( + StreamWriter { + handle, + future: None, + _phantom: PhantomData, + }, + StreamReader { + handle, + future: None, + _phantom: PhantomData, + }, + ) +} + +pub fn spawn(future: impl Future + 'static) { + unsafe { SPAWNED.push(Box::pin(future)) } +} + +fn task_wait(state: &mut FutureState) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-wait]"] + fn wait(_: *mut i32) -> i32; + } + let mut payload = [0i32; 2]; + unsafe { + let event0 = wait(payload.as_mut_ptr()); + callback(state as *mut _ as _, event0, payload[0], payload[1]); + } + } +} + +// TODO: refactor so `'static` bounds aren't necessary +pub fn block_on(future: impl Future + 'static) -> T { + let (mut tx, mut rx) = oneshot::channel(); + let state = &mut FutureState { + todo: 0, + tasks: Some( + [Box::pin(future.map(move |v| drop(tx.send(v)))) as BoxFuture] + .into_iter() + .collect(), + ), + }; + loop { + match unsafe { poll(state) } { + Poll::Ready(()) => break rx.try_recv().unwrap().unwrap(), + Poll::Pending => task_wait(state), + } + } +} + +fn task_poll(state: &mut FutureState) -> bool { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-poll]"] + fn poll(_: *mut i32) -> i32; + } + let mut payload = [0i32; 3]; + unsafe { + let got_event = poll(payload.as_mut_ptr()) != 0; + if got_event { + callback(state as *mut _ as _, payload[0], payload[1], payload[2]); + } + got_event + } + } +} + +// TODO: refactor so `'static` bounds aren't necessary +pub fn poll_future(future: impl Future + 'static) -> Option { + let (mut tx, mut rx) = oneshot::channel(); + let state = &mut FutureState { + todo: 0, + tasks: Some( + [Box::pin(future.map(move |v| drop(tx.send(v)))) as BoxFuture] + .into_iter() + .collect(), + ), + }; + loop { + match unsafe { poll(state) } { + Poll::Ready(()) => break Some(rx.try_recv().unwrap().unwrap()), + Poll::Pending => { + if !task_poll(state) { + break None; + } + } + } + } +} + +pub fn task_yield() { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-yield]"] + fn yield_(); + } + unsafe { + yield_(); + } + } +} + +pub fn task_backpressure(enabled: bool) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-backpressure]"] + fn backpressure(_: i32); + } + unsafe { + backpressure(if enabled { 1 } else { 0 }); + } + } +} + +fn ceiling(x: usize, y: usize) -> usize { + (x / y) + if x % y == 0 { 0 } else { 1 } +} diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 1e9dc9ff1..0d1ce280d 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -8,6 +8,8 @@ use wit_bindgen_core::{dealias, uwrite, uwriteln, wit_parser::*, Source}; pub(super) struct FunctionBindgen<'a, 'b> { pub gen: &'b mut InterfaceGenerator<'a>, params: Vec, + async_: bool, + wasm_import_module: &'b str, pub src: Source, blocks: Vec, block_storage: Vec<(Source, Vec<(String, String)>)>, @@ -23,10 +25,14 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { pub(super) fn new( gen: &'b mut InterfaceGenerator<'a>, params: Vec, + async_: bool, + wasm_import_module: &'b str, ) -> FunctionBindgen<'a, 'b> { FunctionBindgen { gen, params, + async_, + wasm_import_module, src: Default::default(), blocks: Vec::new(), block_storage: Vec::new(), @@ -58,14 +64,9 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } - fn declare_import( - &mut self, - module_name: &str, - name: &str, - params: &[WasmType], - results: &[WasmType], - ) -> String { + fn declare_import(&mut self, name: &str, params: &[WasmType], results: &[WasmType]) -> String { // Define the actual function we're calling inline + let tmp = self.tmp(); let mut sig = "(".to_owned(); for param in params.iter() { sig.push_str("_: "); @@ -78,6 +79,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { sig.push_str(" -> "); sig.push_str(wasm_type(*result)); } + let module_name = self.wasm_import_module; uwrite!( self.src, " @@ -85,14 +87,14 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { #[link(wasm_import_module = \"{module_name}\")] extern \"C\" {{ #[link_name = \"{name}\"] - fn wit_import{sig}; + fn wit_import{tmp}{sig}; }} #[cfg(not(target_arch = \"wasm32\"))] - fn wit_import{sig} {{ unreachable!() }} + extern \"C\" fn wit_import{tmp}{sig} {{ unreachable!() }} " ); - "wit_import".to_string() + format!("wit_import{tmp}") } fn let_results(&mut self, amt: usize, results: &mut Vec) { @@ -456,6 +458,43 @@ impl Bindgen for FunctionBindgen<'_, '_> { results.push(result); } + Instruction::FutureLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).into_handle() as i32")) + } + + Instruction::FutureLift { .. } => { + let async_support = self.gen.path_to_async_support(); + let op = &operands[0]; + results.push(format!( + "{async_support}::FutureReader::from_handle({op} as u32)" + )) + } + + Instruction::StreamLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).into_handle() as i32")) + } + + Instruction::StreamLift { .. } => { + let async_support = self.gen.path_to_async_support(); + let op = &operands[0]; + results.push(format!( + "{async_support}::StreamReader::from_handle({op} as u32)" + )) + } + + Instruction::ErrorLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).handle() as i32")) + } + + Instruction::ErrorLift { .. } => { + let async_support = self.gen.path_to_async_support(); + let op = &operands[0]; + results.push(format!("{async_support}::Error::from_handle({op} as u32)")) + } + Instruction::RecordLower { ty, record, .. } => { self.record_lower(*ty, record, &operands[0], results); } @@ -779,12 +818,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::IterBasePointer => results.push("base".to_string()), Instruction::CallWasm { name, sig, .. } => { - let func = self.declare_import( - self.gen.wasm_import_module.unwrap(), - name, - &sig.params, - &sig.results, - ); + let func = self.declare_import(name, &sig.params, &sig.results); // ... then call the function with all our operands if !sig.results.is_empty() { @@ -797,8 +831,32 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(");\n"); } + Instruction::AsyncCallWasm { name, size, align } => { + let func = self.declare_import(name, &[WasmType::Pointer; 2], &[WasmType::I32]); + + let async_support = self.gen.path_to_async_support(); + let tmp = self.tmp(); + let layout = format!("layout{tmp}"); + let alloc = self.gen.path_to_std_alloc_module(); + self.push_str(&format!( + "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align});\n", + )); + let operands = operands.join(", "); + uwriteln!( + self.src, + "{async_support}::await_result({func}, {layout}, {operands}).await;" + ); + } + Instruction::CallInterface { func, .. } => { - self.let_results(func.results.len(), results); + if self.async_ { + let tmp = self.tmp(); + let result = format!("result{tmp}"); + self.push_str(&format!("let {result} = ")); + results.push(result); + } else { + self.let_results(func.results.len(), results); + }; match &func.kind { FunctionKind::Freestanding => { self.push_str(&format!("T::{}", to_rust_ident(&func.name))); @@ -839,6 +897,77 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(";\n"); } + Instruction::AsyncMalloc { size, align } => { + let alloc = self.gen.path_to_std_alloc_module(); + let tmp = self.tmp(); + let ptr = format!("ptr{tmp}"); + let layout = format!("layout{tmp}"); + uwriteln!( + self.src, + "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align}); + let {ptr} = {alloc}::alloc({layout});" + ); + results.push(ptr); + } + + Instruction::AsyncCallStart { + name, + params, + results: call_results, + } => { + let func = self.declare_import(name, params, call_results); + + if !call_results.is_empty() { + self.push_str("let ret = "); + results.push("ret".to_string()); + } + uwriteln!(self.src, "{func}({});", operands.join(", ")); + } + + Instruction::AsyncPostCallInterface { func } => { + let result = &operands[0]; + results.push("result".into()); + let params = (0..func.results.len()) + .map(|_| { + let tmp = self.tmp(); + let param = format!("result{}", tmp); + results.push(param.clone()); + param + }) + .collect::>() + .join(", "); + let params = format!("({params})"); + let async_support = self.gen.path_to_async_support(); + uwriteln!( + self.src, + "\ + let result = {async_support}::first_poll({result}, |{params}| {{ + " + ); + } + + Instruction::AsyncCallReturn { name, params } => { + let func = self.declare_import(name, params, &[]); + + uwriteln!( + self.src, + "\ + {func}({}); + }}); + ", + operands.join(", ") + ); + } + + Instruction::Flush { amt } => { + for i in 0..*amt { + let tmp = self.tmp(); + let result = format!("result{}", tmp); + uwriteln!(self.src, "let {result} = {};", operands[i]); + results.push(result); + } + } + Instruction::Return { amt, .. } => { self.emit_cleanup(); match amt { @@ -868,7 +997,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { let tmp = self.tmp(); uwriteln!( self.src, - "let l{tmp} = i32::from(*{}.add({offset}).cast::());", + "let l{tmp} = i32::from(*{0}.add({offset}).cast::());", operands[0] ); results.push(format!("l{tmp}")); diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 8a7468960..9446d05c3 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -1,7 +1,7 @@ use crate::bindgen::FunctionBindgen; use crate::{ - int_repr, to_rust_ident, to_upper_camel_case, wasm_type, FnSig, Identifier, InterfaceName, - Ownership, RuntimeItem, RustFlagsRepr, RustWasm, + int_repr, to_rust_ident, to_upper_camel_case, wasm_type, AsyncConfig, FnSig, Identifier, + InterfaceName, Ownership, RuntimeItem, RustFlagsRepr, RustWasm, }; use anyhow::Result; use heck::*; @@ -19,7 +19,7 @@ pub struct InterfaceGenerator<'a> { pub in_import: bool, pub sizes: SizeAlign, pub(super) gen: &'a mut RustWasm, - pub wasm_import_module: Option<&'a str>, + pub wasm_import_module: &'a str, pub resolve: &'a Resolve, pub return_pointer_area_size: usize, pub return_pointer_area_align: usize, @@ -156,6 +156,17 @@ impl InterfaceGenerator<'_> { continue; } + let async_ = match &self.gen.opts.async_ { + AsyncConfig::None => false, + AsyncConfig::All => true, + AsyncConfig::Some { exports, .. } => { + exports.contains(&if let Some((_, key)) = interface { + format!("{}#{}", self.resolve.name_world_key(key), func.name) + } else { + func.name.clone() + }) + } + }; let resource = match func.kind { FunctionKind::Freestanding => None, FunctionKind::Method(id) @@ -163,12 +174,13 @@ impl InterfaceGenerator<'_> { | FunctionKind::Static(id) => Some(id), }; - funcs_to_export.push((func, resource)); + funcs_to_export.push((func, resource, async_)); let (trait_name, methods) = traits.get_mut(&resource).unwrap(); - self.generate_guest_export(func, &trait_name); + self.generate_guest_export(func, interface.map(|(_, k)| k), &trait_name, async_); let prev = mem::take(&mut self.src); let mut sig = FnSig { + async_, use_item_name: true, private: true, ..Default::default() @@ -177,7 +189,7 @@ impl InterfaceGenerator<'_> { sig.self_arg = Some("&self".into()); sig.self_is_first_param = true; } - self.print_signature(func, true, &sig); + self.print_signature(func, true, &sig, false); self.src.push_str(";\n"); let trait_method = mem::replace(&mut self.src, prev); methods.push(trait_method); @@ -188,9 +200,9 @@ impl InterfaceGenerator<'_> { self.generate_interface_trait( &name, &methods, - traits.iter().map(|(resource, (trait_name, _methods))| { - (resource.unwrap(), trait_name.as_str()) - }), + traits + .iter() + .map(|(resource, (trait_name, ..))| (resource.unwrap(), trait_name.as_str())), ) } @@ -259,7 +271,7 @@ fn _resource_rep(handle: u32) -> *mut u8 None => { let world = match self.identifier { Identifier::World(w) => w, - Identifier::Interface(..) => unreachable!(), + Identifier::None | Identifier::Interface(..) => unreachable!(), }; let world = self.resolve.worlds[world].name.to_snake_case(); format!("__export_world_{world}_cabi") @@ -292,7 +304,7 @@ macro_rules! {macro_name} {{ " ); - for (func, resource) in funcs_to_export { + for (func, resource, async_) in funcs_to_export { let ty = match resource { None => "$ty".to_string(), Some(id) => { @@ -304,13 +316,13 @@ macro_rules! {macro_name} {{ format!("<$ty as $($path_to_types)*::Guest>::{name}") } }; - self.generate_raw_cabi_export(func, &ty, "$($path_to_types)*"); + self.generate_raw_cabi_export(func, &ty, "$($path_to_types)*", async_); } let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); for name in resources_to_drop { let module = match self.identifier { Identifier::Interface(_, key) => self.resolve.name_world_key(key), - Identifier::World(_) => unreachable!(), + Identifier::None | Identifier::World(_) => unreachable!(), }; let camel = name.to_upper_camel_case(); uwriteln!( @@ -357,9 +369,13 @@ macro_rules! {macro_name} {{ uwriteln!(self.src, "}}"); } - pub fn generate_imports<'a>(&mut self, funcs: impl Iterator) { + pub fn generate_imports<'a>( + &mut self, + funcs: impl Iterator, + interface: Option<&WorldKey>, + ) { for func in funcs { - self.generate_guest_import(func); + self.generate_guest_import(func, interface); } } @@ -454,12 +470,388 @@ macro_rules! {macro_name} {{ map.push((module, module_path)) } - fn generate_guest_import(&mut self, func: &Function) { + fn generate_payloads(&mut self, prefix: &str, func: &Function, interface: Option<&WorldKey>) { + for (index, ty) in func + .find_futures_and_streams(self.resolve) + .into_iter() + .enumerate() + { + let module = format!( + "{prefix}{}", + interface + .map(|name| self.resolve.name_world_key(name)) + .unwrap_or_else(|| "$root".into()) + ); + let func_name = &func.name; + let type_mode = TypeMode { + lifetime: None, + lists_borrowed: false, + style: TypeOwnershipStyle::Owned, + }; + let async_support = self.path_to_async_support(); + + match &self.resolve.types[ty].kind { + TypeDefKind::Future(payload_type) => { + let (name, full_name) = if let Some(payload_type) = payload_type { + ( + { + let old = mem::take(&mut self.src); + self.print_ty(&payload_type, type_mode); + String::from(mem::replace(&mut self.src, old)) + }, + { + let old = mem::take(&mut self.src); + let old_identifier = + mem::replace(&mut self.identifier, Identifier::None); + self.print_ty(&payload_type, type_mode); + self.identifier = old_identifier; + String::from(mem::replace(&mut self.src, old)) + }, + ) + } else { + ("()".into(), "()".into()) + }; + + if self.gen.future_payloads_emitted.insert(full_name) { + // TODO: `future.read` and `future.write` should always read from and write to the heap, + // and the return value will be either 0xffff_ffff (blocked), 0x8000_0000 (closed), or 0x1 + // (done). + let (size, align) = if let Some(payload_type) = payload_type { + ( + self.sizes.size(payload_type), + self.sizes.align(payload_type), + ) + } else { + ( + ArchitectureSize { + bytes: 0, + pointers: 0, + }, + Alignment::default(), + ) + }; + let size = size.size_wasm32(); + let align = align.align_wasm32(); + let (lower, lift) = if let Some(payload_type) = payload_type { + let lower = + self.lower_to_memory("address", "value", &payload_type, &module); + let lift = + self.lift_from_memory("address", "value", &payload_type, &module); + (lower, lift) + } else { + (String::new(), "let value = ();\n".into()) + }; + + uwriteln!( + self.src, + r#" +impl {async_support}::FuturePayload for {name} {{ + fn new() -> u32 {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-new-{index}]{func_name}"] + fn new() -> u32; + }} + unsafe {{ new() }} + }} + }} + + async fn write(future: u32, value: Self) -> bool {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[repr(align({align}))] + struct Buffer([::core::mem::MaybeUninit::; {size}]); + 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; + }} + + unsafe {{ {async_support}::await_future_result(wit_import, future, address).await }} + }} + }} + + async fn read(future: u32) -> Option {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + struct Buffer([::core::mem::MaybeUninit::; {size}]); + let mut buffer = Buffer([::core::mem::MaybeUninit::uninit(); {size}]); + let address = buffer.0.as_mut_ptr() as *mut u8; + + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[async][future-read-{index}]{func_name}"] + fn wit_import(_: u32, _: *mut u8) -> u32; + }} + + if unsafe {{ {async_support}::await_future_result(wit_import, future, address).await }} {{ + {lift} + Some(value) + }} else {{ + None + }} + }} + }} + + fn drop_writer(writer: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-drop-writer-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(writer) }} + }} + }} + + fn drop_reader(reader: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-drop-reader-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(reader) }} + }} + }} +}} + "#, + ); + } + } + TypeDefKind::Stream(payload_type) => { + let name = { + let old = mem::take(&mut self.src); + self.print_ty(&payload_type, type_mode); + String::from(mem::replace(&mut self.src, old)) + }; + + let full_name = { + let old = mem::take(&mut self.src); + let old_identifier = mem::replace(&mut self.identifier, Identifier::None); + self.print_ty(&payload_type, type_mode); + self.identifier = old_identifier; + String::from(mem::replace(&mut self.src, old)) + }; + + if self.gen.stream_payloads_emitted.insert(full_name) { + let size = self.sizes.size(payload_type).size_wasm32(); + let align = self.sizes.align(payload_type).align_wasm32(); + let alloc = self.path_to_std_alloc_module(); + let (lower_address, lower, lift_address, lift) = + if stream_direct(payload_type) { + let lower_address = "let address = values.as_ptr() as _;".into(); + let lift_address = "let address = values.as_mut_ptr() as _;".into(); + ( + lower_address, + String::new(), + lift_address, + "let value = ();\n".into(), + ) + } else { + let address = format!( + "let address = {alloc}::alloc\ + ({alloc}::Layout::from_size_align_unchecked\ + ({size} * values.len(), {align}));" + ); + let lower = self.lower_to_memory( + "address", + "value", + &payload_type, + &module, + ); + let lower = format!( + r#" +for (index, value) in values.iter().enumerate() {{ + let address = address + (index * size); + {lower} +}} + "# + ); + let lift = self.lift_from_memory( + "address", + "value", + &payload_type, + &module, + ); + let lift = format!( + r#" +for (index, dst) in values.iter_mut().enumerate() {{ + let address = address + (index * size); + {lift} + *dst = value; +}} + "# + ); + (address.clone(), lower, address, lift) + }; + + uwriteln!( + self.src, + r#" +impl {async_support}::StreamPayload for {name} {{ + fn new() -> u32 {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-new-{index}]{func_name}"] + fn new() -> u32; + }} + unsafe {{ new() }} + }} + }} + + async fn write(stream: u32, values: &[Self]) -> Option {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + {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; + }} + + unsafe {{ + {async_support}::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await + }} + }} + }} + + async fn read(stream: u32, values: &mut [::core::mem::MaybeUninit::]) -> Option {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + {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; + }} + + let count = unsafe {{ + {async_support}::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await + }}; + if let Some(count) = count {{ + {lift} + }} + count + }} + }} + + fn drop_writer(writer: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-drop-writer-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(writer) }} + }} + }} + + fn drop_reader(reader: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-drop-reader-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(reader) }} + }} + }} +}} + "# + ); + } + } + _ => unreachable!(), + } + } + } + + fn generate_guest_import(&mut self, func: &Function, interface: Option<&WorldKey>) { if self.gen.skip.contains(&func.name) { return; } - let mut sig = FnSig::default(); + self.generate_payloads("[import-payload]", func, interface); + + let async_ = match &self.gen.opts.async_ { + AsyncConfig::None => false, + AsyncConfig::All => true, + AsyncConfig::Some { imports, .. } => imports.contains(&if let Some(key) = interface { + format!("{}#{}", self.resolve.name_world_key(key), func.name) + } else { + func.name.clone() + }), + }; + let mut sig = FnSig { + async_, + ..Default::default() + }; match func.kind { FunctionKind::Freestanding => {} FunctionKind::Method(id) | FunctionKind::Static(id) | FunctionKind::Constructor(id) => { @@ -474,17 +866,53 @@ macro_rules! {macro_name} {{ } } self.src.push_str("#[allow(unused_unsafe, clippy::all)]\n"); - let params = self.print_signature(func, false, &sig); + let params = self.print_signature(func, false, &sig, true); self.src.push_str("{\n"); self.src.push_str("unsafe {\n"); - let mut f = FunctionBindgen::new(self, params); + self.generate_guest_import_body(&self.wasm_import_module, func, params, async_); + + self.src.push_str("}\n"); + self.src.push_str("}\n"); + + match func.kind { + FunctionKind::Freestanding => {} + FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::Constructor(_) => { + self.src.push_str("}\n"); + } + } + } + + fn lower_to_memory(&mut self, address: &str, value: &str, ty: &Type, module: &str) -> String { + let mut f = FunctionBindgen::new(self, Vec::new(), true, module); + abi::lower_to_memory(f.gen.resolve, &mut f, address.into(), value.into(), ty); + format!("unsafe {{ {} }}", String::from(f.src)) + } + + fn lift_from_memory(&mut self, address: &str, value: &str, ty: &Type, module: &str) -> String { + let mut f = FunctionBindgen::new(self, Vec::new(), true, module); + let result = abi::lift_from_memory(f.gen.resolve, &mut f, address.into(), ty); + format!( + "let {value} = unsafe {{ {}\n{result} }};", + String::from(f.src) + ) + } + + fn generate_guest_import_body( + &mut self, + module: &str, + func: &Function, + params: Vec, + async_: bool, + ) { + let mut f = FunctionBindgen::new(self, params, async_, module); abi::call( f.gen.resolve, AbiVariant::GuestImport, LiftLower::LowerArgsLiftResults, func, &mut f, + async_, ); let FunctionBindgen { needs_cleanup_list, @@ -511,29 +939,28 @@ macro_rules! {macro_name} {{ ); } self.src.push_str(&String::from(src)); - - self.src.push_str("}\n"); - self.src.push_str("}\n"); - - match func.kind { - FunctionKind::Freestanding => {} - FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::Constructor(_) => { - self.src.push_str("}\n"); - } - } } - fn generate_guest_export(&mut self, func: &Function, trait_name: &str) { + fn generate_guest_export( + &mut self, + func: &Function, + interface: Option<&WorldKey>, + trait_name: &str, + async_: bool, + ) { let name_snake = func.name.to_snake_case().replace('.', "_"); + + self.generate_payloads("[export-payload]", func, interface); + uwrite!( self.src, "\ #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn _export_{name_snake}_cabi\ -", + ", ); - let params = self.print_export_sig(func); + let params = self.print_export_sig(func, async_); self.push_str(" {"); if !self.gen.opts.disable_run_ctors_once_workaround { @@ -552,13 +979,14 @@ macro_rules! {macro_name} {{ ); } - let mut f = FunctionBindgen::new(self, params); + let mut f = FunctionBindgen::new(self, params, async_, self.wasm_import_module); abi::call( f.gen.resolve, AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, func, &mut f, + async_, ); let FunctionBindgen { needs_cleanup_list, @@ -574,20 +1002,32 @@ macro_rules! {macro_name} {{ self.src.push_str(&String::from(src)); self.src.push_str("}\n"); - if abi::guest_export_needs_post_return(self.resolve, func) { + if async_ { + let async_support = self.path_to_async_support(); + uwrite!( + self.src, + "\ + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __callback_{name_snake}(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 {{ + {async_support}::callback(ctx, event0, event1, event2) + }} + " + ); + } else if abi::guest_export_needs_post_return(self.resolve, func) { uwrite!( self.src, "\ #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn __post_return_{name_snake}\ -" + " ); let params = self.print_post_return_sig(func); self.src.push_str("{\n"); - let mut f = FunctionBindgen::new(self, params); - abi::post_return(f.gen.resolve, func, &mut f); + let mut f = FunctionBindgen::new(self, params, async_, self.wasm_import_module); + abi::post_return(f.gen.resolve, func, &mut f, async_); let FunctionBindgen { needs_cleanup_list, src, @@ -601,14 +1041,26 @@ macro_rules! {macro_name} {{ } } - fn generate_raw_cabi_export(&mut self, func: &Function, ty: &str, path_to_self: &str) { + fn generate_raw_cabi_export( + &mut self, + func: &Function, + ty: &str, + path_to_self: &str, + async_: bool, + ) { let name_snake = func.name.to_snake_case().replace('.', "_"); let wasm_module_export_name = match self.identifier { Identifier::Interface(_, key) => Some(self.resolve.name_world_key(key)), Identifier::World(_) => None, + Identifier::None => unreachable!(), }; let export_prefix = self.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]{export_name}") + } else { + export_name.to_string() + }; uwrite!( self.src, "\ @@ -617,7 +1069,7 @@ macro_rules! {macro_name} {{ ", ); - let params = self.print_export_sig(func); + let params = self.print_export_sig(func, async_); self.push_str(" {\n"); uwriteln!( self.src, @@ -626,8 +1078,18 @@ macro_rules! {macro_name} {{ ); self.push_str("}\n"); - if abi::guest_export_needs_post_return(self.resolve, func) { - let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); + let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); + if async_ { + uwrite!( + self.src, + "\ + #[export_name = \"{export_prefix}[callback]{export_name}\"] + unsafe extern \"C\" fn _callback_{name_snake}(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 {{ + {path_to_self}::__callback_{name_snake}(ctx, event0, event1, event2) + }} + " + ); + } else if abi::guest_export_needs_post_return(self.resolve, func) { uwrite!( self.src, "\ @@ -646,7 +1108,7 @@ macro_rules! {macro_name} {{ } } - fn print_export_sig(&mut self, func: &Function) -> Vec { + fn print_export_sig(&mut self, func: &Function, async_: bool) -> Vec { self.src.push_str("("); let sig = self.resolve.wasm_signature(AbiVariant::GuestExport, func); let mut params = Vec::new(); @@ -657,13 +1119,18 @@ macro_rules! {macro_name} {{ } self.src.push_str(")"); - match sig.results.len() { - 0 => {} - 1 => { - uwrite!(self.src, " -> {}", wasm_type(sig.results[0])); + if async_ { + self.push_str(" -> *mut u8"); + } else { + match sig.results.len() { + 0 => {} + 1 => { + uwrite!(self.src, " -> {}", wasm_type(sig.results[0])); + } + _ => unimplemented!(), } - _ => unimplemented!(), } + params } @@ -740,7 +1207,7 @@ macro_rules! {macro_name} {{ sig.self_arg = Some("&self".into()); sig.self_is_first_param = true; } - self.print_signature(func, true, &sig); + self.print_signature(func, true, &sig, true); self.src.push_str("{ unreachable!() }\n"); } @@ -798,12 +1265,18 @@ macro_rules! {macro_name} {{ // } } - fn print_signature(&mut self, func: &Function, params_owned: bool, sig: &FnSig) -> Vec { - let params = self.print_docs_and_params(func, params_owned, sig); + fn print_signature( + &mut self, + func: &Function, + params_owned: bool, + sig: &FnSig, + use_async_sugar: bool, + ) -> Vec { + let params = self.print_docs_and_params(func, params_owned, sig, use_async_sugar); if let FunctionKind::Constructor(_) = &func.kind { self.push_str(" -> Self") } else { - self.print_results(&func.results); + self.print_results(&func.results, sig.async_ && !use_async_sugar); } params } @@ -813,6 +1286,7 @@ macro_rules! {macro_name} {{ func: &Function, params_owned: bool, sig: &FnSig, + use_async_sugar: bool, ) -> Vec { self.rustdoc(&func.docs); self.rustdoc_params(&func.params, "Parameters"); @@ -825,7 +1299,7 @@ macro_rules! {macro_name} {{ if sig.unsafe_ { self.push_str("unsafe "); } - if sig.async_ { + if sig.async_ && use_async_sugar { self.push_str("async "); } self.push_str("fn "); @@ -915,18 +1389,24 @@ macro_rules! {macro_name} {{ params } - fn print_results(&mut self, results: &Results) { + fn print_results(&mut self, results: &Results, async_: bool) { + self.push_str(" -> "); + if async_ { + self.push_str("impl ::core::future::Future {} + 0 => { + self.push_str("()"); + } 1 => { - self.push_str(" -> "); let ty = results.iter_types().next().unwrap(); let mode = self.type_mode_for(ty, TypeOwnershipStyle::Owned, "'INVALID"); assert!(mode.lifetime.is_none()); self.print_ty(ty, mode); } _ => { - self.push_str(" -> ("); + self.push_str("("); for ty in results.iter_types() { let mode = self.type_mode_for(ty, TypeOwnershipStyle::Owned, "'INVALID"); assert!(mode.lifetime.is_none()); @@ -936,6 +1416,10 @@ macro_rules! {macro_name} {{ self.push_str(")") } } + + if async_ { + self.push_str("> + 'static"); + } } /// Calculates the `TypeMode` to be used for the `ty` specified. @@ -1863,6 +2347,10 @@ macro_rules! {macro_name} {{ self.path_from_runtime_module(RuntimeItem::StdAllocModule, "alloc") } + pub fn path_to_async_support(&mut self) -> String { + self.path_from_runtime_module(RuntimeItem::AsyncSupport, "async_support") + } + fn path_from_runtime_module( &mut self, item: RuntimeItem, @@ -1920,11 +2408,12 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { }} "# ); - self.wasm_import_module.unwrap().to_string() + self.wasm_import_module.to_string() } else { let module = match self.identifier { Identifier::Interface(_, key) => self.resolve.name_world_key(key), Identifier::World(_) => unimplemented!("resource exports from worlds"), + Identifier::None => unreachable!(), }; let box_path = self.path_to_box(); uwriteln!( @@ -2265,18 +2754,41 @@ impl<'a, 'b> wit_bindgen_core::AnonymousTypeGenerator<'a> for AnonTypeGenerator< } fn anonymous_type_future(&mut self, _id: TypeId, ty: &Option, _docs: &Docs) { - self.interface.push_str("Future<"); + let async_support = self.interface.path_to_async_support(); + self.interface + .push_str(&format!("{async_support}::FutureReader<")); self.interface.print_optional_ty(ty.as_ref(), self.mode); self.interface.push_str(">"); } - fn anonymous_type_stream(&mut self, _id: TypeId, stream: &Stream, _docs: &Docs) { - self.interface.push_str("Stream<"); - self.interface - .print_optional_ty(stream.element.as_ref(), self.mode); - self.interface.push_str(","); + fn anonymous_type_stream(&mut self, _id: TypeId, ty: &Type, _docs: &Docs) { + let async_support = self.interface.path_to_async_support(); self.interface - .print_optional_ty(stream.end.as_ref(), self.mode); + .push_str(&format!("{async_support}::StreamReader<")); + self.interface.print_ty(ty, self.mode); self.interface.push_str(">"); } + + fn anonymous_type_error(&mut self) { + let async_support = self.interface.path_to_async_support(); + self.interface.push_str(&format!("{async_support}::Error")); + } +} + +fn stream_direct(ty: &Type) -> bool { + // TODO: might be able to return `true` for other types if the generated Rust versions of those types are + // guaranteed to be safely transmutable to and from their lowered form. + matches!( + ty, + Type::U8 + | Type::S8 + | Type::U16 + | Type::S16 + | Type::U32 + | Type::S32 + | Type::U64 + | Type::S64 + | Type::F32 + | Type::F64 + ) } diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 6edb6be27..a78970600 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -44,8 +44,12 @@ struct RustWasm { rt_module: IndexSet, export_macros: Vec<(String, String)>, + /// Interface names to how they should be generated with: GenerationConfiguration, + + future_payloads_emitted: HashSet, + stream_payloads_emitted: HashSet, } #[derive(Default)] @@ -97,6 +101,7 @@ enum RuntimeItem { AsF64, ResourceType, BoxType, + AsyncSupport, } #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -117,6 +122,23 @@ fn parse_with(s: &str) -> Result<(String, WithOption), String> { Ok((k.to_string(), v)) } +#[derive(Default, Debug, Clone)] +pub enum AsyncConfig { + #[default] + None, + Some { + imports: Vec, + exports: Vec, + }, + All, +} + +#[cfg(feature = "clap")] +fn parse_async(s: &str) -> Result { + _ = s; + Err("todo: parse `AsyncConfig`".into()) +} + #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "clap", derive(clap::Args))] pub struct Opts { @@ -234,6 +256,10 @@ pub struct Opts { /// library-based usage of `generate!` prone to breakage. #[cfg_attr(feature = "clap", arg(long))] pub disable_custom_section_link_helpers: bool, + + /// Determines which functions to lift or lower `async`, if any. + #[cfg_attr(feature = "clap", arg(long = "async", value_parser = parse_async))] + pub async_: AsyncConfig, } impl Opts { @@ -253,7 +279,7 @@ impl RustWasm { fn interface<'a>( &'a mut self, identifier: Identifier<'a>, - wasm_import_module: Option<&'a str>, + wasm_import_module: &'a str, resolve: &'a Resolve, in_import: bool, ) -> InterfaceGenerator<'a> { @@ -362,6 +388,11 @@ impl RustWasm { } fn finish_runtime_module(&mut self) { + // TODO: This is a hack because there are currently functions and types in the `async_support` module that + // are useful to applications even if the generated bindings don't use it. We should probably move those + // items to a library which the application can add as a dependency. + self.rt_module.insert(RuntimeItem::AsyncSupport); + if self.rt_module.is_empty() { return; } @@ -375,6 +406,9 @@ impl RustWasm { } } self.src.push_str("}\n"); + if emitted.contains(&RuntimeItem::AsyncSupport) { + self.src.push_str("pub use _rt::async_support;\n"); + } } fn emit_runtime_item(&mut self, item: RuntimeItem) { @@ -607,6 +641,12 @@ impl Drop for Resource { "#, ); } + + RuntimeItem::AsyncSupport => { + self.src.push_str("pub mod async_support {"); + self.src.push_str(include_str!("async_support.rs")); + self.src.push_str("}"); + } } } @@ -909,7 +949,7 @@ impl WorldGenerator for RustWasm { let wasm_import_module = resolve.name_world_key(name); let mut gen = self.interface( Identifier::Interface(id, name), - Some(&wasm_import_module), + &wasm_import_module, resolve, true, ); @@ -919,7 +959,7 @@ impl WorldGenerator for RustWasm { } gen.types(id); - gen.generate_imports(resolve.interfaces[id].functions.values()); + gen.generate_imports(resolve.interfaces[id].functions.values(), Some(name)); gen.finish_append_submodule(&snake, module_path); @@ -935,9 +975,9 @@ impl WorldGenerator for RustWasm { ) { self.import_funcs_called = true; - let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true); + let mut gen = self.interface(Identifier::World(world), "$root", resolve, true); - gen.generate_imports(funcs.iter().map(|(_, func)| *func)); + gen.generate_imports(funcs.iter().map(|(_, func)| *func), None); let src = gen.finish(); self.src.push_str(&src); @@ -951,7 +991,13 @@ impl WorldGenerator for RustWasm { _files: &mut Files, ) -> Result<()> { self.interface_last_seen_as_import.insert(id, false); - let mut gen = self.interface(Identifier::Interface(id, name), None, resolve, false); + let wasm_import_module = format!("[export]{}", resolve.name_world_key(name)); + let mut gen = self.interface( + Identifier::Interface(id, name), + &wasm_import_module, + resolve, + false, + ); let (snake, module_path) = gen.start_append_submodule(name); if gen.gen.name_interface(resolve, id, name, true)? { return Ok(()); @@ -965,7 +1011,12 @@ impl WorldGenerator for RustWasm { if self.opts.stubs { let world_id = self.world.unwrap(); - let mut gen = self.interface(Identifier::World(world_id), None, resolve, false); + let mut gen = self.interface( + Identifier::World(world_id), + &wasm_import_module, + resolve, + false, + ); gen.generate_stub(Some((id, name)), resolve.interfaces[id].functions.values()); let stub = gen.finish(); self.src.push_str(&stub); @@ -980,14 +1031,14 @@ impl WorldGenerator for RustWasm { funcs: &[(&str, &Function)], _files: &mut Files, ) -> Result<()> { - let mut gen = self.interface(Identifier::World(world), None, resolve, false); + let mut gen = self.interface(Identifier::World(world), "[export]$root", resolve, false); let macro_name = gen.generate_exports(None, funcs.iter().map(|f| f.1))?; let src = gen.finish(); self.src.push_str(&src); self.export_macros.push((macro_name, String::new())); if self.opts.stubs { - let mut gen = self.interface(Identifier::World(world), None, resolve, false); + let mut gen = self.interface(Identifier::World(world), "[export]$root", resolve, false); gen.generate_stub(None, funcs.iter().map(|f| f.1)); let stub = gen.finish(); self.src.push_str(&stub); @@ -1002,7 +1053,7 @@ impl WorldGenerator for RustWasm { types: &[(&str, TypeId)], _files: &mut Files, ) { - let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true); + let mut gen = self.interface(Identifier::World(world), "$root", resolve, true); for (name, ty) in types { gen.define_type(name, *ty); } @@ -1147,6 +1198,7 @@ fn compute_module_path(name: &WorldKey, resolve: &Resolve, is_export: bool) -> V } enum Identifier<'a> { + None, World(WorldId), Interface(InterfaceId, &'a WorldKey), } diff --git a/crates/teavm-java/src/lib.rs b/crates/teavm-java/src/lib.rs index 1d0c90593..00b75cfd1 100644 --- a/crates/teavm-java/src/lib.rs +++ b/crates/teavm-java/src/lib.rs @@ -501,6 +501,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut bindgen, + false, ); let src = bindgen.src; @@ -570,6 +571,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut bindgen, + false, ); assert!(!bindgen.needs_cleanup_list); @@ -623,7 +625,7 @@ impl InterfaceGenerator<'_> { (0..sig.results.len()).map(|i| format!("p{i}")).collect(), ); - abi::post_return(bindgen.gen.resolve, func, &mut bindgen); + abi::post_return(bindgen.gen.resolve, func, &mut bindgen, false); let src = bindgen.src; @@ -2028,6 +2030,11 @@ impl Bindgen for FunctionBindgen<'_, '_> { "Memory.free(org.teavm.interop.Address.fromInt({address}), ({length}) * {size}, {align});" ); } + + Instruction::AsyncMalloc { .. } + | Instruction::AsyncCallStart { .. } + | Instruction::AsyncPostCallInterface { .. } + | Instruction::AsyncCallReturn { .. } => todo!(), } } From 00845f8ee90dc60754417b432c60e0efac532f30 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Nov 2024 10:46:20 +0100 Subject: [PATCH 382/672] update wasm-tools dependency --- Cargo.lock | 130 ++++++++++++++++++++++++++++------------------------- 1 file changed, 68 insertions(+), 62 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a4d1789a1..5f9aaae4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -478,7 +478,7 @@ dependencies = [ "itertools", "log", "smallvec", - "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.217.0", "wasmtime-types", ] @@ -1462,10 +1462,10 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.219.1", "wit-bindgen-core", "wit-component", - "wit-parser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.219.1", ] [[package]] @@ -1633,6 +1633,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -1740,26 +1746,26 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.217.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#f85ac59e0670f0f80f624b5a72effaaa261542ee" +version = "0.218.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22b896fa8ceb71091ace9bcb81e853f54043183a1c9667cf93422c40252ffa0a" dependencies = [ "leb128", - "wasmparser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] name = "wasm-encoder" -version = "0.218.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22b896fa8ceb71091ace9bcb81e853f54043183a1c9667cf93422c40252ffa0a" +version = "0.219.1" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#66b7df9bcb50647947446d8cce7c480b9768f775" dependencies = [ "leb128", + "wasmparser 0.219.1", ] [[package]] name = "wasm-metadata" -version = "0.217.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#f85ac59e0670f0f80f624b5a72effaaa261542ee" +version = "0.219.1" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#66b7df9bcb50647947446d8cce7c480b9768f775" dependencies = [ "anyhow", "indexmap", @@ -1767,8 +1773,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", - "wasmparser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.219.1", + "wasmparser 0.219.1", ] [[package]] @@ -1787,8 +1793,8 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.217.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#f85ac59e0670f0f80f624b5a72effaaa261542ee" +version = "0.219.1" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#66b7df9bcb50647947446d8cce7c480b9768f775" dependencies = [ "ahash", "bitflags", @@ -1806,7 +1812,7 @@ checksum = "50dc568b3e0d47e8f96ea547c90790cfa783f0205160c40de894a427114185ce" dependencies = [ "anyhow", "termcolor", - "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.217.0", ] [[package]] @@ -1847,8 +1853,8 @@ dependencies = [ "smallvec", "sptr", "target-lexicon", - "wasm-encoder 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder 0.217.0", + "wasmparser 0.217.0", "wasmtime-asm-macros", "wasmtime-cache", "wasmtime-component-macro", @@ -1906,7 +1912,7 @@ dependencies = [ "syn", "wasmtime-component-util", "wasmtime-wit-bindgen", - "wit-parser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wit-parser 0.217.0", ] [[package]] @@ -1935,7 +1941,7 @@ dependencies = [ "smallvec", "target-lexicon", "thiserror", - "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.217.0", "wasmtime-environ", "wasmtime-versioned-export-macros", ] @@ -1960,8 +1966,8 @@ dependencies = [ "serde", "serde_derive", "target-lexicon", - "wasm-encoder 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder 0.217.0", + "wasmparser 0.217.0", "wasmprinter", "wasmtime-component-util", "wasmtime-types", @@ -2023,7 +2029,7 @@ dependencies = [ "serde", "serde_derive", "smallvec", - "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.217.0", ] [[package]] @@ -2079,7 +2085,7 @@ dependencies = [ "gimli 0.29.0", "object", "target-lexicon", - "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.217.0", "wasmtime-cranelift", "wasmtime-environ", "winch-codegen", @@ -2094,7 +2100,7 @@ dependencies = [ "anyhow", "heck 0.4.1", "indexmap", - "wit-parser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wit-parser 0.217.0", ] [[package]] @@ -2108,44 +2114,44 @@ dependencies = [ [[package]] name = "wast" -version = "217.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#f85ac59e0670f0f80f624b5a72effaaa261542ee" +version = "218.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a53cd1f0fa505df97557e36a58bddb8296e2fcdcd089529545ebfdb18a1b9d7" dependencies = [ "bumpalo", "leb128", "memchr", - "unicode-width", - "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "unicode-width 0.1.14", + "wasm-encoder 0.218.0", ] [[package]] name = "wast" -version = "218.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a53cd1f0fa505df97557e36a58bddb8296e2fcdcd089529545ebfdb18a1b9d7" +version = "219.0.1" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#66b7df9bcb50647947446d8cce7c480b9768f775" dependencies = [ "bumpalo", "leb128", "memchr", - "unicode-width", - "wasm-encoder 0.218.0", + "unicode-width 0.2.0", + "wasm-encoder 0.219.1", ] [[package]] name = "wat" -version = "1.217.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#f85ac59e0670f0f80f624b5a72effaaa261542ee" +version = "1.218.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f87f8e14e776762e07927c27c2054d2cf678aab9aae2d431a79b3e31e4dd391" dependencies = [ - "wast 217.0.0", + "wast 218.0.0", ] [[package]] name = "wat" -version = "1.218.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f87f8e14e776762e07927c27c2054d2cf678aab9aae2d431a79b3e31e4dd391" +version = "1.219.1" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#66b7df9bcb50647947446d8cce7c480b9768f775" dependencies = [ - "wast 218.0.0", + "wast 219.0.1", ] [[package]] @@ -2233,7 +2239,7 @@ dependencies = [ "regalloc2", "smallvec", "target-lexicon", - "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.217.0", "wasmtime-cranelift", "wasmtime-environ", ] @@ -2375,11 +2381,11 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.219.1", "wasm-metadata", "wit-bindgen-core", "wit-component", - "wit-parser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.219.1", ] [[package]] @@ -2390,8 +2396,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", - "wasmparser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.219.1", + "wasmparser 0.219.1", "wasmtime", "wasmtime-wasi", "wit-bindgen-bridge", @@ -2405,7 +2411,7 @@ dependencies = [ "wit-bindgen-rust", "wit-bindgen-teavm-java", "wit-component", - "wit-parser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.219.1", ] [[package]] @@ -2414,7 +2420,7 @@ version = "0.34.0" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.219.1", ] [[package]] @@ -2425,7 +2431,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.219.1", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", @@ -2441,12 +2447,12 @@ dependencies = [ "heck 0.5.0", "indexmap", "test-helpers", - "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.219.1", "wasm-metadata", - "wasmparser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.219.1", "wit-bindgen-core", "wit-component", - "wit-parser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.219.1", ] [[package]] @@ -2540,8 +2546,8 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.217.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#f85ac59e0670f0f80f624b5a72effaaa261542ee" +version = "0.219.1" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#66b7df9bcb50647947446d8cce7c480b9768f775" dependencies = [ "anyhow", "bitflags", @@ -2550,11 +2556,11 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.219.1", "wasm-metadata", - "wasmparser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", - "wat 1.217.0", - "wit-parser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.219.1", + "wat 1.219.1", + "wit-parser 0.219.1", ] [[package]] @@ -2572,13 +2578,13 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.217.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.217.0", ] [[package]] name = "wit-parser" -version = "0.217.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#f85ac59e0670f0f80f624b5a72effaaa261542ee" +version = "0.219.1" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#66b7df9bcb50647947446d8cce7c480b9768f775" dependencies = [ "anyhow", "id-arena", @@ -2589,7 +2595,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.217.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.219.1", ] [[package]] From 59b14a8cf1f55586d44ebe534266baa6d64c666e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Nov 2024 13:53:03 +0100 Subject: [PATCH 383/672] small fix --- crates/c/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 89c4eb229..8e0c97a9c 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -1440,7 +1440,7 @@ impl<'a> wit_bindgen_core::AnonymousTypeGenerator<'a> for InterfaceGenerator<'a> todo!("print_anonymous_type for typ"); } - fn anonymous_type_error(&mut self, _id: TypeId, _docs: &Docs) { + fn anonymous_type_error(&mut self) { todo!() } } From a73e05ec82438ff6c0b10a39c6e4efaf8fb9e961 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Nov 2024 14:48:58 +0100 Subject: [PATCH 384/672] fix compilation after merge --- crates/c/src/lib.rs | 9 +------ crates/core/src/abi.rs | 50 ++++++++++++++++-------------------- crates/core/src/types.rs | 1 - crates/csharp/src/lib.rs | 10 +++++++- crates/rust/src/bindgen.rs | 17 ------------ crates/rust/src/lib.rs | 20 ++++----------- crates/teavm-java/src/lib.rs | 8 ++++++ 7 files changed, 45 insertions(+), 70 deletions(-) diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 8e0c97a9c..60331c5b9 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -2093,14 +2093,7 @@ impl InterfaceGenerator<'_> { .as_ref() .map_or(false, |ty| self.contains_droppable_borrow(ty)), - TypeDefKind::Stream(s) => { - s.element - .as_ref() - .map_or(false, |ty| self.contains_droppable_borrow(ty)) - || s.end - .as_ref() - .map_or(false, |ty| self.contains_droppable_borrow(ty)) - } + TypeDefKind::Stream(ty) => self.contains_droppable_borrow(ty), TypeDefKind::Type(ty) => self.contains_droppable_borrow(ty), diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 9e886b43d..4d9744b8c 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -765,7 +765,7 @@ pub fn lower_to_memory( true, ); generator.stack.push(value); - generator.write_to_memory(ty, address, 0); + generator.write_to_memory(ty, address, Default::default()); } pub fn lift_from_memory( @@ -782,7 +782,7 @@ pub fn lift_from_memory( bindgen, true, ); - generator.read_from_memory(ty, address, 0); + generator.read_from_memory(ty, address, Default::default()); generator.stack.pop().unwrap() } @@ -889,15 +889,13 @@ 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; - const MAX_FLAT_RESULTS: usize = 1; + // const MAX_FLAT_RESULTS: usize = 1; let language_to_abi = matches!(self.lift_lower, LiftLower::LowerArgsLiftResults) || (matches!(self.lift_lower, LiftLower::Symmetric) @@ -1081,24 +1079,24 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - if let Some(results) = async_results { - let name = &format!("[task-return]{}", func.name); - - self.emit(&Instruction::AsyncCallReturn { - name, - params: &if results.len() > MAX_FLAT_PARAMS { - vec![WasmType::Pointer] - } else { - results - }, - }); - self.emit(&Instruction::Return { func, amt: 1 }); - } else { - self.emit(&Instruction::Return { - func, - amt: sig.results.len(), - }); - } + // if let Some(results) = async_results { + // let name = &format!("[task-return]{}", func.name); + + // self.emit(&Instruction::AsyncCallReturn { + // name, + // params: &if results.len() > MAX_FLAT_PARAMS { + // vec![WasmType::Pointer] + // } else { + // results + // }, + // }); + // self.emit(&Instruction::Return { func, amt: 1 }); + // } else { + self.emit(&Instruction::Return { + func, + amt: sig.results.len(), + }); + // } } false => { if let (AbiVariant::GuestImport, true) = (self.variant, self.async_) { @@ -1147,7 +1145,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { // self.lift(ty); // } // } - // } else + // } else if !sig.indirect_params { // If parameters are not passed indirectly then we lift each // argument in succession from the component wasm types that @@ -1254,10 +1252,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.stack.push(ptr); } } - - AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { - unreachable!() - } } if matches!( diff --git a/crates/core/src/types.rs b/crates/core/src/types.rs index dc2f39ffc..94c038cf3 100644 --- a/crates/core/src/types.rs +++ b/crates/core/src/types.rs @@ -195,7 +195,6 @@ impl Types { } TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Error => {} TypeDefKind::Unknown => unreachable!(), - TypeDefKind::Error => {} } let prev = self.type_info.insert(ty, info); assert!(prev.is_none()); diff --git a/crates/csharp/src/lib.rs b/crates/csharp/src/lib.rs index 04cab0cbd..6de0848fd 100644 --- a/crates/csharp/src/lib.rs +++ b/crates/csharp/src/lib.rs @@ -2594,7 +2594,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { operands[0], operands[1] )), - Instruction::ListLower { element, realloc } => { + Instruction::ListLower { element, .. } => { let Block { body, results: block_results, @@ -3015,6 +3015,14 @@ impl Bindgen for FunctionBindgen<'_, '_> { | Instruction::AsyncCallStart { .. } | Instruction::AsyncPostCallInterface { .. } | Instruction::AsyncCallReturn { .. } => todo!(), + Instruction::FutureLower { .. } | + Instruction::FutureLift { .. } | + Instruction::StreamLower { .. } | + Instruction::StreamLift { .. } | + Instruction::ErrorLower { .. } | + Instruction::ErrorLift { .. } | + Instruction::AsyncCallWasm { .. } | + Instruction::Flush { .. } => todo!(), } } diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index d953ea5eb..72686d839 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -951,23 +951,6 @@ impl Bindgen for FunctionBindgen<'_, '_> { ); } - Instruction::AsyncCallWasm { name, size, align } => { - let func = self.declare_import(name, &[WasmType::Pointer; 2], &[WasmType::I32]); - - let async_support = self.gen.path_to_async_support(); - let tmp = self.tmp(); - let layout = format!("layout{tmp}"); - let alloc = self.gen.path_to_std_alloc_module(); - self.push_str(&format!( - "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align});\n", - )); - let operands = operands.join(", "); - uwriteln!( - self.src, - "{async_support}::await_result({func}, {layout}, {operands}).await;" - ); - } - Instruction::CallInterface { func, .. } => { if self.async_ { let tmp = self.tmp(); diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index c8dd0ae7c..2adb4d0cb 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -56,6 +56,10 @@ struct RustWasm { future_payloads_emitted: HashSet, stream_payloads_emitted: HashSet, + + // needed for symmetric disambiguation + interface_prefixes: HashMap<(Direction, WorldKey), String>, + import_prefix: Option, } #[derive(Default)] @@ -276,10 +280,6 @@ pub struct Opts { #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))] pub invert_direction: bool, - /// Determines which functions to lift or lower `async`, if any. - #[cfg_attr(feature = "clap", arg(long = "async", value_parser = parse_async, default_value_t = Default::default()))] - pub async_: AsyncConfig, - /// Whether or not to generate helper function/constants to help link custom /// sections into the final output. /// @@ -289,7 +289,7 @@ pub struct Opts { pub disable_custom_section_link_helpers: bool, /// Determines which functions to lift or lower `async`, if any. - #[cfg_attr(feature = "clap", arg(long = "async", value_parser = parse_async))] + #[cfg_attr(feature = "clap", arg(long = "async", value_parser = parse_async, default_value_t = Default::default()))] pub async_: AsyncConfig, } @@ -690,12 +690,6 @@ impl Drop for Resource {{ self.src.push_str(include_str!("async_support.rs")); self.src.push_str("}"); } - - RuntimeItem::AsyncSupport => { - self.src.push_str("pub mod async_support {"); - self.src.push_str(include_str!("async_support.rs")); - self.src.push_str("}"); - } } } @@ -985,10 +979,6 @@ impl WorldGenerator for RustWasm { self.with.insert(k.clone(), v.clone().into()); } self.with.generate_by_default = self.opts.generate_all; - - let (unit_result, payload_results) = resolve.find_future_and_stream_results(); - self.unit_result = unit_result; - self.payload_results = payload_results; } fn import_interface( diff --git a/crates/teavm-java/src/lib.rs b/crates/teavm-java/src/lib.rs index 23536402d..50228edba 100644 --- a/crates/teavm-java/src/lib.rs +++ b/crates/teavm-java/src/lib.rs @@ -2053,6 +2053,14 @@ impl Bindgen for FunctionBindgen<'_, '_> { | Instruction::AsyncCallStart { .. } | Instruction::AsyncPostCallInterface { .. } | Instruction::AsyncCallReturn { .. } => todo!(), + Instruction::FutureLower { .. } + | Instruction::FutureLift { .. } + | Instruction::StreamLower { .. } + | Instruction::StreamLift { .. } + | Instruction::ErrorLower { .. } + | Instruction::ErrorLift { .. } + | Instruction::AsyncCallWasm { .. } + | Instruction::Flush { .. } => todo!(), } } From 2f305f1512ffaf35d902b346d87a4f944a3a547b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Nov 2024 22:18:18 +0100 Subject: [PATCH 385/672] fix symmetric testing --- Cargo.lock | 45 +++++++++++++++++++ crates/core/src/abi.rs | 2 +- crates/cpp/tests/symmetric.rs | 9 +++- .../rust-xcrate-test/Cargo.toml | 4 ++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5f9aaae4b..303164248 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,6 +128,12 @@ dependencies = [ "syn", ] +[[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" @@ -680,6 +686,7 @@ checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -702,12 +709,34 @@ 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" @@ -726,11 +755,16 @@ 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]] @@ -1247,6 +1281,8 @@ dependencies = [ name = "rust-xcrate-test" version = "0.0.0" dependencies = [ + "futures", + "once_cell", "wit-bindgen", ] @@ -1359,6 +1395,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "slice-group-by" version = "0.3.1" diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 4d9744b8c..626785ef1 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -1094,7 +1094,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { // } else { self.emit(&Instruction::Return { func, - amt: sig.results.len(), + amt: func.results.len(), }); // } } diff --git a/crates/cpp/tests/symmetric.rs b/crates/cpp/tests/symmetric.rs index 0b0998a70..e4f041157 100644 --- a/crates/cpp/tests/symmetric.rs +++ b/crates/cpp/tests/symmetric.rs @@ -50,6 +50,8 @@ fn create_cargo_files( [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\ @@ -80,6 +82,8 @@ fn create_cargo_files( [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() ); @@ -104,7 +108,7 @@ fn tests( 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 { + let Some(_tester_source_file) = tester_source_file(dir_name, tester_source_dir) else { println!("Skipping {}", dir_name); return Ok(()); }; @@ -219,9 +223,10 @@ fn tests( panic!("failed to compile"); } else { let mut tester = out_dir.clone(); + tester.pop(); tester.push("target"); tester.push("debug"); - tester.push("tester"); + tester.push(&format!("tester-{dir_name}")); let run = Command::new(tester) .env("LD_LIBRARY_PATH", cpp_dir) .output(); diff --git a/crates/test-rust-wasm/rust-xcrate-test/Cargo.toml b/crates/test-rust-wasm/rust-xcrate-test/Cargo.toml index 148687db5..d2183978f 100644 --- a/crates/test-rust-wasm/rust-xcrate-test/Cargo.toml +++ b/crates/test-rust-wasm/rust-xcrate-test/Cargo.toml @@ -3,5 +3,9 @@ name = "rust-xcrate-test" edition.workspace = true publish = false +[build-dependencies] + [dependencies] wit-bindgen = { path = '../../guest-rust' } +futures = "0.3.31" +once_cell = "1.20.2" From 79565c6454397859e1871fe518c1335fc7c767a2 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Nov 2024 22:33:51 +0100 Subject: [PATCH 386/672] fix test generation --- Cargo.lock | 2 ++ crates/test-rust-wasm/Cargo.toml | 2 ++ crates/test-rust-wasm/rust-xcrate-test/Cargo.toml | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 303164248..c6243f0fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1517,6 +1517,8 @@ dependencies = [ name = "test-rust-wasm" version = "0.0.0" dependencies = [ + "futures", + "once_cell", "rust-xcrate-test", "wit-bindgen", ] diff --git a/crates/test-rust-wasm/Cargo.toml b/crates/test-rust-wasm/Cargo.toml index 63db4d1cb..fff9fb8f0 100644 --- a/crates/test-rust-wasm/Cargo.toml +++ b/crates/test-rust-wasm/Cargo.toml @@ -7,6 +7,8 @@ publish = false [dependencies] wit-bindgen = { path = "../guest-rust" } 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 d2183978f..feea8df0b 100644 --- a/crates/test-rust-wasm/rust-xcrate-test/Cargo.toml +++ b/crates/test-rust-wasm/rust-xcrate-test/Cargo.toml @@ -7,5 +7,5 @@ publish = false [dependencies] wit-bindgen = { path = '../../guest-rust' } -futures = "0.3.31" -once_cell = "1.20.2" +futures = "0.3" +once_cell = "1.20" From 3cf333f3aeaf8fa309931518bae9565b2bf13d6c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 2 Nov 2024 23:04:19 +0100 Subject: [PATCH 387/672] fix strings test (both old and new API) --- tests/runtime/main.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/runtime/main.rs b/tests/runtime/main.rs index a939ec18a..6afeec4cf 100644 --- a/tests/runtime/main.rs +++ b/tests/runtime/main.rs @@ -272,7 +272,12 @@ fn tests(name: &str, dir_name: &str) -> Result> { 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-{}", 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(); From 8ebfc1ecc93540c43c5e9473320ba4fc220e2d64 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 22 Mar 2024 17:33:31 -0600 Subject: [PATCH 388/672] Add support for async/streams/futures to Rust generator This adds support for generating bindings which use the [Async ABI](https://github.com/WebAssembly/component-model/blob/main/design/mvp/Async.md) along with the [`stream`, `future`, and `error-context`](https://github.com/WebAssembly/component-model/pull/405) types. By default, normal synchronous bindings are generated, but the user may opt-in to async bindings for all or some of the imported and/or exported functions in the target world and interfaces -- provided the default-enabled `async` feature is enabled. In addition, we generate `StreamPayload` and/or `FuturePayload` trait implementations for any types appearing as the `T` in `stream` or `future` in the WIT files, respectively. That enables user code to call `new_stream` or `new_future` to create `stream`s or `future`s with those payload types, then write to them, read from them, and/or pass the readable end as a parameter to a component import or return value of a component export. Note that I've added new `core::abi::Instruction` enum variants to handle async lifting and lowering, but they're currently tailored to the Rust generator and will probably change somewhat as we add support for other languages. This does not include any new tests; I'll add those in a follow-up commit. Signed-off-by: Joel Dice --- Cargo.lock | 144 +++- Cargo.toml | 12 +- crates/c/src/lib.rs | 42 +- crates/core/Cargo.toml | 4 + crates/core/src/abi.rs | 424 +++++++++--- crates/core/src/lib.rs | 33 +- crates/core/src/types.rs | 8 +- crates/csharp/src/lib.rs | 21 +- crates/go/src/bindgen.rs | 2 + crates/go/src/interface.rs | 5 +- crates/guest-rust/Cargo.toml | 3 +- crates/guest-rust/macro/Cargo.toml | 3 + crates/guest-rust/macro/src/lib.rs | 87 ++- crates/guest-rust/rt/Cargo.toml | 6 + crates/guest-rust/rt/src/async_support.rs | 510 +++++++++++++++ crates/guest-rust/rt/src/lib.rs | 10 +- crates/markdown/src/lib.rs | 30 +- crates/moonbit/src/lib.rs | 19 +- crates/rust/Cargo.toml | 4 + crates/rust/src/bindgen.rs | 167 ++++- crates/rust/src/interface.rs | 649 +++++++++++++++++-- crates/rust/src/lib.rs | 110 +++- crates/rust/src/stream_and_future_support.rs | 513 +++++++++++++++ crates/teavm-java/src/lib.rs | 19 +- crates/test-helpers/src/lib.rs | 3 +- tests/runtime/flavorful/wasm.rs | 2 - tests/runtime/main.rs | 15 +- 27 files changed, 2556 insertions(+), 289 deletions(-) create mode 100644 crates/guest-rust/rt/src/async_support.rs create mode 100644 crates/rust/src/stream_and_future_support.rs diff --git a/Cargo.lock b/Cargo.lock index 6ce2eb66e..30cd3a2b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,6 +125,12 @@ dependencies = [ "syn", ] +[[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" @@ -677,6 +683,7 @@ checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -699,12 +706,34 @@ 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" @@ -723,11 +752,16 @@ 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]] @@ -1356,6 +1390,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "slice-group-by" version = "0.3.1" @@ -1459,10 +1502,10 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.219.0", + "wasm-encoder 0.219.1", "wit-bindgen-core", "wit-component", - "wit-parser 0.219.0", + "wit-parser 0.219.1", ] [[package]] @@ -1630,6 +1673,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -1745,11 +1794,19 @@ dependencies = [ "wasmparser 0.219.0", ] +[[package]] +name = "wasm-encoder" +version = "0.219.1" +source = "git+https://github.com/dicej/wasm-tools?branch=async#940de62bd278831d1134bde84caedc1adcce6a00" +dependencies = [ + "leb128", + "wasmparser 0.219.1", +] + [[package]] name = "wasm-metadata" -version = "0.219.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96132fe00dd17d092d2be289eeed5a0a68ad3cf30b68e8875bc953b96f55f0be" +version = "0.219.1" +source = "git+https://github.com/dicej/wasm-tools?branch=async#940de62bd278831d1134bde84caedc1adcce6a00" dependencies = [ "anyhow", "indexmap", @@ -1757,8 +1814,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.219.0", - "wasmparser 0.219.0", + "wasm-encoder 0.219.1", + "wasmparser 0.219.1", ] [[package]] @@ -1780,6 +1837,15 @@ name = "wasmparser" version = "0.219.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "324b4e56d24439495b88cd81439dad5e97f3c7b1eedc3c7e10455ed1e045e9a2" +dependencies = [ + "bitflags", + "indexmap", +] + +[[package]] +name = "wasmparser" +version = "0.219.1" +source = "git+https://github.com/dicej/wasm-tools?branch=async#940de62bd278831d1134bde84caedc1adcce6a00" dependencies = [ "ahash", "bitflags", @@ -1852,7 +1918,7 @@ dependencies = [ "wasmtime-slab", "wasmtime-versioned-export-macros", "wasmtime-winch", - "wat", + "wat 1.219.0", "windows-sys 0.52.0", ] @@ -2106,10 +2172,22 @@ dependencies = [ "bumpalo", "leb128", "memchr", - "unicode-width", + "unicode-width 0.1.14", "wasm-encoder 0.219.0", ] +[[package]] +name = "wast" +version = "219.0.1" +source = "git+https://github.com/dicej/wasm-tools?branch=async#940de62bd278831d1134bde84caedc1adcce6a00" +dependencies = [ + "bumpalo", + "leb128", + "memchr", + "unicode-width 0.2.0", + "wasm-encoder 0.219.1", +] + [[package]] name = "wat" version = "1.219.0" @@ -2119,6 +2197,14 @@ dependencies = [ "wast 219.0.0", ] +[[package]] +name = "wat" +version = "1.219.1" +source = "git+https://github.com/dicej/wasm-tools?branch=async#940de62bd278831d1134bde84caedc1adcce6a00" +dependencies = [ + "wast 219.0.1", +] + [[package]] name = "wiggle" version = "25.0.1" @@ -2335,11 +2421,11 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.219.0", + "wasm-encoder 0.219.1", "wasm-metadata", "wit-bindgen-core", "wit-component", - "wit-parser 0.219.0", + "wit-parser 0.219.1", ] [[package]] @@ -2350,8 +2436,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.219.0", - "wasmparser 0.219.0", + "wasm-encoder 0.219.1", + "wasmparser 0.219.1", "wasmtime", "wasmtime-wasi", "wit-bindgen-c", @@ -2363,7 +2449,7 @@ dependencies = [ "wit-bindgen-rust", "wit-bindgen-teavm-java", "wit-component", - "wit-parser 0.219.0", + "wit-parser 0.219.1", ] [[package]] @@ -2372,7 +2458,7 @@ version = "0.34.0" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser 0.219.0", + "wit-parser 0.219.1", ] [[package]] @@ -2384,12 +2470,12 @@ dependencies = [ "heck 0.5.0", "indexmap", "test-helpers", - "wasm-encoder 0.219.0", + "wasm-encoder 0.219.1", "wasm-metadata", - "wasmparser 0.219.0", + "wasmparser 0.219.1", "wit-bindgen-core", "wit-component", - "wit-parser 0.219.0", + "wit-parser 0.219.1", ] [[package]] @@ -2434,6 +2520,8 @@ name = "wit-bindgen-rt" version = "0.34.0" dependencies = [ "bitflags", + "futures", + "once_cell", ] [[package]] @@ -2483,9 +2571,8 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.219.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99a76111c20444a814019de20499d30940ecd219b9512ee296f034a5edb18a2d" +version = "0.219.1" +source = "git+https://github.com/dicej/wasm-tools?branch=async#940de62bd278831d1134bde84caedc1adcce6a00" dependencies = [ "anyhow", "bitflags", @@ -2494,11 +2581,11 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.219.0", + "wasm-encoder 0.219.1", "wasm-metadata", - "wasmparser 0.219.0", - "wat", - "wit-parser 0.219.0", + "wasmparser 0.219.1", + "wat 1.219.1", + "wit-parser 0.219.1", ] [[package]] @@ -2521,9 +2608,8 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.219.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23102e180c0c464f36e293d31a27b524e3ece930d7b5527d2f33f9d2c963de64" +version = "0.219.1" +source = "git+https://github.com/dicej/wasm-tools?branch=async#940de62bd278831d1134bde84caedc1adcce6a00" dependencies = [ "anyhow", "id-arena", @@ -2534,7 +2620,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.219.0", + "wasmparser 0.219.1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 75b172c66..90bd4080a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,11 +32,11 @@ indexmap = "2.0.0" prettyplease = "0.2.20" syn = { version = "2.0", features = ["printing"] } -wasmparser = "0.219.0" -wasm-encoder = "0.219.0" -wasm-metadata = "0.219.0" -wit-parser = "0.219.0" -wit-component = "0.219.0" +wasmparser = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wasm-encoder = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wasm-metadata = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wit-parser = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wit-component = { git = "https://github.com/dicej/wasm-tools", branch = "async" } wit-bindgen-core = { path = 'crates/core', version = '0.34.0' } wit-bindgen-c = { path = 'crates/c', version = '0.34.0' } @@ -74,6 +74,7 @@ default = [ 'go', 'csharp', 'moonbit', + 'async', ] c = ['dep:wit-bindgen-c'] rust = ['dep:wit-bindgen-rust'] @@ -83,6 +84,7 @@ go = ['dep:wit-bindgen-go'] csharp = ['dep:wit-bindgen-csharp'] csharp-mono = ['csharp'] moonbit = ['dep:wit-bindgen-moonbit'] +async = ["wit-bindgen-rust/async"] [dev-dependencies] heck = { workspace = true } diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 99eb5c689..26d5ee13f 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -718,6 +718,7 @@ fn is_prim_type_id(resolve: &Resolve, id: TypeId) -> bool { | TypeDefKind::Result(_) | TypeDefKind::Future(_) | TypeDefKind::Stream(_) + | TypeDefKind::ErrorContext | TypeDefKind::Unknown => false, } } @@ -779,8 +780,9 @@ pub fn push_ty_name(resolve: &Resolve, ty: &Type, src: &mut String) { src.push_str("list_"); push_ty_name(resolve, ty, src); } - TypeDefKind::Future(_) => unimplemented!(), - TypeDefKind::Stream(_) => unimplemented!(), + TypeDefKind::Future(_) => todo!(), + TypeDefKind::Stream(_) => todo!(), + TypeDefKind::ErrorContext => todo!(), TypeDefKind::Handle(Handle::Own(resource)) => { src.push_str("own_"); push_ty_name(resolve, &Type::Id(*resource), src); @@ -992,6 +994,7 @@ impl Return { TypeDefKind::Future(_) => todo!("return_single for future"), TypeDefKind::Stream(_) => todo!("return_single for stream"), + TypeDefKind::ErrorContext => todo!("return_single for error-context"), TypeDefKind::Resource => todo!("return_single for resource"), TypeDefKind::Unknown => unreachable!(), } @@ -1427,12 +1430,16 @@ impl<'a> wit_bindgen_core::AnonymousTypeGenerator<'a> for InterfaceGenerator<'a> todo!("print_anonymous_type for future"); } - fn anonymous_type_stream(&mut self, _id: TypeId, _ty: &Stream, _docs: &Docs) { + fn anonymous_type_stream(&mut self, _id: TypeId, _ty: &Type, _docs: &Docs) { todo!("print_anonymous_type for stream"); } - fn anonymous_typ_type(&mut self, _id: TypeId, _ty: &Type, _docs: &Docs) { - todo!("print_anonymous_type for typ"); + fn anonymous_type_error_context(&mut self) { + todo!("print_anonymous_type for error-context"); + } + + fn anonymous_type_type(&mut self, _id: TypeId, _ty: &Type, _docs: &Docs) { + todo!("print_anonymous_type for type"); } } @@ -1605,6 +1612,7 @@ impl InterfaceGenerator<'_> { } TypeDefKind::Future(_) => todo!("print_dtor for future"), TypeDefKind::Stream(_) => todo!("print_dtor for stream"), + TypeDefKind::ErrorContext => todo!("print_dtor for error-context"), TypeDefKind::Resource => {} TypeDefKind::Handle(Handle::Borrow(id) | Handle::Own(id)) => { self.free(&Type::Id(*id), "*ptr"); @@ -1750,6 +1758,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut f, + false, ); let FunctionBindgen { @@ -1822,6 +1831,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut f, + false, ); let FunctionBindgen { src, .. } = f; self.src.c_adapters(&src); @@ -1852,7 +1862,7 @@ impl InterfaceGenerator<'_> { let mut f = FunctionBindgen::new(self, c_sig, &import_name); f.params = params; - abi::post_return(f.gen.resolve, func, &mut f); + abi::post_return(f.gen.resolve, func, &mut f, false); let FunctionBindgen { src, .. } = f; self.src.c_fns(&src); self.src.c_fns("}\n"); @@ -2075,17 +2085,8 @@ impl InterfaceGenerator<'_> { TypeDefKind::List(ty) => self.contains_droppable_borrow(ty), - TypeDefKind::Future(r) => r - .as_ref() - .map_or(false, |ty| self.contains_droppable_borrow(ty)), - - TypeDefKind::Stream(s) => { - s.element - .as_ref() - .map_or(false, |ty| self.contains_droppable_borrow(ty)) - || s.end - .as_ref() - .map_or(false, |ty| self.contains_droppable_borrow(ty)) + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext => { + false } TypeDefKind::Type(ty) => self.contains_droppable_borrow(ty), @@ -2753,7 +2754,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src.push_str(");\n"); } - Instruction::CallInterface { func } => { + Instruction::CallInterface { func, .. } => { let mut args = String::new(); for (i, (op, (byref, _))) in operands.iter().zip(&self.sig.params).enumerate() { if i > 0 { @@ -3037,6 +3038,10 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!(self.src, "}}"); } + Instruction::Flush { amt } => { + results.extend(operands.iter().take(*amt).map(|v| v.clone())); + } + i => unimplemented!("{:?}", i), } } @@ -3145,6 +3150,7 @@ pub fn is_arg_by_pointer(resolve: &Resolve, ty: &Type) -> bool { TypeDefKind::Tuple(_) | TypeDefKind::Record(_) | TypeDefKind::List(_) => true, TypeDefKind::Future(_) => todo!("is_arg_by_pointer for future"), TypeDefKind::Stream(_) => todo!("is_arg_by_pointer for stream"), + TypeDefKind::ErrorContext => todo!("is_arg_by_pointer for error-context"), TypeDefKind::Resource => todo!("is_arg_by_pointer for resource"), TypeDefKind::Unknown => unreachable!(), }, diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 60935ef8d..bc476ea80 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -18,3 +18,7 @@ doctest = false wit-parser = { workspace = true } anyhow = { workspace = true } heck = { workspace = true } + +[features] +default = ["async"] +async = [] diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index f0175afaa..54f6a069e 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -1,7 +1,7 @@ pub use wit_parser::abi::{AbiVariant, WasmSignature, WasmType}; use wit_parser::{ - Enum, Flags, FlagsRepr, Function, Handle, Int, Record, Resolve, Result_, Results, SizeAlign, - Tuple, Type, TypeDefKind, TypeId, Variant, + ElementInfo, Enum, Flags, FlagsRepr, Function, Handle, Int, Record, Resolve, Result_, Results, + SizeAlign, Tuple, Type, TypeDefKind, TypeId, Variant, }; // Helper macro for defining instructions without having to have tons of @@ -350,6 +350,40 @@ def_instruction! { ty: TypeId, } : [1] => [1], + /// Create an `i32` from a future. + FutureLower { + payload: &'a Option, + ty: TypeId, + } : [1] => [1], + + /// Create a future from an `i32`. + FutureLift { + payload: &'a Option, + ty: TypeId, + } : [1] => [1], + + /// Create an `i32` from a stream. + StreamLower { + payload: &'a Type, + ty: TypeId, + } : [1] => [1], + + /// Create a stream from an `i32`. + StreamLift { + payload: &'a Type, + ty: TypeId, + } : [1] => [1], + + /// Create an `i32` from an error-context. + ErrorContextLower { + ty: TypeId, + } : [1] => [1], + + /// Create a error-context from an `i32`. + ErrorContextLift { + ty: TypeId, + } : [1] => [1], + /// Pops a tuple value off the stack, decomposes the tuple to all of /// its fields, and then pushes the fields onto the stack. TupleLower { @@ -470,7 +504,8 @@ def_instruction! { /// Note that this will be used for async functions. CallInterface { func: &'a Function, - } : [func.params.len()] => [func.results.len()], + async_: bool, + } : [func.params.len()] => [if *async_ { 1 } else { func.results.len() }], /// Returns `amt` values on the stack. This is always the last /// instruction. @@ -519,6 +554,39 @@ def_instruction! { GuestDeallocateVariant { blocks: usize, } : [1] => [0], + + /// Allocate the parameter and/or return areas to use for an + /// async-lowered import call. + /// + /// This cannot be allocated on the (shadow-)stack since it needs to + /// remain valid until the callee has finished using the buffers, which + /// may be after we pop the current stack frame. + AsyncMalloc { size: usize, align: usize } : [0] => [1], + + /// Call an async-lowered import. + /// + /// `size` and `align` are used to deallocate the parameter area + /// allocated using `AsyncMalloc` after the callee task returns a value. + AsyncCallWasm { name: &'a str, size: usize, align: usize } : [2] => [0], + + /// Generate code to run after `CallInterface` for an async-lifted export. + /// + /// For example, this might include task management for the + /// future/promise/task returned by the call made for `CallInterface`. + AsyncPostCallInterface { func: &'a Function } : [1] => [func.results.len() + 1], + + /// Call `task.return` for an async-lifted export once the task returned + /// by `CallInterface` and managed by `AsyncPostCallInterface` + /// yields a value. + AsyncCallReturn { name: &'a str, params: &'a [WasmType] } : [params.len()] => [0], + + /// Force the evaluation of the specified number of expressions and push + /// the results to the stack. + /// + /// This is useful prior to disposing of temporary variables and/or + /// allocations which are referenced by one or more not-yet-evaluated + /// expressions. + Flush { amt: usize } : [*amt] => [*amt], } } @@ -683,8 +751,50 @@ pub fn call( lift_lower: LiftLower, func: &Function, bindgen: &mut impl Bindgen, + async_: bool, +) { + if async_ && !cfg!(feature = "async") { + panic!("must enable `async` feature to lift or lower using the async ABI"); + } + + Generator::new(resolve, variant, lift_lower, bindgen, async_).call(func); +} + +pub fn lower_to_memory( + resolve: &Resolve, + bindgen: &mut B, + address: B::Operand, + value: B::Operand, + ty: &Type, ) { - Generator::new(resolve, variant, lift_lower, bindgen).call(func); + // TODO: refactor so we don't need to pass in a bunch of unused dummy parameters: + let mut generator = Generator::new( + resolve, + AbiVariant::GuestImport, + LiftLower::LowerArgsLiftResults, + bindgen, + true, + ); + generator.stack.push(value); + generator.write_to_memory(ty, address, 0); +} + +pub fn lift_from_memory( + resolve: &Resolve, + bindgen: &mut B, + address: B::Operand, + ty: &Type, +) -> B::Operand { + // TODO: refactor so we don't need to pass in a bunch of unused dummy parameters: + let mut generator = Generator::new( + resolve, + AbiVariant::GuestImport, + LiftLower::LowerArgsLiftResults, + bindgen, + true, + ); + generator.read_from_memory(ty, address, 0); + generator.stack.pop().unwrap() } /// Used in a similar manner as the `Interface::call` function except is @@ -693,12 +803,13 @@ pub fn call( /// This is only intended to be used in guest generators for exported /// functions and will primarily generate `GuestDeallocate*` instructions, /// plus others used as input to those instructions. -pub fn post_return(resolve: &Resolve, func: &Function, bindgen: &mut impl Bindgen) { +pub fn post_return(resolve: &Resolve, func: &Function, bindgen: &mut impl Bindgen, async_: bool) { Generator::new( resolve, AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, bindgen, + async_, ) .post_return(func); } @@ -734,7 +845,9 @@ fn needs_post_return(resolve: &Resolve, ty: &Type) -> bool { .filter_map(|t| t.as_ref()) .any(|t| needs_post_return(resolve, t)), TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => false, - TypeDefKind::Future(_) | TypeDefKind::Stream(_) => unimplemented!(), + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext => { + unimplemented!() + } TypeDefKind::Unknown => unreachable!(), }, @@ -757,6 +870,7 @@ struct Generator<'a, B: Bindgen> { variant: AbiVariant, lift_lower: LiftLower, bindgen: &'a mut B, + async_: bool, resolve: &'a Resolve, operands: Vec, results: Vec, @@ -770,12 +884,14 @@ impl<'a, B: Bindgen> Generator<'a, B> { variant: AbiVariant, lift_lower: LiftLower, bindgen: &'a mut B, + async_: bool, ) -> Generator<'a, B> { Generator { resolve, variant, lift_lower, bindgen, + async_, operands: Vec::new(), results: Vec::new(), stack: Vec::new(), @@ -784,74 +900,122 @@ 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); match self.lift_lower { LiftLower::LowerArgsLiftResults => { - if !sig.indirect_params { - // If the parameters for this function aren't indirect - // (there aren't too many) then we simply do a normal lower - // operation for them all. + if let (AbiVariant::GuestExport, true) = (self.variant, self.async_) { + unimplemented!("host-side code generation for async lift/lower not supported"); + } + + let lower_to_memory = |self_: &mut Self, ptr: B::Operand| { + let mut offset = 0usize; for (nth, (_, ty)) in func.params.iter().enumerate() { - self.emit(&Instruction::GetArg { nth }); - self.lower(ty); + self_.emit(&Instruction::GetArg { nth }); + offset = align_to(offset, self_.bindgen.sizes().align(ty).align_wasm32()); + self_.write_to_memory(ty, ptr.clone(), offset as i32); + offset += self_.bindgen.sizes().size(ty).size_wasm32(); } - } else { - // ... otherwise if parameters are indirect space is - // allocated from them and each argument is lowered - // individually into memory. - let info = self + + self_.stack.push(ptr); + }; + + let params_size_align = if self.async_ { + let ElementInfo { size, align } = self .bindgen .sizes() - .record(func.params.iter().map(|t| &t.1)); - let ptr = match self.variant { - // When a wasm module calls an import it will provide - // space that isn't explicitly deallocated. - AbiVariant::GuestImport => self - .bindgen - .return_pointer(info.size.size_wasm32(), info.align.align_wasm32()), - // When calling a wasm module from the outside, though, - // malloc needs to be called. - AbiVariant::GuestExport => { - self.emit(&Instruction::Malloc { - realloc: "cabi_realloc", - size: info.size.size_wasm32(), - align: info.align.align_wasm32(), - }); - self.stack.pop().unwrap() + .record(func.params.iter().map(|(_, ty)| ty)); + self.emit(&Instruction::AsyncMalloc { + size: size.size_wasm32(), + align: align.align_wasm32(), + }); + let ptr = self.stack.pop().unwrap(); + lower_to_memory(self, ptr); + Some((size, align)) + } else { + if !sig.indirect_params { + // If the parameters for this function aren't indirect + // (there aren't too many) then we simply do a normal lower + // operation for them all. + for (nth, (_, ty)) in func.params.iter().enumerate() { + self.emit(&Instruction::GetArg { nth }); + self.lower(ty); } - }; - let mut offset = 0usize; - for (nth, (_, ty)) in func.params.iter().enumerate() { - self.emit(&Instruction::GetArg { nth }); - offset = align_to(offset, self.bindgen.sizes().align(ty).align_wasm32()); - self.write_to_memory(ty, ptr.clone(), offset as i32); - offset += self.bindgen.sizes().size(ty).size_wasm32(); + } else { + // ... otherwise if parameters are indirect space is + // allocated from them and each argument is lowered + // individually into memory. + let info = self + .bindgen + .sizes() + .record(func.params.iter().map(|t| &t.1)); + let ptr = match self.variant { + // When a wasm module calls an import it will provide + // space that isn't explicitly deallocated. + AbiVariant::GuestImport => self + .bindgen + .return_pointer(info.size.size_wasm32(), info.align.align_wasm32()), + // When calling a wasm module from the outside, though, + // malloc needs to be called. + AbiVariant::GuestExport => { + self.emit(&Instruction::Malloc { + realloc: "cabi_realloc", + size: info.size.size_wasm32(), + align: info.align.align_wasm32(), + }); + self.stack.pop().unwrap() + } + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } + }; + lower_to_memory(self, ptr); } - - self.stack.push(ptr); - } + None + }; // If necessary we may need to prepare a return pointer for // this ABI. - if self.variant == AbiVariant::GuestImport && sig.retptr { - let info = self.bindgen.sizes().params(func.results.iter_types()); - let ptr = self - .bindgen - .return_pointer(info.size.size_wasm32(), info.align.align_wasm32()); - self.return_pointer = Some(ptr.clone()); - self.stack.push(ptr); - } + let dealloc_size_align = + if let Some((params_size, params_align)) = params_size_align { + let ElementInfo { size, align } = + self.bindgen.sizes().record(func.results.iter_types()); + self.emit(&Instruction::AsyncMalloc { + size: size.size_wasm32(), + align: align.align_wasm32(), + }); + let ptr = self.stack.pop().unwrap(); + self.return_pointer = Some(ptr.clone()); + self.stack.push(ptr); + + assert_eq!(self.stack.len(), 2); + self.emit(&Instruction::AsyncCallWasm { + name: &format!("[async]{}", func.name), + size: params_size.size_wasm32(), + align: params_align.align_wasm32(), + }); + Some((size, align)) + } else { + if self.variant == AbiVariant::GuestImport && sig.retptr { + let info = self.bindgen.sizes().params(func.results.iter_types()); + let ptr = self + .bindgen + .return_pointer(info.size.size_wasm32(), info.align.align_wasm32()); + self.return_pointer = Some(ptr.clone()); + self.stack.push(ptr); + } - // Now that all the wasm args are prepared we can call the - // actual wasm function. - assert_eq!(self.stack.len(), sig.params.len()); - self.emit(&Instruction::CallWasm { - name: &func.name, - sig: &sig, - }); + assert_eq!(self.stack.len(), sig.params.len()); + self.emit(&Instruction::CallWasm { + name: &func.name, + sig: &sig, + }); + None + }; - if !sig.retptr { + 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. @@ -862,7 +1026,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { let ptr = match self.variant { // imports into guests means it's a wasm module // calling an imported function. We supplied the - // return poitner as the last argument (saved in + // return pointer as the last argument (saved in // `self.return_pointer`) so we use that to read // the result of the function from memory. AbiVariant::GuestImport => { @@ -874,9 +1038,24 @@ impl<'a, B: Bindgen> Generator<'a, B> { // calling wasm so wasm returned a pointer to where // the result is stored AbiVariant::GuestExport => self.stack.pop().unwrap(), + + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } }; - self.read_results_from_memory(&func.results, ptr, 0); + self.read_results_from_memory(&func.results, ptr.clone(), 0); + self.emit(&Instruction::Flush { + amt: func.results.len(), + }); + + if let Some((size, align)) = dealloc_size_align { + self.stack.push(ptr); + self.emit(&Instruction::GuestDeallocate { + size: size.size_wasm32(), + align: align.align_wasm32(), + }); + } } self.emit(&Instruction::Return { @@ -885,6 +1064,20 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); } LiftLower::LiftArgsLowerResults => { + if let (AbiVariant::GuestImport, true) = (self.variant, self.async_) { + todo!("implement host-side support for async lift/lower"); + } + + let read_from_memory = |self_: &mut Self| { + let mut offset = 0usize; + let ptr = self_.stack.pop().unwrap(); + for (_, ty) in func.params.iter() { + offset = align_to(offset, self_.bindgen.sizes().align(ty).align_wasm32()); + self_.read_from_memory(ty, ptr.clone(), offset as i32); + offset += self_.bindgen.sizes().size(ty).size_wasm32(); + } + }; + if !sig.indirect_params { // If parameters are not passed indirectly then we lift each // argument in succession from the component wasm types that @@ -904,23 +1097,33 @@ impl<'a, B: Bindgen> Generator<'a, B> { // ... otherwise argument is read in succession from memory // where the pointer to the arguments is the first argument // to the function. - let mut offset = 0usize; self.emit(&Instruction::GetArg { nth: 0 }); - let ptr = self.stack.pop().unwrap(); - for (_, ty) in func.params.iter() { - offset = align_to(offset, self.bindgen.sizes().align(ty).align_wasm32()); - self.read_from_memory(ty, ptr.clone(), offset as i32); - offset += self.bindgen.sizes().size(ty).size_wasm32(); - } + read_from_memory(self); } // ... and that allows us to call the interface types function - self.emit(&Instruction::CallInterface { func }); + self.emit(&Instruction::CallInterface { + func, + async_: self.async_, + }); - // This was dynamically allocated by the caller so after - // it's been read by the guest we need to deallocate it. + let (lower_to_memory, async_results) = if self.async_ { + self.emit(&Instruction::AsyncPostCallInterface { func }); + + let mut results = Vec::new(); + for ty in func.results.iter_types() { + self.resolve.push_flat(ty, &mut results); + } + (results.len() > MAX_FLAT_PARAMS, Some(results)) + } else { + (sig.retptr, None) + }; + + // This was dynamically allocated by the caller (or async start + // function) so after it's been read by the guest we need to + // deallocate it. if let AbiVariant::GuestExport = self.variant { - if sig.indirect_params { + if sig.indirect_params && !self.async_ { let info = self .bindgen .sizes() @@ -933,7 +1136,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - if !sig.retptr { + if !lower_to_memory { // With no return pointer in use we simply lower the // result(s) and return that directly from the function. let results = self @@ -973,13 +1176,31 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.write_params_to_memory(func.results.iter_types(), ptr.clone(), 0); self.stack.push(ptr); } + + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } } } - self.emit(&Instruction::Return { - func, - amt: sig.results.len(), - }); + if let Some(results) = async_results { + let name = &format!("[task-return]{}", func.name); + + self.emit(&Instruction::AsyncCallReturn { + name, + params: &if results.len() > MAX_FLAT_PARAMS { + vec![WasmType::Pointer] + } else { + results + }, + }); + self.emit(&Instruction::Return { func, amt: 1 }); + } else { + self.emit(&Instruction::Return { + func, + amt: sig.results.len(), + }); + } } } @@ -1177,8 +1398,21 @@ impl<'a, B: Bindgen> Generator<'a, B> { results: &results, }); } - TypeDefKind::Future(_) => todo!("lower future"), - TypeDefKind::Stream(_) => todo!("lower stream"), + TypeDefKind::Future(ty) => { + self.emit(&FutureLower { + payload: ty, + ty: id, + }); + } + TypeDefKind::Stream(ty) => { + self.emit(&StreamLower { + payload: ty, + ty: id, + }); + } + TypeDefKind::ErrorContext => { + self.emit(&ErrorContextLower { ty: id }); + } TypeDefKind::Unknown => unreachable!(), }, } @@ -1362,8 +1596,21 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&ResultLift { result: r, ty: id }); } - TypeDefKind::Future(_) => todo!("lift future"), - TypeDefKind::Stream(_) => todo!("lift stream"), + TypeDefKind::Future(ty) => { + self.emit(&FutureLift { + payload: ty, + ty: id, + }); + } + TypeDefKind::Stream(ty) => { + self.emit(&StreamLift { + payload: ty, + ty: id, + }); + } + TypeDefKind::ErrorContext => { + self.emit(&ErrorContextLift { ty: id }); + } TypeDefKind::Unknown => unreachable!(), }, } @@ -1431,7 +1678,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::Type(t) => self.write_to_memory(t, addr, offset), TypeDefKind::List(_) => self.write_list_to_memory(ty, addr, offset), - TypeDefKind::Handle(_) => self.lower_and_emit(ty, addr, &I32Store { offset }), + TypeDefKind::Future(_) + | TypeDefKind::Stream(_) + | TypeDefKind::ErrorContext + | TypeDefKind::Handle(_) => self.lower_and_emit(ty, addr, &I32Store { offset }), // Decompose the record into its components and then write all // the components into memory one-by-one. @@ -1521,8 +1771,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.store_intrepr(offset, e.tag()); } - TypeDefKind::Future(_) => todo!("write future to memory"), - TypeDefKind::Stream(_) => todo!("write stream to memory"), TypeDefKind::Unknown => unreachable!(), }, } @@ -1625,7 +1873,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::List(_) => self.read_list_from_memory(ty, addr, offset), - TypeDefKind::Handle(_) => self.emit_and_lift(ty, addr, &I32Load { offset }), + TypeDefKind::Future(_) + | TypeDefKind::Stream(_) + | TypeDefKind::ErrorContext + | TypeDefKind::Handle(_) => self.emit_and_lift(ty, addr, &I32Load { offset }), TypeDefKind::Resource => { todo!(); @@ -1709,8 +1960,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.lift(ty); } - TypeDefKind::Future(_) => todo!("read future from memory"), - TypeDefKind::Stream(_) => todo!("read stream from memory"), TypeDefKind::Unknown => unreachable!(), }, } @@ -1887,6 +2136,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::Future(_) => todo!("read future from memory"), TypeDefKind::Stream(_) => todo!("read stream from memory"), + TypeDefKind::ErrorContext => todo!("read error-context from memory"), TypeDefKind::Unknown => unreachable!(), }, } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index fae3f3b90..b0cc21a47 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -22,6 +22,22 @@ pub enum Direction { pub trait WorldGenerator { fn generate(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) -> Result<()> { + // TODO: Should we refine this test to inspect only types reachable from + // the specified world? + if !cfg!(feature = "async") + && resolve.types.iter().any(|(_, ty)| { + matches!( + ty.kind, + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext + ) + }) + { + anyhow::bail!( + "must enable `async` feature when using WIT files \ + containing future, stream, or error types" + ); + } + let world = &resolve.worlds[id]; self.preprocess(resolve, id); @@ -174,9 +190,12 @@ pub trait InterfaceGenerator<'a> { 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::Future(_) => panic!("future types do not require definition"), + TypeDefKind::Stream(_) => panic!("stream types do not require definition"), + TypeDefKind::Handle(_) => panic!("handle types do not require definition"), + TypeDefKind::ErrorContext => { + panic!("the error-context type does not require definition") + } TypeDefKind::Unknown => unreachable!(), } } @@ -191,8 +210,9 @@ pub trait AnonymousTypeGenerator<'a> { fn anonymous_type_result(&mut self, id: TypeId, ty: &Result_, docs: &Docs); fn anonymous_type_list(&mut self, id: TypeId, ty: &Type, docs: &Docs); fn anonymous_type_future(&mut self, id: TypeId, ty: &Option, docs: &Docs); - fn anonymous_type_stream(&mut self, id: TypeId, ty: &Stream, docs: &Docs); - fn anonymous_typ_type(&mut self, id: TypeId, ty: &Type, docs: &Docs); + fn anonymous_type_stream(&mut self, id: TypeId, ty: &Type, docs: &Docs); + fn anonymous_type_type(&mut self, id: TypeId, ty: &Type, docs: &Docs); + fn anonymous_type_error_context(&mut self); fn define_anonymous_type(&mut self, id: TypeId) { let ty = &self.resolve().types[id]; @@ -204,13 +224,14 @@ pub trait AnonymousTypeGenerator<'a> { | TypeDefKind::Variant(_) => { unreachable!() } - TypeDefKind::Type(t) => self.anonymous_typ_type(id, t, &ty.docs), + TypeDefKind::Type(t) => self.anonymous_type_type(id, t, &ty.docs), TypeDefKind::Tuple(tuple) => self.anonymous_type_tuple(id, tuple, &ty.docs), TypeDefKind::Option(t) => self.anonymous_type_option(id, t, &ty.docs), TypeDefKind::Result(r) => self.anonymous_type_result(id, r, &ty.docs), TypeDefKind::List(t) => self.anonymous_type_list(id, t, &ty.docs), TypeDefKind::Future(f) => self.anonymous_type_future(id, f, &ty.docs), TypeDefKind::Stream(s) => self.anonymous_type_stream(id, s, &ty.docs), + TypeDefKind::ErrorContext => self.anonymous_type_error_context(), TypeDefKind::Handle(handle) => self.anonymous_type_handle(id, handle, &ty.docs), TypeDefKind::Unknown => unreachable!(), } diff --git a/crates/core/src/types.rs b/crates/core/src/types.rs index a0862f5e8..45030c0ea 100644 --- a/crates/core/src/types.rs +++ b/crates/core/src/types.rs @@ -193,13 +193,7 @@ impl Types { info = self.optional_type_info(resolve, r.ok.as_ref()); info |= self.optional_type_info(resolve, r.err.as_ref()); } - TypeDefKind::Future(ty) => { - info = self.optional_type_info(resolve, ty.as_ref()); - } - TypeDefKind::Stream(stream) => { - info = self.optional_type_info(resolve, stream.element.as_ref()); - info |= self.optional_type_info(resolve, stream.end.as_ref()); - } + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext => {} TypeDefKind::Unknown => unreachable!(), } let prev = self.type_info.insert(ty, info); diff --git a/crates/csharp/src/lib.rs b/crates/csharp/src/lib.rs index 6f11ef47b..ff415d33b 100644 --- a/crates/csharp/src/lib.rs +++ b/crates/csharp/src/lib.rs @@ -1059,6 +1059,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut bindgen, + false, ); let src = bindgen.src; @@ -1178,6 +1179,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut bindgen, + false, ); assert!(!bindgen.needs_cleanup_list); @@ -2589,7 +2591,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { operands[0], operands[1] )), - Instruction::ListLower { element, realloc } => { + Instruction::ListLower { element, realloc: _ } => { let Block { body, results: block_results, @@ -2694,7 +2696,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { ); } - Instruction::CallInterface { func } => { + Instruction::CallInterface { func, .. } => { let module = self.gen.name; let func_name = self.func_name.to_upper_camel_case(); let interface_name = CSharp::get_class_name_from_qualified_name(module).1; @@ -3005,6 +3007,21 @@ impl Bindgen for FunctionBindgen<'_, '_> { } results.push(resource); } + + Instruction::Flush { amt } => { + results.extend(operands.iter().take(*amt).map(|v| v.clone())); + } + + Instruction::AsyncMalloc { .. } + | Instruction::AsyncPostCallInterface { .. } + | Instruction::AsyncCallReturn { .. } + | Instruction::FutureLower { .. } + | Instruction::FutureLift { .. } + | Instruction::StreamLower { .. } + | Instruction::StreamLift { .. } + | Instruction::ErrorContextLower { .. } + | Instruction::ErrorContextLift { .. } + | Instruction::AsyncCallWasm { .. } => todo!(), } } diff --git a/crates/go/src/bindgen.rs b/crates/go/src/bindgen.rs index f045bbd00..853f97673 100644 --- a/crates/go/src/bindgen.rs +++ b/crates/go/src/bindgen.rs @@ -319,6 +319,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } TypeDefKind::Future(_) => todo!("impl future"), TypeDefKind::Stream(_) => todo!("impl stream"), + TypeDefKind::ErrorContext => todo!("impl error-context"), TypeDefKind::Resource => todo!("impl resource"), TypeDefKind::Handle(h) => { match self.interface.direction { @@ -609,6 +610,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } TypeDefKind::Future(_) => todo!("impl future"), TypeDefKind::Stream(_) => todo!("impl stream"), + TypeDefKind::ErrorContext => todo!("impl error-context"), TypeDefKind::Resource => todo!("impl resource"), TypeDefKind::Handle(h) => { match self.interface.direction { diff --git a/crates/go/src/interface.rs b/crates/go/src/interface.rs index ba7e150cb..e47438a02 100644 --- a/crates/go/src/interface.rs +++ b/crates/go/src/interface.rs @@ -322,11 +322,11 @@ impl InterfaceGenerator<'_> { TypeDefKind::Stream(t) => { let mut src = String::new(); src.push_str("Stream"); - src.push_str(&self.optional_ty_name(t.element.as_ref())); - src.push_str(&self.optional_ty_name(t.end.as_ref())); + src.push_str(&self.ty_name(t)); src.push('T'); src } + TypeDefKind::ErrorContext => "ErrorContext".to_owned(), TypeDefKind::Handle(Handle::Own(ty)) => { // Currently there is no different between Own and Borrow // in the Go code. They are just represented as @@ -678,6 +678,7 @@ impl InterfaceGenerator<'_> { } TypeDefKind::Future(_) => todo!("anonymous_type for future"), TypeDefKind::Stream(_) => todo!("anonymous_type for stream"), + TypeDefKind::ErrorContext => todo!("anonymous_type for error-context"), TypeDefKind::Unknown => unreachable!(), } } diff --git a/crates/guest-rust/Cargo.toml b/crates/guest-rust/Cargo.toml index 1918921db..71305e02a 100644 --- a/crates/guest-rust/Cargo.toml +++ b/crates/guest-rust/Cargo.toml @@ -16,6 +16,7 @@ wit-bindgen-rust-macro = { path = "./macro", optional = true, version = "0.34.0" wit-bindgen-rt = { path = "./rt", version = "0.34.0", features = ["bitflags"] } [features] -default = ["macros", "realloc"] +default = ["macros", "realloc", "async"] macros = ["dep:wit-bindgen-rust-macro"] realloc = [] +async = ["macros", "wit-bindgen-rt/async", "wit-bindgen-rust-macro/async"] diff --git a/crates/guest-rust/macro/Cargo.toml b/crates/guest-rust/macro/Cargo.toml index 984a1bffc..f30dd8780 100644 --- a/crates/guest-rust/macro/Cargo.toml +++ b/crates/guest-rust/macro/Cargo.toml @@ -24,3 +24,6 @@ anyhow = { workspace = true } syn = { workspace = true } prettyplease = { workspace = true } +[features] +default = ["async"] +async = ["wit-bindgen-rust/async"] diff --git a/crates/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs index c6cb439be..264bb766a 100644 --- a/crates/guest-rust/macro/src/lib.rs +++ b/crates/guest-rust/macro/src/lib.rs @@ -8,7 +8,7 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{braced, token, LitStr, Token}; use wit_bindgen_core::wit_parser::{PackageId, Resolve, UnresolvedPackageGroup, WorldId}; -use wit_bindgen_rust::{Opts, Ownership, WithOption}; +use wit_bindgen_rust::{AsyncConfig, Opts, Ownership, WithOption}; #[proc_macro] pub fn generate(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -46,6 +46,7 @@ struct Config { resolve: Resolve, world: WorldId, files: Vec, + debug: bool, } /// The source of the wit package definition @@ -63,6 +64,8 @@ impl Parse for Config { let mut world = None; let mut source = None; let mut features = Vec::new(); + let mut async_configured = false; + let mut debug = false; if input.peek(token::Brace) { let content; @@ -140,6 +143,22 @@ impl Parse for Config { Opt::DisableCustomSectionLinkHelpers(disable) => { opts.disable_custom_section_link_helpers = disable.value(); } + Opt::Debug(enable) => { + debug = enable.value(); + } + Opt::Async(val, span) => { + if async_configured { + return Err(Error::new(span, "cannot specify second async config")); + } + async_configured = true; + if !matches!(val, AsyncConfig::None) && !cfg!(feature = "async") { + return Err(Error::new( + span, + "must enable `async` feature to enable async imports and/or exports", + )); + } + opts.async_ = val; + } } } } else { @@ -159,6 +178,7 @@ impl Parse for Config { resolve, world, files, + debug, }) } } @@ -222,7 +242,7 @@ fn parse_source( }; let (pkg, sources) = resolve.push_path(normalized_path)?; pkgs.push(pkg); - files.extend(sources); + files.extend(sources.package_paths(pkg).unwrap().map(|v| v.to_owned())); } Ok(()) }; @@ -254,7 +274,7 @@ impl Config { // place a formatted version of the expanded code into a file. This file // will then show up in rustc error messages for any codegen issues and can // be inspected manually. - if std::env::var("WIT_BINDGEN_DEBUG").is_ok() { + if std::env::var("WIT_BINDGEN_DEBUG").is_ok() || self.debug { static INVOCATION: AtomicUsize = AtomicUsize::new(0); let root = Path::new(env!("DEBUG_OUTPUT_DIR")); let world_name = &self.resolve.worlds[self.world].name; @@ -313,6 +333,8 @@ mod kw { syn::custom_keyword!(generate_unused_types); syn::custom_keyword!(features); syn::custom_keyword!(disable_custom_section_link_helpers); + syn::custom_keyword!(imports); + syn::custom_keyword!(debug); } #[derive(Clone)] @@ -342,6 +364,11 @@ impl From for wit_bindgen_rust::ExportKey { } } +enum AsyncConfigSomeKind { + Imports, + Exports, +} + enum Opt { World(syn::LitStr), Path(Span, Vec), @@ -366,6 +393,8 @@ enum Opt { GenerateUnusedTypes(syn::LitBool), Features(Vec), DisableCustomSectionLinkHelpers(syn::LitBool), + Async(AsyncConfig, Span), + Debug(syn::LitBool), } impl Parse for Opt { @@ -513,6 +542,34 @@ impl Parse for Opt { input.parse::()?; input.parse::()?; Ok(Opt::DisableCustomSectionLinkHelpers(input.parse()?)) + } else if l.peek(kw::debug) { + input.parse::()?; + input.parse::()?; + Ok(Opt::Debug(input.parse()?)) + } else if l.peek(Token![async]) { + let span = input.parse::()?.span; + input.parse::()?; + if input.peek(syn::LitBool) { + if input.parse::()?.value { + Ok(Opt::Async(AsyncConfig::All, span)) + } else { + Ok(Opt::Async(AsyncConfig::None, span)) + } + } else { + let mut imports = Vec::new(); + let mut exports = Vec::new(); + let contents; + syn::braced!(contents in input); + for (kind, values) in + contents.parse_terminated(parse_async_some_field, Token![,])? + { + match kind { + AsyncConfigSomeKind::Imports => imports = values, + AsyncConfigSomeKind::Exports => exports = values, + } + } + Ok(Opt::Async(AsyncConfig::Some { imports, exports }, span)) + } } else { Err(l.error()) } @@ -571,3 +628,27 @@ fn fmt(input: &str) -> Result { let syntax_tree = syn::parse_file(&input)?; Ok(prettyplease::unparse(&syntax_tree)) } + +fn parse_async_some_field(input: ParseStream<'_>) -> Result<(AsyncConfigSomeKind, Vec)> { + let lookahead = input.lookahead1(); + let kind = if lookahead.peek(kw::imports) { + input.parse::()?; + input.parse::()?; + AsyncConfigSomeKind::Imports + } else if lookahead.peek(kw::exports) { + input.parse::()?; + input.parse::()?; + AsyncConfigSomeKind::Exports + } else { + return Err(lookahead.error()); + }; + + let list; + syn::bracketed!(list in input); + let fields = list.parse_terminated(Parse::parse, Token![,])?; + + Ok(( + kind, + fields.iter().map(|s: &syn::LitStr| s.value()).collect(), + )) +} diff --git a/crates/guest-rust/rt/Cargo.toml b/crates/guest-rust/rt/Cargo.toml index d038f3fcc..e84f04d18 100644 --- a/crates/guest-rust/rt/Cargo.toml +++ b/crates/guest-rust/rt/Cargo.toml @@ -12,3 +12,9 @@ Runtime support for the `wit-bindgen` crate [dependencies] # Optionally re-export the version of bitflags used by wit-bindgen. bitflags = { workspace = true, optional = true } +futures = { version = "0.3.30", optional = true } +once_cell = { version = "1.19.0", optional = true } + +[features] +default = ["async"] +async = ["dep:futures", "dep:once_cell"] diff --git a/crates/guest-rust/rt/src/async_support.rs b/crates/guest-rust/rt/src/async_support.rs new file mode 100644 index 000000000..fba6ce2d6 --- /dev/null +++ b/crates/guest-rust/rt/src/async_support.rs @@ -0,0 +1,510 @@ +#![deny(missing_docs)] + +use { + futures::{ + channel::oneshot, + future::FutureExt, + stream::{FuturesUnordered, StreamExt}, + }, + once_cell::sync::Lazy, + std::{ + alloc::{self, Layout}, + any::Any, + collections::hash_map, + collections::HashMap, + fmt::{self, Debug, Display}, + future::Future, + pin::Pin, + ptr, + sync::Arc, + task::{Context, Poll, Wake, Waker}, + }, +}; + +type BoxFuture = Pin + 'static>>; + +/// Represents a task created by either a call to an async-lifted export or a +/// future run using `block_on` or `poll_future`. +struct FutureState { + /// Number of in-progress async-lowered import calls and/or stream/future reads/writes. + todo: usize, + /// Remaining work to do (if any) before this task can be considered "done". + /// + /// Note that we won't tell the host the task is done until this is drained + /// and `todo` is zero. + tasks: Option>, +} + +/// Represents the state of a stream or future. +#[doc(hidden)] +pub enum Handle { + LocalOpen, + LocalReady(Box, Waker), + LocalWaiting(oneshot::Sender>), + LocalClosed, + Read, + Write, +} + +/// The current task being polled (or null if none). +static mut CURRENT: *mut FutureState = ptr::null_mut(); + +/// Map of any in-progress calls to async-lowered imports, keyed by the +/// identifiers issued by the host. +static mut CALLS: Lazy>> = Lazy::new(HashMap::new); + +/// Any newly-deferred work queued by calls to the `spawn` function while +/// polling the current task. +static mut SPAWNED: Vec = Vec::new(); + +/// The states of all currently-open streams and futures. +static mut HANDLES: Lazy> = Lazy::new(HashMap::new); + +#[doc(hidden)] +pub fn with_entry(handle: u32, fun: impl FnOnce(hash_map::Entry<'_, u32, Handle>) -> T) -> T { + fun(unsafe { HANDLES.entry(handle) }) +} + +fn dummy_waker() -> Waker { + struct DummyWaker; + + impl Wake for DummyWaker { + fn wake(self: Arc) {} + } + + static WAKER: Lazy> = Lazy::new(|| Arc::new(DummyWaker)); + + WAKER.clone().into() +} + +/// Poll the specified task until it either completes or can't make immediate +/// progress. +unsafe fn poll(state: *mut FutureState) -> Poll<()> { + loop { + if let Some(futures) = (*state).tasks.as_mut() { + CURRENT = state; + let poll = futures.poll_next_unpin(&mut Context::from_waker(&dummy_waker())); + CURRENT = ptr::null_mut(); + + if SPAWNED.is_empty() { + match poll { + Poll::Ready(Some(())) => (), + Poll::Ready(None) => { + (*state).tasks = None; + break Poll::Ready(()); + } + Poll::Pending => break Poll::Pending, + } + } else { + futures.extend(SPAWNED.drain(..)); + } + } else { + break Poll::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( + future: impl Future + 'static, + fun: impl FnOnce(T) + 'static, +) -> *mut u8 { + let state = Box::into_raw(Box::new(FutureState { + todo: 0, + tasks: Some( + [Box::pin(future.map(fun)) as BoxFuture] + .into_iter() + .collect(), + ), + })); + match unsafe { poll(state) } { + Poll::Ready(()) => ptr::null_mut(), + Poll::Pending => state as _, + } +} + +/// Await the completion of a call to an async-lowered import. +#[doc(hidden)] +pub async unsafe fn await_result( + import: unsafe extern "C" fn(*mut u8, *mut u8) -> i32, + params_layout: Layout, + params: *mut u8, + results: *mut u8, +) { + const STATUS_STARTING: u32 = 0; + const STATUS_STARTED: u32 = 1; + const STATUS_RETURNED: u32 = 2; + const STATUS_DONE: u32 = 3; + + let result = import(params, results) as u32; + let status = result >> 30; + let call = (result & !(0b11 << 30)) as i32; + + if status != STATUS_DONE { + assert!(!CURRENT.is_null()); + (*CURRENT).todo += 1; + } + + match status { + STATUS_STARTING => { + let (tx, rx) = oneshot::channel(); + CALLS.insert(call, tx); + rx.await.unwrap(); + alloc::dealloc(params, params_layout); + } + STATUS_STARTED => { + alloc::dealloc(params, params_layout); + let (tx, rx) = oneshot::channel(); + CALLS.insert(call, tx); + rx.await.unwrap(); + } + STATUS_RETURNED | STATUS_DONE => { + alloc::dealloc(params, params_layout); + } + _ => unreachable!(), + } +} + +/// stream/future read/write results defined by the Component Model ABI. +mod results { + pub const BLOCKED: u32 = 0xffff_ffff; + pub const CLOSED: u32 = 0x8000_0000; + pub const CANCELED: u32 = 0; +} + +/// Await the completion of a future read or write. +#[doc(hidden)] +pub async unsafe fn await_future_result( + import: unsafe extern "C" fn(u32, *mut u8) -> u32, + future: u32, + address: *mut u8, +) -> bool { + let result = import(future, address); + match result { + results::BLOCKED => { + assert!(!CURRENT.is_null()); + (*CURRENT).todo += 1; + let (tx, rx) = oneshot::channel(); + CALLS.insert(future as _, tx); + let v = rx.await.unwrap(); + v == 1 + } + results::CLOSED | results::CANCELED => false, + 1 => true, + _ => unreachable!(), + } +} + +/// Await the completion of a stream read or write. +#[doc(hidden)] +pub async unsafe fn await_stream_result( + import: unsafe extern "C" fn(u32, *mut u8, u32) -> u32, + stream: u32, + address: *mut u8, + count: u32, +) -> Option { + let result = import(stream, address, count); + match result { + results::BLOCKED => { + assert!(!CURRENT.is_null()); + (*CURRENT).todo += 1; + let (tx, rx) = oneshot::channel(); + CALLS.insert(stream as _, tx); + let v = rx.await.unwrap(); + if let results::CLOSED | results::CANCELED = v { + None + } else { + Some(usize::try_from(v).unwrap()) + } + } + results::CLOSED | results::CANCELED => None, + v => Some(usize::try_from(v).unwrap()), + } +} + +/// Call the `subtask.drop` canonical built-in function. +fn subtask_drop(subtask: u32) { + #[cfg(not(target_arch = "wasm32"))] + { + _ = subtask; + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[subtask-drop]"] + fn subtask_drop(_: u32); + } + unsafe { + subtask_drop(subtask); + } + } +} + +/// Handle a progress notification from the host regarding either a call to an +/// async-lowered import or a stream/future read/write operation. +#[doc(hidden)] +pub unsafe fn callback(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + const _EVENT_CALL_STARTING: i32 = 0; + const EVENT_CALL_STARTED: i32 = 1; + const EVENT_CALL_RETURNED: i32 = 2; + const EVENT_CALL_DONE: i32 = 3; + const _EVENT_YIELDED: i32 = 4; + const EVENT_STREAM_READ: i32 = 5; + const EVENT_STREAM_WRITE: i32 = 6; + const EVENT_FUTURE_READ: i32 = 7; + const EVENT_FUTURE_WRITE: i32 = 8; + + match event0 { + EVENT_CALL_STARTED => 0, + EVENT_CALL_RETURNED | EVENT_CALL_DONE | EVENT_STREAM_READ | EVENT_STREAM_WRITE + | EVENT_FUTURE_READ | EVENT_FUTURE_WRITE => { + if let Some(call) = CALLS.remove(&event1) { + _ = call.send(event2 as _); + } + + let state = ctx as *mut FutureState; + let done = poll(state).is_ready(); + + if event0 == EVENT_CALL_DONE { + subtask_drop(event1 as u32); + } + + if matches!( + event0, + EVENT_CALL_DONE + | EVENT_STREAM_READ + | EVENT_STREAM_WRITE + | EVENT_FUTURE_READ + | EVENT_FUTURE_WRITE + ) { + (*state).todo -= 1; + } + + if done && (*state).todo == 0 { + drop(Box::from_raw(state)); + 1 + } else { + 0 + } + } + _ => unreachable!(), + } +} + +/// Represents the Component Model `error-context` type. +pub struct ErrorContext { + handle: u32, +} + +impl ErrorContext { + #[doc(hidden)] + pub fn from_handle(handle: u32) -> Self { + Self { handle } + } + + #[doc(hidden)] + pub fn handle(&self) -> u32 { + self.handle + } +} + +impl Debug for ErrorContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ErrorContext").finish() + } +} + +impl Display for ErrorContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Error") + } +} + +impl std::error::Error for ErrorContext {} + +impl Drop for ErrorContext { + fn drop(&mut self) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[error-context-drop]"] + fn error_drop(_: u32); + } + if self.handle != 0 { + unsafe { error_drop(self.handle) } + } + } + } +} + +/// Defer the specified future to be run after the current async-lifted export +/// task has returned a value. +/// +/// The task will remain in a running state until all spawned futures have +/// completed. +pub fn spawn(future: impl Future + 'static) { + unsafe { SPAWNED.push(Box::pin(future)) } +} + +fn task_wait(state: &mut FutureState) { + #[cfg(not(target_arch = "wasm32"))] + { + _ = state; + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-wait]"] + fn wait(_: *mut i32) -> i32; + } + let mut payload = [0i32; 2]; + unsafe { + let event0 = wait(payload.as_mut_ptr()); + callback(state as *mut _ as _, event0, payload[0], payload[1]); + } + } +} + +/// Run the specified future to completion, returning the result. +/// +/// This uses `task.wait` to poll for progress on any in-progress calls to +/// async-lowered imports as necessary. +// TODO: refactor so `'static` bounds aren't necessary +pub fn block_on(future: impl Future + 'static) -> T { + let (tx, mut rx) = oneshot::channel(); + let state = &mut FutureState { + todo: 0, + tasks: Some( + [Box::pin(future.map(move |v| drop(tx.send(v)))) as BoxFuture] + .into_iter() + .collect(), + ), + }; + loop { + match unsafe { poll(state) } { + Poll::Ready(()) => break rx.try_recv().unwrap().unwrap(), + Poll::Pending => task_wait(state), + } + } +} + +fn task_poll(state: &mut FutureState) -> bool { + #[cfg(not(target_arch = "wasm32"))] + { + _ = state; + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-poll]"] + fn poll(_: *mut i32) -> i32; + } + let mut payload = [0i32; 3]; + unsafe { + let got_event = poll(payload.as_mut_ptr()) != 0; + if got_event { + callback(state as *mut _ as _, payload[0], payload[1], payload[2]); + } + got_event + } + } +} + +/// Attempt to run the specified future to completion without blocking, +/// returning the result if it completes. +/// +/// This is similar to `block_on` except that it uses `task.poll` instead of +/// `task.wait` to check for progress on any in-progress calls to async-lowered +/// imports, returning `None` if one or more of those calls remain pending. +// TODO: refactor so `'static` bounds aren't necessary +pub fn poll_future(future: impl Future + 'static) -> Option { + let (tx, mut rx) = oneshot::channel(); + let state = &mut FutureState { + todo: 0, + tasks: Some( + [Box::pin(future.map(move |v| drop(tx.send(v)))) as BoxFuture] + .into_iter() + .collect(), + ), + }; + loop { + match unsafe { poll(state) } { + Poll::Ready(()) => break Some(rx.try_recv().unwrap().unwrap()), + Poll::Pending => { + if !task_poll(state) { + break None; + } + } + } + } +} + +/// Call the `task.yield` canonical built-in function. +/// +/// This yields control to the host temporarily, allowing other tasks to make +/// progress. It's a good idea to call this inside a busy loop which does not +/// otherwise ever yield control the the host. +pub fn task_yield() { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-yield]"] + fn yield_(); + } + unsafe { + yield_(); + } + } +} + +/// Call the `task.backpressure` canonical built-in function. +/// +/// When `enabled` is `true`, this tells the host to defer any new calls to this +/// component instance until further notice (i.e. until `task.backpressure` is +/// called again with `enabled` set to `false`). +pub fn task_backpressure(enabled: bool) { + #[cfg(not(target_arch = "wasm32"))] + { + _ = enabled; + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-backpressure]"] + fn backpressure(_: i32); + } + unsafe { + backpressure(if enabled { 1 } else { 0 }); + } + } +} diff --git a/crates/guest-rust/rt/src/lib.rs b/crates/guest-rust/rt/src/lib.rs index 406ed61dc..c9a63cf71 100644 --- a/crates/guest-rust/rt/src/lib.rs +++ b/crates/guest-rust/rt/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(feature = "async"), no_std)] extern crate alloc; @@ -112,3 +112,11 @@ pub fn run_ctors_once() { } } } + +/// Support for using the Component Model Async ABI +#[cfg(not(feature = "async"))] +pub mod async_support {} + +/// Support for using the Component Model Async ABI +#[cfg(feature = "async")] +pub mod async_support; diff --git a/crates/markdown/src/lib.rs b/crates/markdown/src/lib.rs index ae6380003..2ecd5421f 100644 --- a/crates/markdown/src/lib.rs +++ b/crates/markdown/src/lib.rs @@ -413,28 +413,14 @@ impl InterfaceGenerator<'_> { self.push_str("future"); } }, - TypeDefKind::Stream(s) => match (s.element, s.end) { - (Some(element), Some(end)) => { - self.push_str("stream<"); - self.print_ty(&element); - self.push_str(", "); - self.print_ty(&end); - self.push_str(">"); - } - (None, Some(end)) => { - self.push_str("stream<_, "); - self.print_ty(&end); - self.push_str(">"); - } - (Some(element), None) => { - self.push_str("stream<"); - self.print_ty(&element); - self.push_str(">"); - } - (None, None) => { - self.push_str("stream"); - } - }, + TypeDefKind::Stream(t) => { + self.push_str("stream<"); + self.print_ty(t); + self.push_str(">"); + } + TypeDefKind::ErrorContext => { + self.push_str("error-context"); + } TypeDefKind::Handle(Handle::Own(ty)) => { self.push_str("own<"); self.print_ty(&Type::Id(*ty)); diff --git a/crates/moonbit/src/lib.rs b/crates/moonbit/src/lib.rs index 0fb989372..6ddd7a288 100644 --- a/crates/moonbit/src/lib.rs +++ b/crates/moonbit/src/lib.rs @@ -779,6 +779,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut bindgen, + false, ); let src = bindgen.src; @@ -868,6 +869,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut bindgen, + false, ); assert!(!bindgen.needs_cleanup_list); @@ -927,7 +929,7 @@ impl InterfaceGenerator<'_> { (0..sig.results.len()).map(|i| format!("p{i}")).collect(), ); - abi::post_return(bindgen.gen.resolve, func, &mut bindgen); + abi::post_return(bindgen.gen.resolve, func, &mut bindgen, false); let src = bindgen.src; @@ -2579,6 +2581,21 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!(self.src, "{ffi_qualifier}free({address})"); } + + Instruction::Flush { amt } => { + results.extend(operands.iter().take(*amt).map(|v| v.clone())); + } + + Instruction::AsyncMalloc { .. } + | Instruction::AsyncPostCallInterface { .. } + | Instruction::AsyncCallReturn { .. } + | Instruction::FutureLower { .. } + | Instruction::FutureLift { .. } + | Instruction::StreamLower { .. } + | Instruction::StreamLift { .. } + | Instruction::ErrorContextLower { .. } + | Instruction::ErrorContextLift { .. } + | Instruction::AsyncCallWasm { .. } => todo!(), } } diff --git a/crates/rust/Cargo.toml b/crates/rust/Cargo.toml index 1fe1d3bc2..09a70bed8 100644 --- a/crates/rust/Cargo.toml +++ b/crates/rust/Cargo.toml @@ -32,3 +32,7 @@ test-helpers = { path = '../test-helpers' } # For use with the custom attributes test serde = { version = "1.0", features = ["derive"] } serde_json = "1" + +[features] +default = ["async"] +async = ["wit-bindgen-core/async"] diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 1e9dc9ff1..f8e77a5d8 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -8,6 +8,8 @@ use wit_bindgen_core::{dealias, uwrite, uwriteln, wit_parser::*, Source}; pub(super) struct FunctionBindgen<'a, 'b> { pub gen: &'b mut InterfaceGenerator<'a>, params: Vec, + async_: bool, + wasm_import_module: &'b str, pub src: Source, blocks: Vec, block_storage: Vec<(Source, Vec<(String, String)>)>, @@ -23,10 +25,14 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { pub(super) fn new( gen: &'b mut InterfaceGenerator<'a>, params: Vec, + async_: bool, + wasm_import_module: &'b str, ) -> FunctionBindgen<'a, 'b> { FunctionBindgen { gen, params, + async_, + wasm_import_module, src: Default::default(), blocks: Vec::new(), block_storage: Vec::new(), @@ -58,14 +64,9 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } - fn declare_import( - &mut self, - module_name: &str, - name: &str, - params: &[WasmType], - results: &[WasmType], - ) -> String { + fn declare_import(&mut self, name: &str, params: &[WasmType], results: &[WasmType]) -> String { // Define the actual function we're calling inline + let tmp = self.tmp(); let mut sig = "(".to_owned(); for param in params.iter() { sig.push_str("_: "); @@ -78,6 +79,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { sig.push_str(" -> "); sig.push_str(wasm_type(*result)); } + let module_name = self.wasm_import_module; uwrite!( self.src, " @@ -85,14 +87,14 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { #[link(wasm_import_module = \"{module_name}\")] extern \"C\" {{ #[link_name = \"{name}\"] - fn wit_import{sig}; + fn wit_import{tmp}{sig}; }} #[cfg(not(target_arch = \"wasm32\"))] - fn wit_import{sig} {{ unreachable!() }} + extern \"C\" fn wit_import{tmp}{sig} {{ unreachable!() }} " ); - "wit_import".to_string() + format!("wit_import{tmp}") } fn let_results(&mut self, amt: usize, results: &mut Vec) { @@ -456,6 +458,45 @@ impl Bindgen for FunctionBindgen<'_, '_> { results.push(result); } + Instruction::FutureLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).into_handle() as i32")) + } + + Instruction::FutureLift { .. } => { + let stream_and_future_support = self.gen.path_to_stream_and_future_support(); + let op = &operands[0]; + results.push(format!( + "{stream_and_future_support}::FutureReader::from_handle({op} as u32)" + )) + } + + Instruction::StreamLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).into_handle() as i32")) + } + + Instruction::StreamLift { .. } => { + let stream_and_future_support = self.gen.path_to_stream_and_future_support(); + let op = &operands[0]; + results.push(format!( + "{stream_and_future_support}::StreamReader::from_handle({op} as u32)" + )) + } + + Instruction::ErrorContextLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).handle() as i32")) + } + + Instruction::ErrorContextLift { .. } => { + let async_support = self.gen.path_to_async_support(); + let op = &operands[0]; + results.push(format!( + "{async_support}::ErrorContext::from_handle({op} as u32)" + )) + } + Instruction::RecordLower { ty, record, .. } => { self.record_lower(*ty, record, &operands[0], results); } @@ -779,12 +820,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::IterBasePointer => results.push("base".to_string()), Instruction::CallWasm { name, sig, .. } => { - let func = self.declare_import( - self.gen.wasm_import_module.unwrap(), - name, - &sig.params, - &sig.results, - ); + let func = self.declare_import(name, &sig.params, &sig.results); // ... then call the function with all our operands if !sig.results.is_empty() { @@ -797,8 +833,32 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(");\n"); } + Instruction::AsyncCallWasm { name, size, align } => { + let func = self.declare_import(name, &[WasmType::Pointer; 2], &[WasmType::I32]); + + let async_support = self.gen.path_to_async_support(); + let tmp = self.tmp(); + let layout = format!("layout{tmp}"); + let alloc = self.gen.path_to_std_alloc_module(); + self.push_str(&format!( + "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align});\n", + )); + let operands = operands.join(", "); + uwriteln!( + self.src, + "{async_support}::await_result({func}, {layout}, {operands}).await;" + ); + } + Instruction::CallInterface { func, .. } => { - self.let_results(func.results.len(), results); + if self.async_ { + let tmp = self.tmp(); + let result = format!("result{tmp}"); + self.push_str(&format!("let {result} = ")); + results.push(result); + } else { + self.let_results(func.results.len(), results); + }; match &func.kind { FunctionKind::Freestanding => { self.push_str(&format!("T::{}", to_rust_ident(&func.name))); @@ -839,6 +899,77 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(";\n"); } + Instruction::AsyncMalloc { size, align } => { + let alloc = self.gen.path_to_std_alloc_module(); + let tmp = self.tmp(); + let ptr = format!("ptr{tmp}"); + let layout = format!("layout{tmp}"); + uwriteln!( + self.src, + "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align}); + let {ptr} = {alloc}::alloc({layout});" + ); + results.push(ptr); + } + + Instruction::AsyncPostCallInterface { func } => { + let result = &operands[0]; + results.push("result".into()); + let params = (0..func.results.len()) + .map(|_| { + let tmp = self.tmp(); + let param = format!("result{}", tmp); + results.push(param.clone()); + param + }) + .collect::>() + .join(", "); + let params = if func.results.len() != 1 { + format!("({params})") + } else { + params + }; + let async_support = self.gen.path_to_async_support(); + // TODO: This relies on `abi::Generator` emitting + // `AsyncCallReturn` immediately after this instruction to + // complete the incomplete expression we generate here. We + // should refactor this so it's less fragile (e.g. have + // `abi::Generator` emit a `AsyncCallReturn` first, which would + // push a closure expression we can consume here). + // + // The async-specific `Instruction`s will probably need to be + // refactored anyway once we start implementing support for + // other languages besides Rust. + uwriteln!( + self.src, + "\ + let result = {async_support}::first_poll({result}, |{params}| {{ + " + ); + } + + Instruction::AsyncCallReturn { name, params } => { + let func = self.declare_import(name, params, &[]); + + uwriteln!( + self.src, + "\ + {func}({}); + }}); + ", + operands.join(", ") + ); + } + + Instruction::Flush { amt } => { + for i in 0..*amt { + let tmp = self.tmp(); + let result = format!("result{}", tmp); + uwriteln!(self.src, "let {result} = {};", operands[i]); + results.push(result); + } + } + Instruction::Return { amt, .. } => { self.emit_cleanup(); match amt { @@ -868,7 +999,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { let tmp = self.tmp(); uwriteln!( self.src, - "let l{tmp} = i32::from(*{}.add({offset}).cast::());", + "let l{tmp} = i32::from(*{0}.add({offset}).cast::());", operands[0] ); results.push(format!("l{tmp}")); diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 8a7468960..a96795e6f 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -1,7 +1,7 @@ use crate::bindgen::FunctionBindgen; use crate::{ - int_repr, to_rust_ident, to_upper_camel_case, wasm_type, FnSig, Identifier, InterfaceName, - Ownership, RuntimeItem, RustFlagsRepr, RustWasm, + int_repr, to_rust_ident, to_upper_camel_case, wasm_type, AsyncConfig, FnSig, Identifier, + InterfaceName, Ownership, RuntimeItem, RustFlagsRepr, RustWasm, }; use anyhow::Result; use heck::*; @@ -19,7 +19,7 @@ pub struct InterfaceGenerator<'a> { pub in_import: bool, pub sizes: SizeAlign, pub(super) gen: &'a mut RustWasm, - pub wasm_import_module: Option<&'a str>, + pub wasm_import_module: &'a str, pub resolve: &'a Resolve, pub return_pointer_area_size: usize, pub return_pointer_area_align: usize, @@ -156,6 +156,17 @@ impl InterfaceGenerator<'_> { continue; } + let async_ = match &self.gen.opts.async_ { + AsyncConfig::None => false, + AsyncConfig::All => true, + AsyncConfig::Some { exports, .. } => { + exports.contains(&if let Some((_, key)) = interface { + format!("{}#{}", self.resolve.name_world_key(key), func.name) + } else { + func.name.clone() + }) + } + }; let resource = match func.kind { FunctionKind::Freestanding => None, FunctionKind::Method(id) @@ -163,12 +174,13 @@ impl InterfaceGenerator<'_> { | FunctionKind::Static(id) => Some(id), }; - funcs_to_export.push((func, resource)); + funcs_to_export.push((func, resource, async_)); let (trait_name, methods) = traits.get_mut(&resource).unwrap(); - self.generate_guest_export(func, &trait_name); + self.generate_guest_export(func, interface.map(|(_, k)| k), &trait_name, async_); let prev = mem::take(&mut self.src); let mut sig = FnSig { + async_, use_item_name: true, private: true, ..Default::default() @@ -177,7 +189,7 @@ impl InterfaceGenerator<'_> { sig.self_arg = Some("&self".into()); sig.self_is_first_param = true; } - self.print_signature(func, true, &sig); + self.print_signature(func, true, &sig, false); self.src.push_str(";\n"); let trait_method = mem::replace(&mut self.src, prev); methods.push(trait_method); @@ -188,9 +200,9 @@ impl InterfaceGenerator<'_> { self.generate_interface_trait( &name, &methods, - traits.iter().map(|(resource, (trait_name, _methods))| { - (resource.unwrap(), trait_name.as_str()) - }), + traits + .iter() + .map(|(resource, (trait_name, ..))| (resource.unwrap(), trait_name.as_str())), ) } @@ -259,7 +271,7 @@ fn _resource_rep(handle: u32) -> *mut u8 None => { let world = match self.identifier { Identifier::World(w) => w, - Identifier::Interface(..) => unreachable!(), + Identifier::None | Identifier::Interface(..) => unreachable!(), }; let world = self.resolve.worlds[world].name.to_snake_case(); format!("__export_world_{world}_cabi") @@ -292,7 +304,7 @@ macro_rules! {macro_name} {{ " ); - for (func, resource) in funcs_to_export { + for (func, resource, async_) in funcs_to_export { let ty = match resource { None => "$ty".to_string(), Some(id) => { @@ -304,13 +316,13 @@ macro_rules! {macro_name} {{ format!("<$ty as $($path_to_types)*::Guest>::{name}") } }; - self.generate_raw_cabi_export(func, &ty, "$($path_to_types)*"); + self.generate_raw_cabi_export(func, &ty, "$($path_to_types)*", async_); } let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); for name in resources_to_drop { let module = match self.identifier { Identifier::Interface(_, key) => self.resolve.name_world_key(key), - Identifier::World(_) => unreachable!(), + Identifier::None | Identifier::World(_) => unreachable!(), }; let camel = name.to_upper_camel_case(); uwriteln!( @@ -357,9 +369,13 @@ macro_rules! {macro_name} {{ uwriteln!(self.src, "}}"); } - pub fn generate_imports<'a>(&mut self, funcs: impl Iterator) { + pub fn generate_imports<'a>( + &mut self, + funcs: impl Iterator, + interface: Option<&WorldKey>, + ) { for func in funcs { - self.generate_guest_import(func); + self.generate_guest_import(func, interface); } } @@ -454,12 +470,387 @@ macro_rules! {macro_name} {{ map.push((module, module_path)) } - fn generate_guest_import(&mut self, func: &Function) { + fn generate_payloads(&mut self, prefix: &str, func: &Function, interface: Option<&WorldKey>) { + for (index, ty) in func + .find_futures_and_streams(self.resolve) + .into_iter() + .enumerate() + { + let module = format!( + "{prefix}{}", + interface + .map(|name| self.resolve.name_world_key(name)) + .unwrap_or_else(|| "$root".into()) + ); + let func_name = &func.name; + let type_mode = TypeMode { + lifetime: None, + lists_borrowed: false, + style: TypeOwnershipStyle::Owned, + }; + let stream_and_future_support = self.path_to_stream_and_future_support(); + let async_support = self.path_to_async_support(); + + match &self.resolve.types[ty].kind { + TypeDefKind::Future(payload_type) => { + let (name, full_name) = if let Some(payload_type) = payload_type { + ( + { + let old = mem::take(&mut self.src); + self.print_ty(&payload_type, type_mode); + String::from(mem::replace(&mut self.src, old)) + }, + { + let old = mem::take(&mut self.src); + let old_identifier = + mem::replace(&mut self.identifier, Identifier::None); + self.print_ty(&payload_type, type_mode); + self.identifier = old_identifier; + String::from(mem::replace(&mut self.src, old)) + }, + ) + } else { + ("()".into(), "()".into()) + }; + + if self.gen.future_payloads_emitted.insert(full_name) { + let (size, align) = if let Some(payload_type) = payload_type { + ( + self.sizes.size(payload_type), + self.sizes.align(payload_type), + ) + } else { + ( + ArchitectureSize { + bytes: 0, + pointers: 0, + }, + Alignment::default(), + ) + }; + let size = size.size_wasm32(); + let align = align.align_wasm32(); + let (lower, lift) = if let Some(payload_type) = payload_type { + let lower = + self.lower_to_memory("address", "value", &payload_type, &module); + let lift = + self.lift_from_memory("address", "value", &payload_type, &module); + (lower, lift) + } else { + (String::new(), "let value = ();\n".into()) + }; + + uwriteln!( + self.src, + r#" +impl {stream_and_future_support}::FuturePayload for {name} {{ + fn new() -> u32 {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-new-{index}]{func_name}"] + fn new() -> u32; + }} + unsafe {{ new() }} + }} + }} + + async fn write(future: u32, value: Self) -> bool {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[repr(align({align}))] + struct Buffer([::core::mem::MaybeUninit::; {size}]); + 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; + }} + + unsafe {{ {async_support}::await_future_result(wit_import, future, address).await }} + }} + }} + + async fn read(future: u32) -> Option {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + struct Buffer([::core::mem::MaybeUninit::; {size}]); + let mut buffer = Buffer([::core::mem::MaybeUninit::uninit(); {size}]); + let address = buffer.0.as_mut_ptr() as *mut u8; + + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[async][future-read-{index}]{func_name}"] + fn wit_import(_: u32, _: *mut u8) -> u32; + }} + + if unsafe {{ {async_support}::await_future_result(wit_import, future, address).await }} {{ + {lift} + Some(value) + }} else {{ + None + }} + }} + }} + + fn close_writable(writer: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-close-writable-{index}]{func_name}"] + fn drop(_: u32, _: u32); + }} + unsafe {{ drop(writer, 0) }} + }} + }} + + fn close_readable(reader: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-close-readable-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(reader) }} + }} + }} +}} + "#, + ); + } + } + TypeDefKind::Stream(payload_type) => { + let name = { + let old = mem::take(&mut self.src); + self.print_ty(&payload_type, type_mode); + String::from(mem::replace(&mut self.src, old)) + }; + + let full_name = { + let old = mem::take(&mut self.src); + let old_identifier = mem::replace(&mut self.identifier, Identifier::None); + self.print_ty(&payload_type, type_mode); + self.identifier = old_identifier; + String::from(mem::replace(&mut self.src, old)) + }; + + if self.gen.stream_payloads_emitted.insert(full_name) { + let size = self.sizes.size(payload_type).size_wasm32(); + let align = self.sizes.align(payload_type).align_wasm32(); + let alloc = self.path_to_std_alloc_module(); + let (lower_address, lower, lift_address, lift) = + if stream_direct(payload_type) { + let lower_address = "let address = values.as_ptr() as _;".into(); + let lift_address = "let address = values.as_mut_ptr() as _;".into(); + ( + lower_address, + String::new(), + lift_address, + "let value = ();\n".into(), + ) + } else { + let address = format!( + "let address = {alloc}::alloc\ + ({alloc}::Layout::from_size_align_unchecked\ + ({size} * values.len(), {align}));" + ); + let lower = self.lower_to_memory( + "address", + "value", + &payload_type, + &module, + ); + let lower = format!( + r#" +for (index, value) in values.iter().enumerate() {{ + let address = address + (index * size); + {lower} +}} + "# + ); + let lift = self.lift_from_memory( + "address", + "value", + &payload_type, + &module, + ); + let lift = format!( + r#" +for (index, dst) in values.iter_mut().enumerate() {{ + let address = address + (index * size); + {lift} + *dst = value; +}} + "# + ); + (address.clone(), lower, address, lift) + }; + + uwriteln!( + self.src, + r#" +impl {stream_and_future_support}::StreamPayload for {name} {{ + fn new() -> u32 {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-new-{index}]{func_name}"] + fn new() -> u32; + }} + unsafe {{ new() }} + }} + }} + + async fn write(stream: u32, values: &[Self]) -> Option {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + {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; + }} + + unsafe {{ + {async_support}::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await + }} + }} + }} + + async fn read(stream: u32, values: &mut [::core::mem::MaybeUninit::]) -> Option {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + {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; + }} + + let count = unsafe {{ + {async_support}::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await + }}; + #[allow(unused)] + if let Some(count) = count {{ + {lift} + }} + count + }} + }} + + fn close_writable(writer: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-close-writable-{index}]{func_name}"] + fn drop(_: u32, _: u32); + }} + unsafe {{ drop(writer, 0) }} + }} + }} + + fn close_readable(reader: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-close-readable-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(reader) }} + }} + }} +}} + "# + ); + } + } + _ => unreachable!(), + } + } + } + + fn generate_guest_import(&mut self, func: &Function, interface: Option<&WorldKey>) { if self.gen.skip.contains(&func.name) { return; } - let mut sig = FnSig::default(); + self.generate_payloads("[import-payload]", func, interface); + + let async_ = match &self.gen.opts.async_ { + AsyncConfig::None => false, + AsyncConfig::All => true, + AsyncConfig::Some { imports, .. } => imports.contains(&if let Some(key) = interface { + format!("{}#{}", self.resolve.name_world_key(key), func.name) + } else { + func.name.clone() + }), + }; + let mut sig = FnSig { + async_, + ..Default::default() + }; match func.kind { FunctionKind::Freestanding => {} FunctionKind::Method(id) | FunctionKind::Static(id) | FunctionKind::Constructor(id) => { @@ -474,17 +865,53 @@ macro_rules! {macro_name} {{ } } self.src.push_str("#[allow(unused_unsafe, clippy::all)]\n"); - let params = self.print_signature(func, false, &sig); + let params = self.print_signature(func, false, &sig, true); self.src.push_str("{\n"); self.src.push_str("unsafe {\n"); - let mut f = FunctionBindgen::new(self, params); + self.generate_guest_import_body(&self.wasm_import_module, func, params, async_); + + self.src.push_str("}\n"); + self.src.push_str("}\n"); + + match func.kind { + FunctionKind::Freestanding => {} + FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::Constructor(_) => { + self.src.push_str("}\n"); + } + } + } + + fn lower_to_memory(&mut self, address: &str, value: &str, ty: &Type, module: &str) -> String { + let mut f = FunctionBindgen::new(self, Vec::new(), true, module); + abi::lower_to_memory(f.gen.resolve, &mut f, address.into(), value.into(), ty); + format!("unsafe {{ {} }}", String::from(f.src)) + } + + fn lift_from_memory(&mut self, address: &str, value: &str, ty: &Type, module: &str) -> String { + let mut f = FunctionBindgen::new(self, Vec::new(), true, module); + let result = abi::lift_from_memory(f.gen.resolve, &mut f, address.into(), ty); + format!( + "let {value} = unsafe {{ {}\n{result} }};", + String::from(f.src) + ) + } + + fn generate_guest_import_body( + &mut self, + module: &str, + func: &Function, + params: Vec, + async_: bool, + ) { + let mut f = FunctionBindgen::new(self, params, async_, module); abi::call( f.gen.resolve, AbiVariant::GuestImport, LiftLower::LowerArgsLiftResults, func, &mut f, + async_, ); let FunctionBindgen { needs_cleanup_list, @@ -511,29 +938,28 @@ macro_rules! {macro_name} {{ ); } self.src.push_str(&String::from(src)); - - self.src.push_str("}\n"); - self.src.push_str("}\n"); - - match func.kind { - FunctionKind::Freestanding => {} - FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::Constructor(_) => { - self.src.push_str("}\n"); - } - } } - fn generate_guest_export(&mut self, func: &Function, trait_name: &str) { + fn generate_guest_export( + &mut self, + func: &Function, + interface: Option<&WorldKey>, + trait_name: &str, + async_: bool, + ) { let name_snake = func.name.to_snake_case().replace('.', "_"); + + self.generate_payloads("[export-payload]", func, interface); + uwrite!( self.src, "\ #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn _export_{name_snake}_cabi\ -", + ", ); - let params = self.print_export_sig(func); + let params = self.print_export_sig(func, async_); self.push_str(" {"); if !self.gen.opts.disable_run_ctors_once_workaround { @@ -552,13 +978,14 @@ macro_rules! {macro_name} {{ ); } - let mut f = FunctionBindgen::new(self, params); + let mut f = FunctionBindgen::new(self, params, async_, self.wasm_import_module); abi::call( f.gen.resolve, AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, func, &mut f, + async_, ); let FunctionBindgen { needs_cleanup_list, @@ -574,20 +1001,32 @@ macro_rules! {macro_name} {{ self.src.push_str(&String::from(src)); self.src.push_str("}\n"); - if abi::guest_export_needs_post_return(self.resolve, func) { + if async_ { + let async_support = self.path_to_async_support(); + uwrite!( + self.src, + "\ + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __callback_{name_snake}(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 {{ + {async_support}::callback(ctx, event0, event1, event2) + }} + " + ); + } else if abi::guest_export_needs_post_return(self.resolve, func) { uwrite!( self.src, "\ #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn __post_return_{name_snake}\ -" + " ); let params = self.print_post_return_sig(func); self.src.push_str("{\n"); - let mut f = FunctionBindgen::new(self, params); - abi::post_return(f.gen.resolve, func, &mut f); + let mut f = FunctionBindgen::new(self, params, async_, self.wasm_import_module); + abi::post_return(f.gen.resolve, func, &mut f, async_); let FunctionBindgen { needs_cleanup_list, src, @@ -601,14 +1040,26 @@ macro_rules! {macro_name} {{ } } - fn generate_raw_cabi_export(&mut self, func: &Function, ty: &str, path_to_self: &str) { + fn generate_raw_cabi_export( + &mut self, + func: &Function, + ty: &str, + path_to_self: &str, + async_: bool, + ) { let name_snake = func.name.to_snake_case().replace('.', "_"); let wasm_module_export_name = match self.identifier { Identifier::Interface(_, key) => Some(self.resolve.name_world_key(key)), Identifier::World(_) => None, + Identifier::None => unreachable!(), }; let export_prefix = self.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]{export_name}") + } else { + export_name.to_string() + }; uwrite!( self.src, "\ @@ -617,7 +1068,7 @@ macro_rules! {macro_name} {{ ", ); - let params = self.print_export_sig(func); + let params = self.print_export_sig(func, async_); self.push_str(" {\n"); uwriteln!( self.src, @@ -626,8 +1077,18 @@ macro_rules! {macro_name} {{ ); self.push_str("}\n"); - if abi::guest_export_needs_post_return(self.resolve, func) { - let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); + let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); + if async_ { + uwrite!( + self.src, + "\ + #[export_name = \"{export_prefix}[callback]{export_name}\"] + unsafe extern \"C\" fn _callback_{name_snake}(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 {{ + {path_to_self}::__callback_{name_snake}(ctx, event0, event1, event2) + }} + " + ); + } else if abi::guest_export_needs_post_return(self.resolve, func) { uwrite!( self.src, "\ @@ -646,7 +1107,7 @@ macro_rules! {macro_name} {{ } } - fn print_export_sig(&mut self, func: &Function) -> Vec { + fn print_export_sig(&mut self, func: &Function, async_: bool) -> Vec { self.src.push_str("("); let sig = self.resolve.wasm_signature(AbiVariant::GuestExport, func); let mut params = Vec::new(); @@ -657,13 +1118,18 @@ macro_rules! {macro_name} {{ } self.src.push_str(")"); - match sig.results.len() { - 0 => {} - 1 => { - uwrite!(self.src, " -> {}", wasm_type(sig.results[0])); + if async_ { + self.push_str(" -> *mut u8"); + } else { + match sig.results.len() { + 0 => {} + 1 => { + uwrite!(self.src, " -> {}", wasm_type(sig.results[0])); + } + _ => unimplemented!(), } - _ => unimplemented!(), } + params } @@ -740,7 +1206,7 @@ macro_rules! {macro_name} {{ sig.self_arg = Some("&self".into()); sig.self_is_first_param = true; } - self.print_signature(func, true, &sig); + self.print_signature(func, true, &sig, true); self.src.push_str("{ unreachable!() }\n"); } @@ -798,12 +1264,18 @@ macro_rules! {macro_name} {{ // } } - fn print_signature(&mut self, func: &Function, params_owned: bool, sig: &FnSig) -> Vec { - let params = self.print_docs_and_params(func, params_owned, sig); + fn print_signature( + &mut self, + func: &Function, + params_owned: bool, + sig: &FnSig, + use_async_sugar: bool, + ) -> Vec { + let params = self.print_docs_and_params(func, params_owned, sig, use_async_sugar); if let FunctionKind::Constructor(_) = &func.kind { self.push_str(" -> Self") } else { - self.print_results(&func.results); + self.print_results(&func.results, sig.async_ && !use_async_sugar); } params } @@ -813,6 +1285,7 @@ macro_rules! {macro_name} {{ func: &Function, params_owned: bool, sig: &FnSig, + use_async_sugar: bool, ) -> Vec { self.rustdoc(&func.docs); self.rustdoc_params(&func.params, "Parameters"); @@ -825,7 +1298,7 @@ macro_rules! {macro_name} {{ if sig.unsafe_ { self.push_str("unsafe "); } - if sig.async_ { + if sig.async_ && use_async_sugar { self.push_str("async "); } self.push_str("fn "); @@ -915,18 +1388,24 @@ macro_rules! {macro_name} {{ params } - fn print_results(&mut self, results: &Results) { + fn print_results(&mut self, results: &Results, async_: bool) { + self.push_str(" -> "); + if async_ { + self.push_str("impl ::core::future::Future {} + 0 => { + self.push_str("()"); + } 1 => { - self.push_str(" -> "); let ty = results.iter_types().next().unwrap(); let mode = self.type_mode_for(ty, TypeOwnershipStyle::Owned, "'INVALID"); assert!(mode.lifetime.is_none()); self.print_ty(ty, mode); } _ => { - self.push_str(" -> ("); + self.push_str("("); for ty in results.iter_types() { let mode = self.type_mode_for(ty, TypeOwnershipStyle::Owned, "'INVALID"); assert!(mode.lifetime.is_none()); @@ -936,6 +1415,10 @@ macro_rules! {macro_name} {{ self.push_str(")") } } + + if async_ { + self.push_str("> + 'static"); + } } /// Calculates the `TypeMode` to be used for the `ty` specified. @@ -1863,6 +2346,17 @@ macro_rules! {macro_name} {{ self.path_from_runtime_module(RuntimeItem::StdAllocModule, "alloc") } + pub fn path_to_stream_and_future_support(&mut self) -> String { + self.path_from_runtime_module( + RuntimeItem::StreamAndFutureSupport, + "stream_and_future_support", + ) + } + + pub fn path_to_async_support(&mut self) -> String { + "::wit_bindgen_rt::async_support".into() + } + fn path_from_runtime_module( &mut self, item: RuntimeItem, @@ -1920,11 +2414,12 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { }} "# ); - self.wasm_import_module.unwrap().to_string() + self.wasm_import_module.to_string() } else { let module = match self.identifier { Identifier::Interface(_, key) => self.resolve.name_world_key(key), Identifier::World(_) => unimplemented!("resource exports from worlds"), + Identifier::None => unreachable!(), }; let box_path = self.path_to_box(); uwriteln!( @@ -2197,7 +2692,7 @@ impl<'a, 'b> wit_bindgen_core::AnonymousTypeGenerator<'a> for AnonTypeGenerator< self.resolve } - fn anonymous_typ_type(&mut self, _id: TypeId, ty: &Type, _docs: &Docs) { + fn anonymous_type_type(&mut self, _id: TypeId, ty: &Type, _docs: &Docs) { self.interface.print_ty(ty, self.mode); } @@ -2265,18 +2760,42 @@ impl<'a, 'b> wit_bindgen_core::AnonymousTypeGenerator<'a> for AnonTypeGenerator< } fn anonymous_type_future(&mut self, _id: TypeId, ty: &Option, _docs: &Docs) { - self.interface.push_str("Future<"); + let stream_and_future_support = self.interface.path_to_stream_and_future_support(); + self.interface + .push_str(&format!("{stream_and_future_support}::FutureReader<")); self.interface.print_optional_ty(ty.as_ref(), self.mode); self.interface.push_str(">"); } - fn anonymous_type_stream(&mut self, _id: TypeId, stream: &Stream, _docs: &Docs) { - self.interface.push_str("Stream<"); + fn anonymous_type_stream(&mut self, _id: TypeId, ty: &Type, _docs: &Docs) { + let stream_and_future_support = self.interface.path_to_stream_and_future_support(); self.interface - .print_optional_ty(stream.element.as_ref(), self.mode); - self.interface.push_str(","); - self.interface - .print_optional_ty(stream.end.as_ref(), self.mode); + .push_str(&format!("{stream_and_future_support}::StreamReader<")); + self.interface.print_ty(ty, self.mode); self.interface.push_str(">"); } + + fn anonymous_type_error_context(&mut self) { + let async_support = self.interface.path_to_async_support(); + self.interface + .push_str(&format!("{async_support}::ErrorContext")); + } +} + +fn stream_direct(ty: &Type) -> bool { + // TODO: might be able to return `true` for other types if the generated Rust versions of those types are + // guaranteed to be safely transmutable to and from their lowered form. + matches!( + ty, + Type::U8 + | Type::S8 + | Type::U16 + | Type::S16 + | Type::U32 + | Type::S32 + | Type::U64 + | Type::S64 + | Type::F32 + | Type::F64 + ) } diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 6edb6be27..745e49361 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -44,8 +44,12 @@ struct RustWasm { rt_module: IndexSet, export_macros: Vec<(String, String)>, + /// Interface names to how they should be generated with: GenerationConfiguration, + + future_payloads_emitted: HashSet, + stream_payloads_emitted: HashSet, } #[derive(Default)] @@ -97,6 +101,7 @@ enum RuntimeItem { AsF64, ResourceType, BoxType, + StreamAndFutureSupport, } #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -117,6 +122,52 @@ fn parse_with(s: &str) -> Result<(String, WithOption), String> { Ok((k.to_string(), v)) } +#[derive(Default, Debug, Clone)] +pub enum AsyncConfig { + #[default] + None, + Some { + imports: Vec, + exports: Vec, + }, + All, +} + +#[cfg(feature = "clap")] +fn parse_async(s: &str) -> Result { + Ok(match s { + "none" => AsyncConfig::None, + "all" => AsyncConfig::All, + _ => { + if let Some(values) = s.strip_prefix("some=") { + let mut imports = Vec::new(); + let mut exports = Vec::new(); + for value in values.split(',') { + let error = || { + Err(format!( + "expected string of form `import:` or `export:`; got `{value}`" + )) + }; + if let Some((k, v)) = value.split_once(":") { + match k { + "import" => imports.push(v.into()), + "export" => exports.push(v.into()), + _ => return error(), + } + } else { + return error(); + } + } + AsyncConfig::Some { imports, exports } + } else { + return Err(format!( + "expected string of form `none`, `all`, or `some=[,...]`; got `{s}`" + )); + } + } + }) +} + #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "clap", derive(clap::Args))] pub struct Opts { @@ -234,6 +285,18 @@ pub struct Opts { /// library-based usage of `generate!` prone to breakage. #[cfg_attr(feature = "clap", arg(long))] pub disable_custom_section_link_helpers: bool, + + /// Determines which functions to lift or lower `async`, if any. + /// + /// Accepted values are: + /// - none + /// - all + /// - some=[,...], where each is of the form: + /// - import: or + /// - export: + #[cfg_attr(all(feature = "clap", feature = "async"), arg(long = "async", value_parser = parse_async))] + #[cfg_attr(all(feature = "clap", not(feature = "async")), skip)] + pub async_: AsyncConfig, } impl Opts { @@ -253,7 +316,7 @@ impl RustWasm { fn interface<'a>( &'a mut self, identifier: Identifier<'a>, - wasm_import_module: Option<&'a str>, + wasm_import_module: &'a str, resolve: &'a Resolve, in_import: bool, ) -> InterfaceGenerator<'a> { @@ -297,7 +360,7 @@ impl RustWasm { // Ignore dead-code warnings. If the bindings are only used // within a crate, and not exported to a different crate, some // parts may be unused, and that's ok. - uwriteln!(me, "#[allow(dead_code)]"); + uwriteln!(me, "#[allow(dead_code, clippy::all)]"); uwriteln!(me, "pub mod {name} {{"); emit(me, submodule); @@ -366,6 +429,7 @@ impl RustWasm { return; } self.src.push_str("mod _rt {\n"); + self.src.push_str("#![allow(dead_code, clippy::all)]\n"); let mut emitted = IndexSet::new(); while !self.rt_module.is_empty() { for item in mem::take(&mut self.rt_module) { @@ -375,6 +439,10 @@ impl RustWasm { } } self.src.push_str("}\n"); + if emitted.contains(&RuntimeItem::StreamAndFutureSupport) { + self.src + .push_str("pub use _rt::stream_and_future_support;\n"); + } } fn emit_runtime_item(&mut self, item: RuntimeItem) { @@ -607,6 +675,13 @@ impl Drop for Resource { "#, ); } + + RuntimeItem::StreamAndFutureSupport => { + self.src.push_str("pub mod stream_and_future_support {"); + self.src + .push_str(include_str!("stream_and_future_support.rs")); + self.src.push_str("}"); + } } } @@ -780,6 +855,7 @@ macro_rules! __export_{world_name}_impl {{ .unwrap(); self.src.push_str("#[doc(hidden)]\n"); + self.src.push_str("#[allow(clippy::octal_escapes)]\n"); self.src.push_str(&format!( "pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; {}] = *b\"\\\n", component_type.len() @@ -909,7 +985,7 @@ impl WorldGenerator for RustWasm { let wasm_import_module = resolve.name_world_key(name); let mut gen = self.interface( Identifier::Interface(id, name), - Some(&wasm_import_module), + &wasm_import_module, resolve, true, ); @@ -919,7 +995,7 @@ impl WorldGenerator for RustWasm { } gen.types(id); - gen.generate_imports(resolve.interfaces[id].functions.values()); + gen.generate_imports(resolve.interfaces[id].functions.values(), Some(name)); gen.finish_append_submodule(&snake, module_path); @@ -935,9 +1011,9 @@ impl WorldGenerator for RustWasm { ) { self.import_funcs_called = true; - let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true); + let mut gen = self.interface(Identifier::World(world), "$root", resolve, true); - gen.generate_imports(funcs.iter().map(|(_, func)| *func)); + gen.generate_imports(funcs.iter().map(|(_, func)| *func), None); let src = gen.finish(); self.src.push_str(&src); @@ -951,7 +1027,13 @@ impl WorldGenerator for RustWasm { _files: &mut Files, ) -> Result<()> { self.interface_last_seen_as_import.insert(id, false); - let mut gen = self.interface(Identifier::Interface(id, name), None, resolve, false); + let wasm_import_module = format!("[export]{}", resolve.name_world_key(name)); + let mut gen = self.interface( + Identifier::Interface(id, name), + &wasm_import_module, + resolve, + false, + ); let (snake, module_path) = gen.start_append_submodule(name); if gen.gen.name_interface(resolve, id, name, true)? { return Ok(()); @@ -965,7 +1047,12 @@ impl WorldGenerator for RustWasm { if self.opts.stubs { let world_id = self.world.unwrap(); - let mut gen = self.interface(Identifier::World(world_id), None, resolve, false); + let mut gen = self.interface( + Identifier::World(world_id), + &wasm_import_module, + resolve, + false, + ); gen.generate_stub(Some((id, name)), resolve.interfaces[id].functions.values()); let stub = gen.finish(); self.src.push_str(&stub); @@ -980,14 +1067,14 @@ impl WorldGenerator for RustWasm { funcs: &[(&str, &Function)], _files: &mut Files, ) -> Result<()> { - let mut gen = self.interface(Identifier::World(world), None, resolve, false); + let mut gen = self.interface(Identifier::World(world), "[export]$root", resolve, false); let macro_name = gen.generate_exports(None, funcs.iter().map(|f| f.1))?; let src = gen.finish(); self.src.push_str(&src); self.export_macros.push((macro_name, String::new())); if self.opts.stubs { - let mut gen = self.interface(Identifier::World(world), None, resolve, false); + let mut gen = self.interface(Identifier::World(world), "[export]$root", resolve, false); gen.generate_stub(None, funcs.iter().map(|f| f.1)); let stub = gen.finish(); self.src.push_str(&stub); @@ -1002,7 +1089,7 @@ impl WorldGenerator for RustWasm { types: &[(&str, TypeId)], _files: &mut Files, ) { - let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true); + let mut gen = self.interface(Identifier::World(world), "$root", resolve, true); for (name, ty) in types { gen.define_type(name, *ty); } @@ -1147,6 +1234,7 @@ fn compute_module_path(name: &WorldKey, resolve: &Resolve, is_export: bool) -> V } enum Identifier<'a> { + None, World(WorldId), Interface(InterfaceId, &'a WorldKey), } diff --git a/crates/rust/src/stream_and_future_support.rs b/crates/rust/src/stream_and_future_support.rs new file mode 100644 index 000000000..7a61bb5e1 --- /dev/null +++ b/crates/rust/src/stream_and_future_support.rs @@ -0,0 +1,513 @@ +use { + futures::{ + channel::oneshot, + future::{self, FutureExt}, + sink::Sink, + stream::Stream, + }, + std::{ + collections::hash_map::Entry, + convert::Infallible, + 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: Sized + 'static { + fn new() -> u32; + async fn write(future: u32, value: Self) -> bool; + async fn read(future: u32) -> Option; + fn close_writable(future: u32); + fn close_readable(future: u32); +} + +/// Represents the writable end of a Component Model `future`. +pub struct FutureWriter { + handle: u32, + _phantom: PhantomData, +} + +impl FutureWriter { + /// Write the specified value to this `future`. + pub async fn write(self, v: T) { + async_support::with_entry(self.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(self.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>> + } + 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(self.handle, v).map(drop)), + }, + }) + .await; + } +} + +impl Drop for FutureWriter { + 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 the readable end of a Component Model `future`. +pub struct FutureReader { + handle: u32, + _phantom: PhantomData, +} + +impl FutureReader { + #[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 IntoFuture for FutureReader { + type Output = Option; + type IntoFuture = Pin + 'static>>; + + /// 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 { + async_support::with_entry(self.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(self.handle).await }) + as Pin>>, + 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 Drop for FutureReader { + 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; + async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; + fn close_writable(future: u32); + fn close_readable(future: u32); +} + +/// Represents the writable end of a Component Model `stream`. +pub struct StreamWriter { + handle: u32, + future: Option + 'static>>>, + _phantom: PhantomData, +} + +impl Sink> for StreamWriter { + type Error = Infallible; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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) -> 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); + 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 { + Poll::Ready(()) + } + } + Handle::LocalReady(..) => Poll::Pending, + Handle::LocalClosed => 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; + 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; + } + } + })); + } + }, + }); + Ok(()) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } +} + +impl Drop for StreamWriter { + fn drop(&mut self) { + if self.future.is_some() { + todo!("gracefully handle `StreamWriter::drop` when a write is in progress by calling `stream.cancel-write`"); + } + + 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 the readable end of a Component Model `stream`. +pub struct StreamReader { + handle: u32, + future: Option>> + 'static>>>, + _phantom: PhantomData, +} + +impl StreamReader { + #[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 Stream for StreamReader { + type Item = Vec; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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; + Box::pin(async move { + let mut buffer = iter::repeat_with(MaybeUninit::uninit) + .take(ceiling(64 * 1024, mem::size_of::())) + .collect::>(); + + if let Some(count) = T::read(handle, &mut buffer).await { + buffer.truncate(count); + Some(unsafe { + mem::transmute::>, Vec>(buffer) + }) + } else { + None + } + }) as Pin>> + } + Handle::LocalOpen => { + let (tx, rx) = oneshot::channel(); + entry.insert(Handle::LocalWaiting(tx)); + Box::pin(rx.map(|v| v.ok().map(|v| *v.downcast().unwrap()))) + } + 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 Drop for StreamReader { + fn drop(&mut self) { + if self.future.is_some() { + todo!("gracefully handle `StreamReader::drop` when a read is in progress by calling `stream.cancel-read`"); + } + + 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() -> (FutureWriter, FutureReader) { + 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() -> (StreamWriter, StreamReader) { + 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 } +} diff --git a/crates/teavm-java/src/lib.rs b/crates/teavm-java/src/lib.rs index 1d0c90593..0a9689e90 100644 --- a/crates/teavm-java/src/lib.rs +++ b/crates/teavm-java/src/lib.rs @@ -501,6 +501,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut bindgen, + false, ); let src = bindgen.src; @@ -570,6 +571,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut bindgen, + false, ); assert!(!bindgen.needs_cleanup_list); @@ -623,7 +625,7 @@ impl InterfaceGenerator<'_> { (0..sig.results.len()).map(|i| format!("p{i}")).collect(), ); - abi::post_return(bindgen.gen.resolve, func, &mut bindgen); + abi::post_return(bindgen.gen.resolve, func, &mut bindgen, false); let src = bindgen.src; @@ -2028,6 +2030,21 @@ impl Bindgen for FunctionBindgen<'_, '_> { "Memory.free(org.teavm.interop.Address.fromInt({address}), ({length}) * {size}, {align});" ); } + + Instruction::Flush { amt } => { + results.extend(operands.iter().take(*amt).map(|v| v.clone())); + } + + Instruction::AsyncMalloc { .. } + | Instruction::AsyncPostCallInterface { .. } + | Instruction::AsyncCallReturn { .. } + | Instruction::FutureLower { .. } + | Instruction::FutureLift { .. } + | Instruction::StreamLower { .. } + | Instruction::StreamLift { .. } + | Instruction::ErrorContextLower { .. } + | Instruction::ErrorContextLift { .. } + | Instruction::AsyncCallWasm { .. } => todo!(), } } diff --git a/crates/test-helpers/src/lib.rs b/crates/test-helpers/src/lib.rs index 1129238bb..1af51bcc9 100644 --- a/crates/test-helpers/src/lib.rs +++ b/crates/test-helpers/src/lib.rs @@ -32,9 +32,10 @@ pub fn test_directory(suite_name: &str, gen_name: &str, wit_name: &str) -> PathB /// Helper function to execute a process during tests and print informative /// information if it fails. pub fn run_command(cmd: &mut Command) { + let command = format!("{cmd:?}"); let output = cmd .output() - .expect("failed to run executable; is it installed"); + .unwrap_or_else(|e| panic!("failed to run executable: {e}; command was `{command}`")); if output.status.success() { return; diff --git a/tests/runtime/flavorful/wasm.rs b/tests/runtime/flavorful/wasm.rs index 058eb2f74..82454cda9 100644 --- a/tests/runtime/flavorful/wasm.rs +++ b/tests/runtime/flavorful/wasm.rs @@ -44,7 +44,6 @@ impl Guest for Component { assert!(errno_result().is_err()); MyErrno::A.to_string(); - format!("{:?}", MyErrno::A); fn assert_error() {} assert_error::(); @@ -107,7 +106,6 @@ impl exports::test::flavorful::test::Guest for Component { fn errno_result() -> Result<(), MyErrno> { MyErrno::A.to_string(); - format!("{:?}", MyErrno::A); fn assert_error() {} assert_error::(); Err(MyErrno::B) diff --git a/tests/runtime/main.rs b/tests/runtime/main.rs index 0b3ae64f3..53bd38d7c 100644 --- a/tests/runtime/main.rs +++ b/tests/runtime/main.rs @@ -229,10 +229,10 @@ fn tests(name: &str, dir_name: &str) -> Result> { if compiler.ends_with("++") { cmd.arg("-Wno-deprecated"); } - println!("{:?}", cmd); + let command = format!("{cmd:?}"); let output = match cmd.output() { Ok(output) => output, - Err(e) => panic!("failed to spawn compiler: {}", e), + Err(e) => panic!("failed to spawn compiler: {e}; command was `{command}`"), }; if !output.status.success() { @@ -301,10 +301,10 @@ fn tests(name: &str, dir_name: &str) -> Result> { cmd.arg(&out_wasm); cmd.arg(format!("{snake}.go")); cmd.current_dir(&out_dir); - + let command = format!("{cmd:?}"); let output = match cmd.output() { Ok(output) => output, - Err(e) => panic!("failed to spawn compiler: {}", e), + Err(e) => panic!("failed to spawn compiler: {e}; command was `{command}`"), }; if !output.status.success() { @@ -571,10 +571,10 @@ fn tests(name: &str, dir_name: &str) -> Result> { .arg("--self-contained") .arg("-o") .arg(&out_wasm); - + let command = format!("{cmd:?}"); let output = match cmd.output() { Ok(output) => output, - Err(e) => panic!("failed to spawn compiler: {}", e), + Err(e) => panic!("failed to spawn compiler: {e}; command was `{command}`"), }; if !output.status.success() { @@ -735,9 +735,10 @@ fn tests(name: &str, dir_name: &str) -> Result> { .arg("/p:UseAppHost=false") .arg("-o") .arg(&out_wasm); + let command = format!("{cmd:?}"); let output = match cmd.output() { Ok(output) => output, - Err(e) => panic!("failed to spawn compiler: {}", e), + Err(e) => panic!("failed to spawn compiler: {e}; command was `{command}`"), }; if !output.status.success() { From 2f37f79a4d815192cbe8acf52421d16e934a3f57 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Tue, 19 Nov 2024 14:10:24 -0700 Subject: [PATCH 389/672] add `async: true` case to Rust `codegen_tests` This ensures that all the codegen test WIT files produce compile-able bindings with `async: true` (i.e. all imports lowered and all exports lifted using the async ABI). That revealed some issues involving resource methods and constructors, as well as missing stub support, which I've resolved. Signed-off-by: Joel Dice --- Cargo.lock | 3 +- crates/core/src/abi.rs | 2 +- crates/guest-rust/rt/src/async_support.rs | 3 ++ crates/rust/Cargo.toml | 1 + crates/rust/src/bindgen.rs | 35 +++++++++++++++-------- crates/rust/src/interface.rs | 33 +++++++++++++++++---- crates/rust/tests/codegen.rs | 14 +++++++++ 7 files changed, 72 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30cd3a2b0..ee17ad71a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -2540,6 +2540,7 @@ dependencies = [ "wasm-metadata", "wit-bindgen", "wit-bindgen-core", + "wit-bindgen-rt", "wit-component", ] diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 54f6a069e..9cf2f13f0 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -1030,7 +1030,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { // `self.return_pointer`) so we use that to read // the result of the function from memory. AbiVariant::GuestImport => { - assert!(sig.results.is_empty()); + assert!(sig.results.is_empty() || self.async_); self.return_pointer.take().unwrap() } diff --git a/crates/guest-rust/rt/src/async_support.rs b/crates/guest-rust/rt/src/async_support.rs index fba6ce2d6..f326f1cb6 100644 --- a/crates/guest-rust/rt/src/async_support.rs +++ b/crates/guest-rust/rt/src/async_support.rs @@ -1,4 +1,5 @@ #![deny(missing_docs)] +#![allow(static_mut_refs)] use { futures::{ @@ -21,6 +22,8 @@ use { }, }; +pub use futures; + type BoxFuture = Pin + 'static>>; /// Represents a task created by either a call to an async-lifted export or a diff --git a/crates/rust/Cargo.toml b/crates/rust/Cargo.toml index 09a70bed8..7c64aea6c 100644 --- a/crates/rust/Cargo.toml +++ b/crates/rust/Cargo.toml @@ -28,6 +28,7 @@ prettyplease = { workspace = true } [dev-dependencies] wit-bindgen = { path = '../guest-rust' } +wit-bindgen-rt = { path = '../guest-rust/rt' } test-helpers = { path = '../test-helpers' } # For use with the custom attributes test serde = { version = "1.0", features = ["derive"] } diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index f8e77a5d8..ecbc87a18 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -859,24 +859,31 @@ impl Bindgen for FunctionBindgen<'_, '_> { } else { self.let_results(func.results.len(), results); }; - match &func.kind { + let constructor_type = match &func.kind { FunctionKind::Freestanding => { self.push_str(&format!("T::{}", to_rust_ident(&func.name))); + None } FunctionKind::Method(_) | FunctionKind::Static(_) => { self.push_str(&format!("T::{}", to_rust_ident(func.item_name()))); + None } FunctionKind::Constructor(ty) => { - self.push_str(&format!( - "{}::new(T::new", - resolve.types[*ty] - .name - .as_deref() - .unwrap() - .to_upper_camel_case() - )); + let ty = resolve.types[*ty] + .name + .as_deref() + .unwrap() + .to_upper_camel_case(); + let call = if self.async_ { + let async_support = self.gen.path_to_async_support(); + format!("{async_support}::futures::FutureExt::map(T::new") + } else { + format!("{ty}::new(T::new",) + }; + self.push_str(&call); + Some(ty) } - } + }; self.push_str("("); for (i, operand) in operands.iter().enumerate() { if i > 0 { @@ -893,8 +900,12 @@ impl Bindgen for FunctionBindgen<'_, '_> { } } self.push_str(")"); - if let FunctionKind::Constructor(_) = &func.kind { - self.push_str(")"); + if let Some(ty) = constructor_type { + self.push_str(&if self.async_ { + format!(", {ty}::new)") + } else { + ")".into() + }); } self.push_str(";\n"); } diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index a96795e6f..31fa98d88 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -1169,7 +1169,7 @@ impl {stream_and_future_support}::StreamPayload for {name} {{ let resource_methods = funcs.remove(&Some(*id)).unwrap_or(Vec::new()); let trait_name = format!("{path}::Guest{camel}"); - self.generate_stub_impl(&trait_name, "", &resource_methods); + self.generate_stub_impl(&trait_name, "", &resource_methods, interface); } format!("{path}::Guest") } @@ -1180,7 +1180,7 @@ impl {stream_and_future_support}::StreamPayload for {name} {{ }; if !root_methods.is_empty() || !extra_trait_items.is_empty() { - self.generate_stub_impl(&guest_trait, &extra_trait_items, &root_methods); + self.generate_stub_impl(&guest_trait, &extra_trait_items, &root_methods, interface); } } @@ -1189,6 +1189,7 @@ impl {stream_and_future_support}::StreamPayload for {name} {{ trait_name: &str, extra_trait_items: &str, funcs: &[&Function], + interface: Option<(InterfaceId, &WorldKey)>, ) { uwriteln!(self.src, "impl {trait_name} for Stub {{"); self.src.push_str(extra_trait_items); @@ -1197,7 +1198,19 @@ impl {stream_and_future_support}::StreamPayload for {name} {{ if self.gen.skip.contains(&func.name) { continue; } + let async_ = match &self.gen.opts.async_ { + AsyncConfig::None => false, + AsyncConfig::All => true, + AsyncConfig::Some { exports, .. } => { + exports.contains(&if let Some((_, key)) = interface { + format!("{}#{}", self.resolve.name_world_key(key), func.name) + } else { + func.name.clone() + }) + } + }; let mut sig = FnSig { + async_, use_item_name: true, private: true, ..Default::default() @@ -1206,8 +1219,14 @@ impl {stream_and_future_support}::StreamPayload for {name} {{ sig.self_arg = Some("&self".into()); sig.self_is_first_param = true; } - self.print_signature(func, true, &sig, true); - self.src.push_str("{ unreachable!() }\n"); + self.print_signature(func, true, &sig, false); + let call = if async_ { + let async_support = self.path_to_async_support(); + format!("{{ #[allow(unreachable_code)]{async_support}::futures::future::ready(unreachable!()) }}\n") + } else { + "{ unreachable!() }\n".into() + }; + self.src.push_str(&call); } self.src.push_str("}\n"); @@ -1273,7 +1292,11 @@ impl {stream_and_future_support}::StreamPayload for {name} {{ ) -> Vec { let params = self.print_docs_and_params(func, params_owned, sig, use_async_sugar); if let FunctionKind::Constructor(_) = &func.kind { - self.push_str(" -> Self") + self.push_str(if sig.async_ && !use_async_sugar { + " -> impl ::core::future::Future" + } else { + " -> Self" + }) } else { self.print_results(&func.results, sig.async_ && !use_async_sugar); } diff --git a/crates/rust/tests/codegen.rs b/crates/rust/tests/codegen.rs index b5757b3df..b38ac189b 100644 --- a/crates/rust/tests/codegen.rs +++ b/crates/rust/tests/codegen.rs @@ -51,6 +51,20 @@ mod codegen_tests { #[test] fn works() {} } + + #[cfg(feature = "async")] + mod async_ { + wit_bindgen::generate!({ + path: $test, + stubs, + export_prefix: "[async]", + generate_all, + async: true + }); + + #[test] + fn works() {} + } } }; From 7c27b23e3caafd76cbf6bb7ac84db0318ec8e54c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 20 Nov 2024 00:33:16 +0100 Subject: [PATCH 390/672] tools update --- Cargo.lock | 112 ++++++++++++++++++++++++++--------------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3d22bf9d3..69ac55e44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -839,11 +839,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" dependencies = [ "foldhash", + "serde", ] [[package]] @@ -910,7 +911,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "serde", ] @@ -1093,7 +1094,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "crc32fast", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "indexmap", "memchr", ] @@ -1507,10 +1508,10 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.219.1", + "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-core", "wit-component", - "wit-parser 0.219.1", + "wit-parser 0.220.0", ] [[package]] @@ -1787,27 +1788,27 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.219.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#66b7df9bcb50647947446d8cce7c480b9768f775" +version = "0.220.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebf48234b389415b226a4daef6562933d38c7b28a8b8f64c5c4130dad1561ab7" dependencies = [ "leb128", - "wasmparser 0.219.1", + "wasmparser 0.220.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-encoder" version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebf48234b389415b226a4daef6562933d38c7b28a8b8f64c5c4130dad1561ab7" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#aac81f80af8a8b75aa7b2c9cc51992c4753a0e6f" dependencies = [ "leb128", - "wasmparser 0.220.0", + "wasmparser 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] name = "wasm-metadata" -version = "0.219.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#66b7df9bcb50647947446d8cce7c480b9768f775" +version = "0.220.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#aac81f80af8a8b75aa7b2c9cc51992c4753a0e6f" dependencies = [ "anyhow", "indexmap", @@ -1815,8 +1816,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.219.1", - "wasmparser 0.219.1", + "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -1835,25 +1836,24 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.219.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#66b7df9bcb50647947446d8cce7c480b9768f775" +version = "0.220.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e246c2772ce3ebc83f89a2d4487ac5794cad6c309b2071818a88c7db7c36d87b" dependencies = [ - "ahash", "bitflags", - "hashbrown 0.14.5", "indexmap", - "semver", - "serde", ] [[package]] name = "wasmparser" version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e246c2772ce3ebc83f89a2d4487ac5794cad6c309b2071818a88c7db7c36d87b" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#aac81f80af8a8b75aa7b2c9cc51992c4753a0e6f" dependencies = [ "bitflags", + "hashbrown 0.15.1", "indexmap", + "semver", + "serde", ] [[package]] @@ -1919,7 +1919,7 @@ dependencies = [ "wasmtime-slab", "wasmtime-versioned-export-macros", "wasmtime-winch", - "wat 1.220.0", + "wat 1.220.0 (registry+https://github.com/rust-lang/crates.io-index)", "windows-sys 0.52.0", ] @@ -2166,44 +2166,44 @@ dependencies = [ [[package]] name = "wast" -version = "219.0.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#66b7df9bcb50647947446d8cce7c480b9768f775" +version = "220.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e708c8de08751fd66e70961a32bae9d71901f14a70871e181cb8461a3bb3165" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.219.1", + "wasm-encoder 0.220.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wast" version = "220.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e708c8de08751fd66e70961a32bae9d71901f14a70871e181cb8461a3bb3165" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#aac81f80af8a8b75aa7b2c9cc51992c4753a0e6f" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.220.0", + "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] name = "wat" -version = "1.219.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#66b7df9bcb50647947446d8cce7c480b9768f775" +version = "1.220.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de4f1d7d59614ba690541360102b995c4eb1b9ed373701d5102cc1a968b1c5a3" dependencies = [ - "wast 219.0.1", + "wast 220.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wat" version = "1.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de4f1d7d59614ba690541360102b995c4eb1b9ed373701d5102cc1a968b1c5a3" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#aac81f80af8a8b75aa7b2c9cc51992c4753a0e6f" dependencies = [ - "wast 220.0.0", + "wast 220.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2433,11 +2433,11 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.219.1", + "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wit-bindgen-core", "wit-component", - "wit-parser 0.219.1", + "wit-parser 0.220.0", ] [[package]] @@ -2448,8 +2448,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.219.1", - "wasmparser 0.219.1", + "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasmtime", "wasmtime-wasi", "wit-bindgen-bridge", @@ -2463,7 +2463,7 @@ dependencies = [ "wit-bindgen-rust", "wit-bindgen-teavm-java", "wit-component", - "wit-parser 0.219.1", + "wit-parser 0.220.0", ] [[package]] @@ -2472,7 +2472,7 @@ version = "0.35.0" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser 0.219.1", + "wit-parser 0.220.0", ] [[package]] @@ -2483,7 +2483,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.219.1", + "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", @@ -2499,12 +2499,12 @@ dependencies = [ "heck 0.5.0", "indexmap", "test-helpers", - "wasm-encoder 0.219.1", + "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", - "wasmparser 0.219.1", + "wasmparser 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-core", "wit-component", - "wit-parser 0.219.1", + "wit-parser 0.220.0", ] [[package]] @@ -2598,8 +2598,8 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.219.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#66b7df9bcb50647947446d8cce7c480b9768f775" +version = "0.220.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#aac81f80af8a8b75aa7b2c9cc51992c4753a0e6f" dependencies = [ "anyhow", "bitflags", @@ -2608,11 +2608,11 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.219.1", + "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", - "wasmparser 0.219.1", - "wat 1.219.1", - "wit-parser 0.219.1", + "wasmparser 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wat 1.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.220.0", ] [[package]] @@ -2635,8 +2635,8 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.219.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#66b7df9bcb50647947446d8cce7c480b9768f775" +version = "0.220.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#aac81f80af8a8b75aa7b2c9cc51992c4753a0e6f" dependencies = [ "anyhow", "id-arena", @@ -2647,7 +2647,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.219.1", + "wasmparser 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] From 456e995c3036955d6d0f919275828252ddcdada2 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Fri, 22 Mar 2024 17:33:31 -0600 Subject: [PATCH 391/672] Add support for async/streams/futures to Rust generator This adds support for generating bindings which use the [Async ABI](https://github.com/WebAssembly/component-model/blob/main/design/mvp/Async.md) along with the [`stream`, `future`, and `error-context`](https://github.com/WebAssembly/component-model/pull/405) types. By default, normal synchronous bindings are generated, but the user may opt-in to async bindings for all or some of the imported and/or exported functions in the target world and interfaces -- provided the default-enabled `async` feature is enabled. In addition, we generate `StreamPayload` and/or `FuturePayload` trait implementations for any types appearing as the `T` in `stream` or `future` in the WIT files, respectively. That enables user code to call `new_stream` or `new_future` to create `stream`s or `future`s with those payload types, then write to them, read from them, and/or pass the readable end as a parameter to a component import or return value of a component export. Note that I've added new `core::abi::Instruction` enum variants to handle async lifting and lowering, but they're currently tailored to the Rust generator and will probably change somewhat as we add support for other languages. This does not include any new tests; I'll add those in a follow-up commit. Signed-off-by: Joel Dice add `async: true` case to Rust `codegen_tests` This ensures that all the codegen test WIT files produce compile-able bindings with `async: true` (i.e. all imports lowered and all exports lifted using the async ABI). That revealed some issues involving resource methods and constructors, as well as missing stub support, which I've resolved. Signed-off-by: Joel Dice add codegen tests for futures, streams, and error-contexts Signed-off-by: Joel Dice --- Cargo.lock | 138 +++- Cargo.toml | 13 +- crates/c/src/lib.rs | 57 +- crates/c/tests/codegen.rs | 8 + crates/core/Cargo.toml | 4 + crates/core/src/abi.rs | 424 ++++++++--- crates/core/src/lib.rs | 34 +- crates/core/src/types.rs | 8 +- crates/csharp/src/lib.rs | 36 +- crates/csharp/tests/codegen.rs | 8 + crates/go/src/bindgen.rs | 2 + crates/go/src/interface.rs | 20 +- crates/go/tests/codegen.rs | 9 + crates/guest-rust/Cargo.toml | 3 +- crates/guest-rust/macro/Cargo.toml | 3 + crates/guest-rust/macro/src/lib.rs | 85 ++- crates/guest-rust/rt/Cargo.toml | 6 + crates/guest-rust/rt/src/async_support.rs | 513 +++++++++++++ crates/guest-rust/rt/src/lib.rs | 10 +- crates/markdown/src/lib.rs | 45 +- crates/moonbit/src/lib.rs | 34 +- crates/moonbit/tests/codegen.rs | 8 + crates/rust/Cargo.toml | 6 + crates/rust/src/bindgen.rs | 202 ++++- crates/rust/src/interface.rs | 734 +++++++++++++++++-- crates/rust/src/lib.rs | 108 ++- crates/rust/src/stream_and_future_support.rs | 546 ++++++++++++++ crates/rust/tests/codegen.rs | 13 + crates/rust/tests/codegen_no_std.rs | 10 + crates/teavm-java/src/lib.rs | 34 +- crates/teavm-java/tests/codegen.rs | 8 + crates/test-helpers/src/lib.rs | 3 +- tests/codegen/error-context.wit | 12 + tests/codegen/futures.wit | 87 +++ tests/codegen/resources-with-futures.wit | 17 + tests/codegen/resources-with-streams.wit | 17 + tests/codegen/streams.wit | 85 +++ tests/runtime/flavorful/wasm.rs | 2 - tests/runtime/main.rs | 15 +- 39 files changed, 3064 insertions(+), 303 deletions(-) create mode 100644 crates/guest-rust/rt/src/async_support.rs create mode 100644 crates/rust/src/stream_and_future_support.rs create mode 100644 tests/codegen/error-context.wit create mode 100644 tests/codegen/futures.wit create mode 100644 tests/codegen/resources-with-futures.wit create mode 100644 tests/codegen/resources-with-streams.wit create mode 100644 tests/codegen/streams.wit diff --git a/Cargo.lock b/Cargo.lock index 1b7bf4616..16950a7ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,6 +125,12 @@ dependencies = [ "syn", ] +[[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" @@ -677,6 +683,7 @@ checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", + "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -699,12 +706,34 @@ 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" @@ -723,11 +752,16 @@ 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]] @@ -802,11 +836,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" dependencies = [ "foldhash", + "serde", ] [[package]] @@ -873,7 +908,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "serde", ] @@ -1056,7 +1091,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "crc32fast", - "hashbrown 0.15.0", + "hashbrown 0.15.1", "indexmap", "memchr", ] @@ -1356,6 +1391,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + [[package]] name = "slice-group-by" version = "0.3.1" @@ -1459,7 +1503,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.220.0", + "wasm-encoder 0.220.0 (git+https://github.com/dicej/wasm-tools?branch=async)", "wit-bindgen-core", "wit-component", "wit-parser 0.220.0", @@ -1742,14 +1786,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebf48234b389415b226a4daef6562933d38c7b28a8b8f64c5c4130dad1561ab7" dependencies = [ "leb128", - "wasmparser 0.220.0", + "wasmparser 0.220.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-encoder" +version = "0.220.0" +source = "git+https://github.com/dicej/wasm-tools?branch=async#5f4ad172759da4d53d2efbc45cdcf2e0a4beb545" +dependencies = [ + "leb128", + "wasmparser 0.220.0 (git+https://github.com/dicej/wasm-tools?branch=async)", ] [[package]] name = "wasm-metadata" version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f3e5f5920c5abfc45573c89b07b38efdaae1515ef86f83dad12d60e50ecd62b" +source = "git+https://github.com/dicej/wasm-tools?branch=async#5f4ad172759da4d53d2efbc45cdcf2e0a4beb545" dependencies = [ "anyhow", "indexmap", @@ -1757,8 +1809,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.220.0", - "wasmparser 0.220.0", + "wasm-encoder 0.220.0 (git+https://github.com/dicej/wasm-tools?branch=async)", + "wasmparser 0.220.0 (git+https://github.com/dicej/wasm-tools?branch=async)", ] [[package]] @@ -1781,9 +1833,17 @@ version = "0.220.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e246c2772ce3ebc83f89a2d4487ac5794cad6c309b2071818a88c7db7c36d87b" dependencies = [ - "ahash", "bitflags", - "hashbrown 0.14.5", + "indexmap", +] + +[[package]] +name = "wasmparser" +version = "0.220.0" +source = "git+https://github.com/dicej/wasm-tools?branch=async#5f4ad172759da4d53d2efbc45cdcf2e0a4beb545" +dependencies = [ + "bitflags", + "hashbrown 0.15.1", "indexmap", "semver", "serde", @@ -1852,7 +1912,7 @@ dependencies = [ "wasmtime-slab", "wasmtime-versioned-export-macros", "wasmtime-winch", - "wat", + "wat 1.220.0 (registry+https://github.com/rust-lang/crates.io-index)", "windows-sys 0.52.0", ] @@ -2107,7 +2167,19 @@ dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.220.0", + "wasm-encoder 0.220.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wast" +version = "220.0.0" +source = "git+https://github.com/dicej/wasm-tools?branch=async#5f4ad172759da4d53d2efbc45cdcf2e0a4beb545" +dependencies = [ + "bumpalo", + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.220.0 (git+https://github.com/dicej/wasm-tools?branch=async)", ] [[package]] @@ -2116,7 +2188,15 @@ version = "1.220.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de4f1d7d59614ba690541360102b995c4eb1b9ed373701d5102cc1a968b1c5a3" dependencies = [ - "wast 220.0.0", + "wast 220.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wat" +version = "1.220.0" +source = "git+https://github.com/dicej/wasm-tools?branch=async#5f4ad172759da4d53d2efbc45cdcf2e0a4beb545" +dependencies = [ + "wast 220.0.0 (git+https://github.com/dicej/wasm-tools?branch=async)", ] [[package]] @@ -2335,7 +2415,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.220.0", + "wasm-encoder 0.220.0 (git+https://github.com/dicej/wasm-tools?branch=async)", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -2350,8 +2430,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.220.0", - "wasmparser 0.220.0", + "wasm-encoder 0.220.0 (git+https://github.com/dicej/wasm-tools?branch=async)", + "wasmparser 0.220.0 (git+https://github.com/dicej/wasm-tools?branch=async)", "wasmtime", "wasmtime-wasi", "wit-bindgen-c", @@ -2384,9 +2464,9 @@ dependencies = [ "heck 0.5.0", "indexmap", "test-helpers", - "wasm-encoder 0.220.0", + "wasm-encoder 0.220.0 (git+https://github.com/dicej/wasm-tools?branch=async)", "wasm-metadata", - "wasmparser 0.220.0", + "wasmparser 0.220.0 (git+https://github.com/dicej/wasm-tools?branch=async)", "wit-bindgen-core", "wit-component", "wit-parser 0.220.0", @@ -2434,6 +2514,8 @@ name = "wit-bindgen-rt" version = "0.35.0" dependencies = [ "bitflags", + "futures", + "once_cell", ] [[package]] @@ -2442,6 +2524,7 @@ version = "0.35.0" dependencies = [ "anyhow", "clap", + "futures", "heck 0.5.0", "indexmap", "prettyplease", @@ -2452,6 +2535,7 @@ dependencies = [ "wasm-metadata", "wit-bindgen", "wit-bindgen-core", + "wit-bindgen-rt", "wit-component", ] @@ -2484,8 +2568,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ccedf54cc65f287da268d64d2bf4f7530d2cfb2296ffbe3ad5f65567e4cf53" +source = "git+https://github.com/dicej/wasm-tools?branch=async#5f4ad172759da4d53d2efbc45cdcf2e0a4beb545" dependencies = [ "anyhow", "bitflags", @@ -2494,10 +2577,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.220.0", + "wasm-encoder 0.220.0 (git+https://github.com/dicej/wasm-tools?branch=async)", "wasm-metadata", - "wasmparser 0.220.0", - "wat", + "wasmparser 0.220.0 (git+https://github.com/dicej/wasm-tools?branch=async)", + "wat 1.220.0 (git+https://github.com/dicej/wasm-tools?branch=async)", "wit-parser 0.220.0", ] @@ -2522,8 +2605,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.220.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b7117ce3adc0b4354b46dc1cf3190b00b333e65243d244c613ffcc58bdec84d" +source = "git+https://github.com/dicej/wasm-tools?branch=async#5f4ad172759da4d53d2efbc45cdcf2e0a4beb545" dependencies = [ "anyhow", "id-arena", @@ -2534,7 +2616,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.220.0", + "wasmparser 0.220.0 (git+https://github.com/dicej/wasm-tools?branch=async)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index b8cbf23b6..8bcd97568 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,12 +31,13 @@ clap = { version = "4.3.19", features = ["derive"] } indexmap = "2.0.0" prettyplease = "0.2.20" syn = { version = "2.0", features = ["printing"] } +futures = "0.3.31" -wasmparser = "0.220.0" -wasm-encoder = "0.220.0" -wasm-metadata = "0.220.0" -wit-parser = "0.220.0" -wit-component = "0.220.0" +wasmparser = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wasm-encoder = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wasm-metadata = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wit-parser = { git = "https://github.com/dicej/wasm-tools", branch = "async" } +wit-component = { git = "https://github.com/dicej/wasm-tools", branch = "async" } wit-bindgen-core = { path = 'crates/core', version = '0.35.0' } wit-bindgen-c = { path = 'crates/c', version = '0.35.0' } @@ -74,6 +75,7 @@ default = [ 'go', 'csharp', 'moonbit', + 'async', ] c = ['dep:wit-bindgen-c'] rust = ['dep:wit-bindgen-rust'] @@ -83,6 +85,7 @@ go = ['dep:wit-bindgen-go'] csharp = ['dep:wit-bindgen-csharp'] csharp-mono = ['csharp'] moonbit = ['dep:wit-bindgen-moonbit'] +async = ["wit-bindgen-rust/async"] [dev-dependencies] heck = { workspace = true } diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 99eb5c689..fd7a5cc81 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -718,6 +718,7 @@ fn is_prim_type_id(resolve: &Resolve, id: TypeId) -> bool { | TypeDefKind::Result(_) | TypeDefKind::Future(_) | TypeDefKind::Stream(_) + | TypeDefKind::ErrorContext | TypeDefKind::Unknown => false, } } @@ -779,8 +780,9 @@ pub fn push_ty_name(resolve: &Resolve, ty: &Type, src: &mut String) { src.push_str("list_"); push_ty_name(resolve, ty, src); } - TypeDefKind::Future(_) => unimplemented!(), - TypeDefKind::Stream(_) => unimplemented!(), + TypeDefKind::Future(_) => todo!(), + TypeDefKind::Stream(_) => todo!(), + TypeDefKind::ErrorContext => todo!(), TypeDefKind::Handle(Handle::Own(resource)) => { src.push_str("own_"); push_ty_name(resolve, &Type::Id(*resource), src); @@ -992,6 +994,7 @@ impl Return { TypeDefKind::Future(_) => todo!("return_single for future"), TypeDefKind::Stream(_) => todo!("return_single for stream"), + TypeDefKind::ErrorContext => todo!("return_single for error-context"), TypeDefKind::Resource => todo!("return_single for resource"), TypeDefKind::Unknown => unreachable!(), } @@ -1339,6 +1342,21 @@ void __wasm_export_{ns}_{snake}_dtor({ns}_{snake}_t* arg) {{ self.finish_typedef_struct(id); } + fn type_future(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs) { + _ = (id, name, ty, docs); + todo!() + } + + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { + _ = (id, name, ty, docs); + todo!() + } + + fn type_error_context(&mut self, id: TypeId, name: &str, docs: &Docs) { + _ = (id, name, docs); + todo!() + } + fn type_builtin(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { let _ = (id, name, ty, docs); } @@ -1427,12 +1445,16 @@ impl<'a> wit_bindgen_core::AnonymousTypeGenerator<'a> for InterfaceGenerator<'a> todo!("print_anonymous_type for future"); } - fn anonymous_type_stream(&mut self, _id: TypeId, _ty: &Stream, _docs: &Docs) { + fn anonymous_type_stream(&mut self, _id: TypeId, _ty: &Type, _docs: &Docs) { todo!("print_anonymous_type for stream"); } - fn anonymous_typ_type(&mut self, _id: TypeId, _ty: &Type, _docs: &Docs) { - todo!("print_anonymous_type for typ"); + fn anonymous_type_error_context(&mut self) { + todo!("print_anonymous_type for error-context"); + } + + fn anonymous_type_type(&mut self, _id: TypeId, _ty: &Type, _docs: &Docs) { + todo!("print_anonymous_type for type"); } } @@ -1605,6 +1627,7 @@ impl InterfaceGenerator<'_> { } TypeDefKind::Future(_) => todo!("print_dtor for future"), TypeDefKind::Stream(_) => todo!("print_dtor for stream"), + TypeDefKind::ErrorContext => todo!("print_dtor for error-context"), TypeDefKind::Resource => {} TypeDefKind::Handle(Handle::Borrow(id) | Handle::Own(id)) => { self.free(&Type::Id(*id), "*ptr"); @@ -1750,6 +1773,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut f, + false, ); let FunctionBindgen { @@ -1822,6 +1846,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut f, + false, ); let FunctionBindgen { src, .. } = f; self.src.c_adapters(&src); @@ -1852,7 +1877,7 @@ impl InterfaceGenerator<'_> { let mut f = FunctionBindgen::new(self, c_sig, &import_name); f.params = params; - abi::post_return(f.gen.resolve, func, &mut f); + abi::post_return(f.gen.resolve, func, &mut f, false); let FunctionBindgen { src, .. } = f; self.src.c_fns(&src); self.src.c_fns("}\n"); @@ -2075,17 +2100,8 @@ impl InterfaceGenerator<'_> { TypeDefKind::List(ty) => self.contains_droppable_borrow(ty), - TypeDefKind::Future(r) => r - .as_ref() - .map_or(false, |ty| self.contains_droppable_borrow(ty)), - - TypeDefKind::Stream(s) => { - s.element - .as_ref() - .map_or(false, |ty| self.contains_droppable_borrow(ty)) - || s.end - .as_ref() - .map_or(false, |ty| self.contains_droppable_borrow(ty)) + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext => { + false } TypeDefKind::Type(ty) => self.contains_droppable_borrow(ty), @@ -2753,7 +2769,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.src.push_str(");\n"); } - Instruction::CallInterface { func } => { + Instruction::CallInterface { func, .. } => { let mut args = String::new(); for (i, (op, (byref, _))) in operands.iter().zip(&self.sig.params).enumerate() { if i > 0 { @@ -3037,6 +3053,10 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!(self.src, "}}"); } + Instruction::Flush { amt } => { + results.extend(operands.iter().take(*amt).map(|v| v.clone())); + } + i => unimplemented!("{:?}", i), } } @@ -3145,6 +3165,7 @@ pub fn is_arg_by_pointer(resolve: &Resolve, ty: &Type) -> bool { TypeDefKind::Tuple(_) | TypeDefKind::Record(_) | TypeDefKind::List(_) => true, TypeDefKind::Future(_) => todo!("is_arg_by_pointer for future"), TypeDefKind::Stream(_) => todo!("is_arg_by_pointer for stream"), + TypeDefKind::ErrorContext => todo!("is_arg_by_pointer for error-context"), TypeDefKind::Resource => todo!("is_arg_by_pointer for resource"), TypeDefKind::Unknown => unreachable!(), }, diff --git a/crates/c/tests/codegen.rs b/crates/c/tests/codegen.rs index 7546cd846..359217cb1 100644 --- a/crates/c/tests/codegen.rs +++ b/crates/c/tests/codegen.rs @@ -6,6 +6,14 @@ use std::process::Command; use wit_parser::{Resolve, UnresolvedPackageGroup}; macro_rules! codegen_test { + // TODO: implement support for stream, future, and error-context, and then + // remove these lines: + (streams $name:tt $test:tt) => {}; + (futures $name:tt $test:tt) => {}; + (resources_with_streams $name:tt $test:tt) => {}; + (resources_with_futures $name:tt $test:tt) => {}; + (error_context $name:tt $test:tt) => {}; + ($id:ident $name:tt $test:tt) => { #[test] fn $id() { diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 60935ef8d..bc476ea80 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -18,3 +18,7 @@ doctest = false wit-parser = { workspace = true } anyhow = { workspace = true } heck = { workspace = true } + +[features] +default = ["async"] +async = [] diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index f0175afaa..33851a0e5 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -1,7 +1,7 @@ pub use wit_parser::abi::{AbiVariant, WasmSignature, WasmType}; use wit_parser::{ - Enum, Flags, FlagsRepr, Function, Handle, Int, Record, Resolve, Result_, Results, SizeAlign, - Tuple, Type, TypeDefKind, TypeId, Variant, + ElementInfo, Enum, Flags, FlagsRepr, Function, Handle, Int, Record, Resolve, Result_, Results, + SizeAlign, Tuple, Type, TypeDefKind, TypeId, Variant, }; // Helper macro for defining instructions without having to have tons of @@ -350,6 +350,40 @@ def_instruction! { ty: TypeId, } : [1] => [1], + /// Create an `i32` from a future. + FutureLower { + payload: &'a Option, + ty: TypeId, + } : [1] => [1], + + /// Create a future from an `i32`. + FutureLift { + payload: &'a Option, + ty: TypeId, + } : [1] => [1], + + /// Create an `i32` from a stream. + StreamLower { + payload: &'a Type, + ty: TypeId, + } : [1] => [1], + + /// Create a stream from an `i32`. + StreamLift { + payload: &'a Type, + ty: TypeId, + } : [1] => [1], + + /// Create an `i32` from an error-context. + ErrorContextLower { + ty: TypeId, + } : [1] => [1], + + /// Create a error-context from an `i32`. + ErrorContextLift { + ty: TypeId, + } : [1] => [1], + /// Pops a tuple value off the stack, decomposes the tuple to all of /// its fields, and then pushes the fields onto the stack. TupleLower { @@ -470,7 +504,8 @@ def_instruction! { /// Note that this will be used for async functions. CallInterface { func: &'a Function, - } : [func.params.len()] => [func.results.len()], + async_: bool, + } : [func.params.len()] => [if *async_ { 1 } else { func.results.len() }], /// Returns `amt` values on the stack. This is always the last /// instruction. @@ -519,6 +554,39 @@ def_instruction! { GuestDeallocateVariant { blocks: usize, } : [1] => [0], + + /// Allocate the parameter and/or return areas to use for an + /// async-lowered import call. + /// + /// This cannot be allocated on the (shadow-)stack since it needs to + /// remain valid until the callee has finished using the buffers, which + /// may be after we pop the current stack frame. + AsyncMalloc { size: usize, align: usize } : [0] => [1], + + /// Call an async-lowered import. + /// + /// `size` and `align` are used to deallocate the parameter area + /// allocated using `AsyncMalloc` after the callee task returns a value. + AsyncCallWasm { name: &'a str, size: usize, align: usize } : [2] => [0], + + /// Generate code to run after `CallInterface` for an async-lifted export. + /// + /// For example, this might include task management for the + /// future/promise/task returned by the call made for `CallInterface`. + AsyncPostCallInterface { func: &'a Function } : [1] => [func.results.len() + 1], + + /// Call `task.return` for an async-lifted export once the task returned + /// by `CallInterface` and managed by `AsyncPostCallInterface` + /// yields a value. + AsyncCallReturn { name: &'a str, params: &'a [WasmType] } : [params.len()] => [0], + + /// Force the evaluation of the specified number of expressions and push + /// the results to the stack. + /// + /// This is useful prior to disposing of temporary variables and/or + /// allocations which are referenced by one or more not-yet-evaluated + /// expressions. + Flush { amt: usize } : [*amt] => [*amt], } } @@ -683,8 +751,50 @@ pub fn call( lift_lower: LiftLower, func: &Function, bindgen: &mut impl Bindgen, + async_: bool, ) { - Generator::new(resolve, variant, lift_lower, bindgen).call(func); + if async_ && !cfg!(feature = "async") { + panic!("must enable `async` feature to lift or lower using the async ABI"); + } + + Generator::new(resolve, variant, lift_lower, bindgen, async_).call(func); +} + +pub fn lower_to_memory( + resolve: &Resolve, + bindgen: &mut B, + address: B::Operand, + value: B::Operand, + ty: &Type, +) { + // TODO: refactor so we don't need to pass in a bunch of unused dummy parameters: + let mut generator = Generator::new( + resolve, + AbiVariant::GuestImport, + LiftLower::LowerArgsLiftResults, + bindgen, + true, + ); + generator.stack.push(value); + generator.write_to_memory(ty, address, 0); +} + +pub fn lift_from_memory( + resolve: &Resolve, + bindgen: &mut B, + address: B::Operand, + ty: &Type, +) -> B::Operand { + // TODO: refactor so we don't need to pass in a bunch of unused dummy parameters: + let mut generator = Generator::new( + resolve, + AbiVariant::GuestImport, + LiftLower::LowerArgsLiftResults, + bindgen, + true, + ); + generator.read_from_memory(ty, address, 0); + generator.stack.pop().unwrap() } /// Used in a similar manner as the `Interface::call` function except is @@ -693,12 +803,13 @@ pub fn call( /// This is only intended to be used in guest generators for exported /// functions and will primarily generate `GuestDeallocate*` instructions, /// plus others used as input to those instructions. -pub fn post_return(resolve: &Resolve, func: &Function, bindgen: &mut impl Bindgen) { +pub fn post_return(resolve: &Resolve, func: &Function, bindgen: &mut impl Bindgen, async_: bool) { Generator::new( resolve, AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, bindgen, + async_, ) .post_return(func); } @@ -734,7 +845,7 @@ fn needs_post_return(resolve: &Resolve, ty: &Type) -> bool { .filter_map(|t| t.as_ref()) .any(|t| needs_post_return(resolve, t)), TypeDefKind::Flags(_) | TypeDefKind::Enum(_) => false, - TypeDefKind::Future(_) | TypeDefKind::Stream(_) => unimplemented!(), + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext => false, TypeDefKind::Unknown => unreachable!(), }, @@ -757,6 +868,7 @@ struct Generator<'a, B: Bindgen> { variant: AbiVariant, lift_lower: LiftLower, bindgen: &'a mut B, + async_: bool, resolve: &'a Resolve, operands: Vec, results: Vec, @@ -770,12 +882,14 @@ impl<'a, B: Bindgen> Generator<'a, B> { variant: AbiVariant, lift_lower: LiftLower, bindgen: &'a mut B, + async_: bool, ) -> Generator<'a, B> { Generator { resolve, variant, lift_lower, bindgen, + async_, operands: Vec::new(), results: Vec::new(), stack: Vec::new(), @@ -784,74 +898,122 @@ 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); match self.lift_lower { LiftLower::LowerArgsLiftResults => { - if !sig.indirect_params { - // If the parameters for this function aren't indirect - // (there aren't too many) then we simply do a normal lower - // operation for them all. + if let (AbiVariant::GuestExport, true) = (self.variant, self.async_) { + unimplemented!("host-side code generation for async lift/lower not supported"); + } + + let lower_to_memory = |self_: &mut Self, ptr: B::Operand| { + let mut offset = 0usize; for (nth, (_, ty)) in func.params.iter().enumerate() { - self.emit(&Instruction::GetArg { nth }); - self.lower(ty); + self_.emit(&Instruction::GetArg { nth }); + offset = align_to(offset, self_.bindgen.sizes().align(ty).align_wasm32()); + self_.write_to_memory(ty, ptr.clone(), offset as i32); + offset += self_.bindgen.sizes().size(ty).size_wasm32(); } - } else { - // ... otherwise if parameters are indirect space is - // allocated from them and each argument is lowered - // individually into memory. - let info = self + + self_.stack.push(ptr); + }; + + let params_size_align = if self.async_ { + let ElementInfo { size, align } = self .bindgen .sizes() - .record(func.params.iter().map(|t| &t.1)); - let ptr = match self.variant { - // When a wasm module calls an import it will provide - // space that isn't explicitly deallocated. - AbiVariant::GuestImport => self - .bindgen - .return_pointer(info.size.size_wasm32(), info.align.align_wasm32()), - // When calling a wasm module from the outside, though, - // malloc needs to be called. - AbiVariant::GuestExport => { - self.emit(&Instruction::Malloc { - realloc: "cabi_realloc", - size: info.size.size_wasm32(), - align: info.align.align_wasm32(), - }); - self.stack.pop().unwrap() + .record(func.params.iter().map(|(_, ty)| ty)); + self.emit(&Instruction::AsyncMalloc { + size: size.size_wasm32(), + align: align.align_wasm32(), + }); + let ptr = self.stack.pop().unwrap(); + lower_to_memory(self, ptr); + Some((size, align)) + } else { + if !sig.indirect_params { + // If the parameters for this function aren't indirect + // (there aren't too many) then we simply do a normal lower + // operation for them all. + for (nth, (_, ty)) in func.params.iter().enumerate() { + self.emit(&Instruction::GetArg { nth }); + self.lower(ty); } - }; - let mut offset = 0usize; - for (nth, (_, ty)) in func.params.iter().enumerate() { - self.emit(&Instruction::GetArg { nth }); - offset = align_to(offset, self.bindgen.sizes().align(ty).align_wasm32()); - self.write_to_memory(ty, ptr.clone(), offset as i32); - offset += self.bindgen.sizes().size(ty).size_wasm32(); + } else { + // ... otherwise if parameters are indirect space is + // allocated from them and each argument is lowered + // individually into memory. + let info = self + .bindgen + .sizes() + .record(func.params.iter().map(|t| &t.1)); + let ptr = match self.variant { + // When a wasm module calls an import it will provide + // space that isn't explicitly deallocated. + AbiVariant::GuestImport => self + .bindgen + .return_pointer(info.size.size_wasm32(), info.align.align_wasm32()), + // When calling a wasm module from the outside, though, + // malloc needs to be called. + AbiVariant::GuestExport => { + self.emit(&Instruction::Malloc { + realloc: "cabi_realloc", + size: info.size.size_wasm32(), + align: info.align.align_wasm32(), + }); + self.stack.pop().unwrap() + } + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } + }; + lower_to_memory(self, ptr); } - - self.stack.push(ptr); - } + None + }; // If necessary we may need to prepare a return pointer for // this ABI. - if self.variant == AbiVariant::GuestImport && sig.retptr { - let info = self.bindgen.sizes().params(func.results.iter_types()); - let ptr = self - .bindgen - .return_pointer(info.size.size_wasm32(), info.align.align_wasm32()); - self.return_pointer = Some(ptr.clone()); - self.stack.push(ptr); - } + let dealloc_size_align = + if let Some((params_size, params_align)) = params_size_align { + let ElementInfo { size, align } = + self.bindgen.sizes().record(func.results.iter_types()); + self.emit(&Instruction::AsyncMalloc { + size: size.size_wasm32(), + align: align.align_wasm32(), + }); + let ptr = self.stack.pop().unwrap(); + self.return_pointer = Some(ptr.clone()); + self.stack.push(ptr); + + assert_eq!(self.stack.len(), 2); + self.emit(&Instruction::AsyncCallWasm { + name: &format!("[async]{}", func.name), + size: params_size.size_wasm32(), + align: params_align.align_wasm32(), + }); + Some((size, align)) + } else { + if self.variant == AbiVariant::GuestImport && sig.retptr { + let info = self.bindgen.sizes().params(func.results.iter_types()); + let ptr = self + .bindgen + .return_pointer(info.size.size_wasm32(), info.align.align_wasm32()); + self.return_pointer = Some(ptr.clone()); + self.stack.push(ptr); + } - // Now that all the wasm args are prepared we can call the - // actual wasm function. - assert_eq!(self.stack.len(), sig.params.len()); - self.emit(&Instruction::CallWasm { - name: &func.name, - sig: &sig, - }); + assert_eq!(self.stack.len(), sig.params.len()); + self.emit(&Instruction::CallWasm { + name: &func.name, + sig: &sig, + }); + None + }; - if !sig.retptr { + 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. @@ -862,11 +1024,11 @@ impl<'a, B: Bindgen> Generator<'a, B> { let ptr = match self.variant { // imports into guests means it's a wasm module // calling an imported function. We supplied the - // return poitner as the last argument (saved in + // return pointer as the last argument (saved in // `self.return_pointer`) so we use that to read // the result of the function from memory. AbiVariant::GuestImport => { - assert!(sig.results.is_empty()); + assert!(sig.results.is_empty() || self.async_); self.return_pointer.take().unwrap() } @@ -874,9 +1036,24 @@ impl<'a, B: Bindgen> Generator<'a, B> { // calling wasm so wasm returned a pointer to where // the result is stored AbiVariant::GuestExport => self.stack.pop().unwrap(), + + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } }; - self.read_results_from_memory(&func.results, ptr, 0); + self.read_results_from_memory(&func.results, ptr.clone(), 0); + self.emit(&Instruction::Flush { + amt: func.results.len(), + }); + + if let Some((size, align)) = dealloc_size_align { + self.stack.push(ptr); + self.emit(&Instruction::GuestDeallocate { + size: size.size_wasm32(), + align: align.align_wasm32(), + }); + } } self.emit(&Instruction::Return { @@ -885,6 +1062,20 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); } LiftLower::LiftArgsLowerResults => { + if let (AbiVariant::GuestImport, true) = (self.variant, self.async_) { + todo!("implement host-side support for async lift/lower"); + } + + let read_from_memory = |self_: &mut Self| { + let mut offset = 0usize; + let ptr = self_.stack.pop().unwrap(); + for (_, ty) in func.params.iter() { + offset = align_to(offset, self_.bindgen.sizes().align(ty).align_wasm32()); + self_.read_from_memory(ty, ptr.clone(), offset as i32); + offset += self_.bindgen.sizes().size(ty).size_wasm32(); + } + }; + if !sig.indirect_params { // If parameters are not passed indirectly then we lift each // argument in succession from the component wasm types that @@ -904,23 +1095,33 @@ impl<'a, B: Bindgen> Generator<'a, B> { // ... otherwise argument is read in succession from memory // where the pointer to the arguments is the first argument // to the function. - let mut offset = 0usize; self.emit(&Instruction::GetArg { nth: 0 }); - let ptr = self.stack.pop().unwrap(); - for (_, ty) in func.params.iter() { - offset = align_to(offset, self.bindgen.sizes().align(ty).align_wasm32()); - self.read_from_memory(ty, ptr.clone(), offset as i32); - offset += self.bindgen.sizes().size(ty).size_wasm32(); - } + read_from_memory(self); } // ... and that allows us to call the interface types function - self.emit(&Instruction::CallInterface { func }); + self.emit(&Instruction::CallInterface { + func, + async_: self.async_, + }); - // This was dynamically allocated by the caller so after - // it's been read by the guest we need to deallocate it. + let (lower_to_memory, async_results) = if self.async_ { + self.emit(&Instruction::AsyncPostCallInterface { func }); + + let mut results = Vec::new(); + for ty in func.results.iter_types() { + self.resolve.push_flat(ty, &mut results); + } + (results.len() > MAX_FLAT_PARAMS, Some(results)) + } else { + (sig.retptr, None) + }; + + // This was dynamically allocated by the caller (or async start + // function) so after it's been read by the guest we need to + // deallocate it. if let AbiVariant::GuestExport = self.variant { - if sig.indirect_params { + if sig.indirect_params && !self.async_ { let info = self .bindgen .sizes() @@ -933,7 +1134,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { } } - if !sig.retptr { + if !lower_to_memory { // With no return pointer in use we simply lower the // result(s) and return that directly from the function. let results = self @@ -973,13 +1174,31 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.write_params_to_memory(func.results.iter_types(), ptr.clone(), 0); self.stack.push(ptr); } + + AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + unreachable!() + } } } - self.emit(&Instruction::Return { - func, - amt: sig.results.len(), - }); + if let Some(results) = async_results { + let name = &format!("[task-return]{}", func.name); + + self.emit(&Instruction::AsyncCallReturn { + name, + params: &if results.len() > MAX_FLAT_PARAMS { + vec![WasmType::Pointer] + } else { + results + }, + }); + self.emit(&Instruction::Return { func, amt: 1 }); + } else { + self.emit(&Instruction::Return { + func, + amt: sig.results.len(), + }); + } } } @@ -1177,8 +1396,21 @@ impl<'a, B: Bindgen> Generator<'a, B> { results: &results, }); } - TypeDefKind::Future(_) => todo!("lower future"), - TypeDefKind::Stream(_) => todo!("lower stream"), + TypeDefKind::Future(ty) => { + self.emit(&FutureLower { + payload: ty, + ty: id, + }); + } + TypeDefKind::Stream(ty) => { + self.emit(&StreamLower { + payload: ty, + ty: id, + }); + } + TypeDefKind::ErrorContext => { + self.emit(&ErrorContextLower { ty: id }); + } TypeDefKind::Unknown => unreachable!(), }, } @@ -1362,8 +1594,21 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.emit(&ResultLift { result: r, ty: id }); } - TypeDefKind::Future(_) => todo!("lift future"), - TypeDefKind::Stream(_) => todo!("lift stream"), + TypeDefKind::Future(ty) => { + self.emit(&FutureLift { + payload: ty, + ty: id, + }); + } + TypeDefKind::Stream(ty) => { + self.emit(&StreamLift { + payload: ty, + ty: id, + }); + } + TypeDefKind::ErrorContext => { + self.emit(&ErrorContextLift { ty: id }); + } TypeDefKind::Unknown => unreachable!(), }, } @@ -1431,7 +1676,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::Type(t) => self.write_to_memory(t, addr, offset), TypeDefKind::List(_) => self.write_list_to_memory(ty, addr, offset), - TypeDefKind::Handle(_) => self.lower_and_emit(ty, addr, &I32Store { offset }), + TypeDefKind::Future(_) + | TypeDefKind::Stream(_) + | TypeDefKind::ErrorContext + | TypeDefKind::Handle(_) => self.lower_and_emit(ty, addr, &I32Store { offset }), // Decompose the record into its components and then write all // the components into memory one-by-one. @@ -1521,8 +1769,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.store_intrepr(offset, e.tag()); } - TypeDefKind::Future(_) => todo!("write future to memory"), - TypeDefKind::Stream(_) => todo!("write stream to memory"), TypeDefKind::Unknown => unreachable!(), }, } @@ -1625,7 +1871,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::List(_) => self.read_list_from_memory(ty, addr, offset), - TypeDefKind::Handle(_) => self.emit_and_lift(ty, addr, &I32Load { offset }), + TypeDefKind::Future(_) + | TypeDefKind::Stream(_) + | TypeDefKind::ErrorContext + | TypeDefKind::Handle(_) => self.emit_and_lift(ty, addr, &I32Load { offset }), TypeDefKind::Resource => { todo!(); @@ -1709,8 +1958,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.lift(ty); } - TypeDefKind::Future(_) => todo!("read future from memory"), - TypeDefKind::Stream(_) => todo!("read stream from memory"), TypeDefKind::Unknown => unreachable!(), }, } @@ -1887,6 +2134,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::Future(_) => todo!("read future from memory"), TypeDefKind::Stream(_) => todo!("read stream from memory"), + TypeDefKind::ErrorContext => todo!("read error-context from memory"), TypeDefKind::Unknown => unreachable!(), }, } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index fae3f3b90..ccebe94d1 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -22,6 +22,22 @@ pub enum Direction { pub trait WorldGenerator { fn generate(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) -> Result<()> { + // TODO: Should we refine this test to inspect only types reachable from + // the specified world? + if !cfg!(feature = "async") + && resolve.types.iter().any(|(_, ty)| { + matches!( + ty.kind, + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext + ) + }) + { + anyhow::bail!( + "must enable `async` feature when using WIT files \ + containing future, stream, or error types" + ); + } + let world = &resolve.worlds[id]; self.preprocess(resolve, id); @@ -154,6 +170,9 @@ pub trait InterfaceGenerator<'a> { fn type_alias(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs); fn type_list(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs); fn type_builtin(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs); + fn type_future(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs); + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs); + fn type_error_context(&mut self, id: TypeId, name: &str, docs: &Docs); fn types(&mut self, iface: InterfaceId) { let iface = &self.resolve().interfaces[iface]; for (name, id) in iface.types.iter() { @@ -174,9 +193,10 @@ pub trait InterfaceGenerator<'a> { 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::Future(t) => self.type_future(id, name, t, &ty.docs), + TypeDefKind::Stream(t) => self.type_stream(id, name, t, &ty.docs), + TypeDefKind::Handle(_) => panic!("handle types do not require definition"), + TypeDefKind::ErrorContext => self.type_error_context(id, name, &ty.docs), TypeDefKind::Unknown => unreachable!(), } } @@ -191,8 +211,9 @@ pub trait AnonymousTypeGenerator<'a> { fn anonymous_type_result(&mut self, id: TypeId, ty: &Result_, docs: &Docs); fn anonymous_type_list(&mut self, id: TypeId, ty: &Type, docs: &Docs); fn anonymous_type_future(&mut self, id: TypeId, ty: &Option, docs: &Docs); - fn anonymous_type_stream(&mut self, id: TypeId, ty: &Stream, docs: &Docs); - fn anonymous_typ_type(&mut self, id: TypeId, ty: &Type, docs: &Docs); + fn anonymous_type_stream(&mut self, id: TypeId, ty: &Type, docs: &Docs); + fn anonymous_type_type(&mut self, id: TypeId, ty: &Type, docs: &Docs); + fn anonymous_type_error_context(&mut self); fn define_anonymous_type(&mut self, id: TypeId) { let ty = &self.resolve().types[id]; @@ -204,13 +225,14 @@ pub trait AnonymousTypeGenerator<'a> { | TypeDefKind::Variant(_) => { unreachable!() } - TypeDefKind::Type(t) => self.anonymous_typ_type(id, t, &ty.docs), + TypeDefKind::Type(t) => self.anonymous_type_type(id, t, &ty.docs), TypeDefKind::Tuple(tuple) => self.anonymous_type_tuple(id, tuple, &ty.docs), TypeDefKind::Option(t) => self.anonymous_type_option(id, t, &ty.docs), TypeDefKind::Result(r) => self.anonymous_type_result(id, r, &ty.docs), TypeDefKind::List(t) => self.anonymous_type_list(id, t, &ty.docs), TypeDefKind::Future(f) => self.anonymous_type_future(id, f, &ty.docs), TypeDefKind::Stream(s) => self.anonymous_type_stream(id, s, &ty.docs), + TypeDefKind::ErrorContext => self.anonymous_type_error_context(), TypeDefKind::Handle(handle) => self.anonymous_type_handle(id, handle, &ty.docs), TypeDefKind::Unknown => unreachable!(), } diff --git a/crates/core/src/types.rs b/crates/core/src/types.rs index a0862f5e8..42f4f60e6 100644 --- a/crates/core/src/types.rs +++ b/crates/core/src/types.rs @@ -193,12 +193,8 @@ impl Types { info = self.optional_type_info(resolve, r.ok.as_ref()); info |= self.optional_type_info(resolve, r.err.as_ref()); } - TypeDefKind::Future(ty) => { - info = self.optional_type_info(resolve, ty.as_ref()); - } - TypeDefKind::Stream(stream) => { - info = self.optional_type_info(resolve, stream.element.as_ref()); - info |= self.optional_type_info(resolve, stream.end.as_ref()); + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext => { + info.has_resource = true; } TypeDefKind::Unknown => unreachable!(), } diff --git a/crates/csharp/src/lib.rs b/crates/csharp/src/lib.rs index 744239a91..e40a8c3ca 100644 --- a/crates/csharp/src/lib.rs +++ b/crates/csharp/src/lib.rs @@ -1128,6 +1128,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut bindgen, + false, ); let src = bindgen.src; @@ -1251,6 +1252,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut bindgen, + false, ); assert!(!bindgen.needs_cleanup_list); @@ -1995,6 +1997,21 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { self.type_name(&Type::Id(id)); } + fn type_future(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs) { + _ = (id, name, ty, docs); + todo!() + } + + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { + _ = (id, name, ty, docs); + todo!() + } + + fn type_error_context(&mut self, id: TypeId, name: &str, docs: &Docs) { + _ = (id, name, docs); + todo!() + } + fn type_builtin(&mut self, _id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) { unimplemented!(); } @@ -2691,7 +2708,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { )); } - Instruction::ListLower { element, realloc } => { + Instruction::ListLower { element, realloc: _ } => { let Block { body, results: block_results, @@ -2796,7 +2813,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { ); } - Instruction::CallInterface { func } => { + Instruction::CallInterface { func, .. } => { let module = self.gen.name; let func_name = self.func_name.to_upper_camel_case(); let interface_name = CSharp::get_class_name_from_qualified_name(module).1; @@ -3107,6 +3124,21 @@ impl Bindgen for FunctionBindgen<'_, '_> { } results.push(resource); } + + Instruction::Flush { amt } => { + results.extend(operands.iter().take(*amt).map(|v| v.clone())); + } + + Instruction::AsyncMalloc { .. } + | Instruction::AsyncPostCallInterface { .. } + | Instruction::AsyncCallReturn { .. } + | Instruction::FutureLower { .. } + | Instruction::FutureLift { .. } + | Instruction::StreamLower { .. } + | Instruction::StreamLift { .. } + | Instruction::ErrorContextLower { .. } + | Instruction::ErrorContextLift { .. } + | Instruction::AsyncCallWasm { .. } => todo!(), } } diff --git a/crates/csharp/tests/codegen.rs b/crates/csharp/tests/codegen.rs index 6d10104cd..9501e6df0 100644 --- a/crates/csharp/tests/codegen.rs +++ b/crates/csharp/tests/codegen.rs @@ -8,6 +8,14 @@ use std::{ use wit_component::StringEncoding; macro_rules! codegen_test { + // TODO: implement support for stream, future, and error-context, and then + // remove these lines: + (streams $name:tt $test:tt) => {}; + (futures $name:tt $test:tt) => {}; + (resources_with_streams $name:tt $test:tt) => {}; + (resources_with_futures $name:tt $test:tt) => {}; + (error_context $name:tt $test:tt) => {}; + ($id:ident $name:tt $test:tt) => { #[test] fn $id() { diff --git a/crates/go/src/bindgen.rs b/crates/go/src/bindgen.rs index f045bbd00..853f97673 100644 --- a/crates/go/src/bindgen.rs +++ b/crates/go/src/bindgen.rs @@ -319,6 +319,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } TypeDefKind::Future(_) => todo!("impl future"), TypeDefKind::Stream(_) => todo!("impl stream"), + TypeDefKind::ErrorContext => todo!("impl error-context"), TypeDefKind::Resource => todo!("impl resource"), TypeDefKind::Handle(h) => { match self.interface.direction { @@ -609,6 +610,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } TypeDefKind::Future(_) => todo!("impl future"), TypeDefKind::Stream(_) => todo!("impl stream"), + TypeDefKind::ErrorContext => todo!("impl error-context"), TypeDefKind::Resource => todo!("impl resource"), TypeDefKind::Handle(h) => { match self.interface.direction { diff --git a/crates/go/src/interface.rs b/crates/go/src/interface.rs index ba7e150cb..39c54aef1 100644 --- a/crates/go/src/interface.rs +++ b/crates/go/src/interface.rs @@ -322,11 +322,11 @@ impl InterfaceGenerator<'_> { TypeDefKind::Stream(t) => { let mut src = String::new(); src.push_str("Stream"); - src.push_str(&self.optional_ty_name(t.element.as_ref())); - src.push_str(&self.optional_ty_name(t.end.as_ref())); + src.push_str(&self.ty_name(t)); src.push('T'); src } + TypeDefKind::ErrorContext => "ErrorContext".to_owned(), TypeDefKind::Handle(Handle::Own(ty)) => { // Currently there is no different between Own and Borrow // in the Go code. They are just represented as @@ -678,6 +678,7 @@ impl InterfaceGenerator<'_> { } TypeDefKind::Future(_) => todo!("anonymous_type for future"), TypeDefKind::Stream(_) => todo!("anonymous_type for stream"), + TypeDefKind::ErrorContext => todo!("anonymous_type for error-context"), TypeDefKind::Unknown => unreachable!(), } } @@ -1265,6 +1266,21 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { // no impl since these types are generated as anonymous types } + fn type_future(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs) { + _ = (id, name, ty, docs); + todo!() + } + + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { + _ = (id, name, ty, docs); + todo!() + } + + fn type_error_context(&mut self, id: TypeId, name: &str, docs: &Docs) { + _ = (id, name, docs); + todo!() + } + fn type_builtin(&mut self, _id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) { todo!("type_builtin") } diff --git a/crates/go/tests/codegen.rs b/crates/go/tests/codegen.rs index 9a7d8b837..daa437658 100644 --- a/crates/go/tests/codegen.rs +++ b/crates/go/tests/codegen.rs @@ -8,6 +8,15 @@ use heck::*; macro_rules! codegen_test { (issue668 $name:tt $test:tt) => {}; (multiversion $name:tt $test:tt) => {}; + + // TODO: implement support for stream, future, and error-context, and then + // remove these lines: + (streams $name:tt $test:tt) => {}; + (futures $name:tt $test:tt) => {}; + (resources_with_streams $name:tt $test:tt) => {}; + (resources_with_futures $name:tt $test:tt) => {}; + (error_context $name:tt $test:tt) => {}; + ($id:ident $name:tt $test:tt) => { #[test] fn $id() { diff --git a/crates/guest-rust/Cargo.toml b/crates/guest-rust/Cargo.toml index 1f666c479..c632f434f 100644 --- a/crates/guest-rust/Cargo.toml +++ b/crates/guest-rust/Cargo.toml @@ -16,6 +16,7 @@ wit-bindgen-rust-macro = { path = "./macro", optional = true, version = "0.35.0" wit-bindgen-rt = { path = "./rt", version = "0.35.0", features = ["bitflags"] } [features] -default = ["macros", "realloc"] +default = ["macros", "realloc", "async"] macros = ["dep:wit-bindgen-rust-macro"] realloc = [] +async = ["macros", "wit-bindgen-rt/async", "wit-bindgen-rust-macro/async"] diff --git a/crates/guest-rust/macro/Cargo.toml b/crates/guest-rust/macro/Cargo.toml index 984a1bffc..f30dd8780 100644 --- a/crates/guest-rust/macro/Cargo.toml +++ b/crates/guest-rust/macro/Cargo.toml @@ -24,3 +24,6 @@ anyhow = { workspace = true } syn = { workspace = true } prettyplease = { workspace = true } +[features] +default = ["async"] +async = ["wit-bindgen-rust/async"] diff --git a/crates/guest-rust/macro/src/lib.rs b/crates/guest-rust/macro/src/lib.rs index 5648eff71..24a04f291 100644 --- a/crates/guest-rust/macro/src/lib.rs +++ b/crates/guest-rust/macro/src/lib.rs @@ -8,7 +8,7 @@ use syn::punctuated::Punctuated; use syn::spanned::Spanned; use syn::{braced, token, LitStr, Token}; use wit_bindgen_core::wit_parser::{PackageId, Resolve, UnresolvedPackageGroup, WorldId}; -use wit_bindgen_rust::{Opts, Ownership, WithOption}; +use wit_bindgen_rust::{AsyncConfig, Opts, Ownership, WithOption}; #[proc_macro] pub fn generate(input: proc_macro::TokenStream) -> proc_macro::TokenStream { @@ -46,6 +46,7 @@ struct Config { resolve: Resolve, world: WorldId, files: Vec, + debug: bool, } /// The source of the wit package definition @@ -63,6 +64,8 @@ impl Parse for Config { let mut world = None; let mut source = None; let mut features = Vec::new(); + let mut async_configured = false; + let mut debug = false; if input.peek(token::Brace) { let content; @@ -140,6 +143,22 @@ impl Parse for Config { Opt::DisableCustomSectionLinkHelpers(disable) => { opts.disable_custom_section_link_helpers = disable.value(); } + Opt::Debug(enable) => { + debug = enable.value(); + } + Opt::Async(val, span) => { + if async_configured { + return Err(Error::new(span, "cannot specify second async config")); + } + async_configured = true; + if !matches!(val, AsyncConfig::None) && !cfg!(feature = "async") { + return Err(Error::new( + span, + "must enable `async` feature to enable async imports and/or exports", + )); + } + opts.async_ = val; + } } } } else { @@ -159,6 +178,7 @@ impl Parse for Config { resolve, world, files, + debug, }) } } @@ -254,7 +274,7 @@ impl Config { // place a formatted version of the expanded code into a file. This file // will then show up in rustc error messages for any codegen issues and can // be inspected manually. - if std::env::var("WIT_BINDGEN_DEBUG").is_ok() { + if std::env::var("WIT_BINDGEN_DEBUG").is_ok() || self.debug { static INVOCATION: AtomicUsize = AtomicUsize::new(0); let root = Path::new(env!("DEBUG_OUTPUT_DIR")); let world_name = &self.resolve.worlds[self.world].name; @@ -313,6 +333,8 @@ mod kw { syn::custom_keyword!(generate_unused_types); syn::custom_keyword!(features); syn::custom_keyword!(disable_custom_section_link_helpers); + syn::custom_keyword!(imports); + syn::custom_keyword!(debug); } #[derive(Clone)] @@ -342,6 +364,11 @@ impl From for wit_bindgen_rust::ExportKey { } } +enum AsyncConfigSomeKind { + Imports, + Exports, +} + enum Opt { World(syn::LitStr), Path(Span, Vec), @@ -366,6 +393,8 @@ enum Opt { GenerateUnusedTypes(syn::LitBool), Features(Vec), DisableCustomSectionLinkHelpers(syn::LitBool), + Async(AsyncConfig, Span), + Debug(syn::LitBool), } impl Parse for Opt { @@ -513,6 +542,34 @@ impl Parse for Opt { input.parse::()?; input.parse::()?; Ok(Opt::DisableCustomSectionLinkHelpers(input.parse()?)) + } else if l.peek(kw::debug) { + input.parse::()?; + input.parse::()?; + Ok(Opt::Debug(input.parse()?)) + } else if l.peek(Token![async]) { + let span = input.parse::()?.span; + input.parse::()?; + if input.peek(syn::LitBool) { + if input.parse::()?.value { + Ok(Opt::Async(AsyncConfig::All, span)) + } else { + Ok(Opt::Async(AsyncConfig::None, span)) + } + } else { + let mut imports = Vec::new(); + let mut exports = Vec::new(); + let contents; + syn::braced!(contents in input); + for (kind, values) in + contents.parse_terminated(parse_async_some_field, Token![,])? + { + match kind { + AsyncConfigSomeKind::Imports => imports = values, + AsyncConfigSomeKind::Exports => exports = values, + } + } + Ok(Opt::Async(AsyncConfig::Some { imports, exports }, span)) + } } else { Err(l.error()) } @@ -571,3 +628,27 @@ fn fmt(input: &str) -> Result { let syntax_tree = syn::parse_file(&input)?; Ok(prettyplease::unparse(&syntax_tree)) } + +fn parse_async_some_field(input: ParseStream<'_>) -> Result<(AsyncConfigSomeKind, Vec)> { + let lookahead = input.lookahead1(); + let kind = if lookahead.peek(kw::imports) { + input.parse::()?; + input.parse::()?; + AsyncConfigSomeKind::Imports + } else if lookahead.peek(kw::exports) { + input.parse::()?; + input.parse::()?; + AsyncConfigSomeKind::Exports + } else { + return Err(lookahead.error()); + }; + + let list; + syn::bracketed!(list in input); + let fields = list.parse_terminated(Parse::parse, Token![,])?; + + Ok(( + kind, + fields.iter().map(|s: &syn::LitStr| s.value()).collect(), + )) +} diff --git a/crates/guest-rust/rt/Cargo.toml b/crates/guest-rust/rt/Cargo.toml index d038f3fcc..e84f04d18 100644 --- a/crates/guest-rust/rt/Cargo.toml +++ b/crates/guest-rust/rt/Cargo.toml @@ -12,3 +12,9 @@ Runtime support for the `wit-bindgen` crate [dependencies] # Optionally re-export the version of bitflags used by wit-bindgen. bitflags = { workspace = true, optional = true } +futures = { version = "0.3.30", optional = true } +once_cell = { version = "1.19.0", optional = true } + +[features] +default = ["async"] +async = ["dep:futures", "dep:once_cell"] diff --git a/crates/guest-rust/rt/src/async_support.rs b/crates/guest-rust/rt/src/async_support.rs new file mode 100644 index 000000000..f326f1cb6 --- /dev/null +++ b/crates/guest-rust/rt/src/async_support.rs @@ -0,0 +1,513 @@ +#![deny(missing_docs)] +#![allow(static_mut_refs)] + +use { + futures::{ + channel::oneshot, + future::FutureExt, + stream::{FuturesUnordered, StreamExt}, + }, + once_cell::sync::Lazy, + std::{ + alloc::{self, Layout}, + any::Any, + collections::hash_map, + collections::HashMap, + fmt::{self, Debug, Display}, + future::Future, + pin::Pin, + ptr, + sync::Arc, + task::{Context, Poll, Wake, Waker}, + }, +}; + +pub use futures; + +type BoxFuture = Pin + 'static>>; + +/// Represents a task created by either a call to an async-lifted export or a +/// future run using `block_on` or `poll_future`. +struct FutureState { + /// Number of in-progress async-lowered import calls and/or stream/future reads/writes. + todo: usize, + /// Remaining work to do (if any) before this task can be considered "done". + /// + /// Note that we won't tell the host the task is done until this is drained + /// and `todo` is zero. + tasks: Option>, +} + +/// Represents the state of a stream or future. +#[doc(hidden)] +pub enum Handle { + LocalOpen, + LocalReady(Box, Waker), + LocalWaiting(oneshot::Sender>), + LocalClosed, + Read, + Write, +} + +/// The current task being polled (or null if none). +static mut CURRENT: *mut FutureState = ptr::null_mut(); + +/// Map of any in-progress calls to async-lowered imports, keyed by the +/// identifiers issued by the host. +static mut CALLS: Lazy>> = Lazy::new(HashMap::new); + +/// Any newly-deferred work queued by calls to the `spawn` function while +/// polling the current task. +static mut SPAWNED: Vec = Vec::new(); + +/// The states of all currently-open streams and futures. +static mut HANDLES: Lazy> = Lazy::new(HashMap::new); + +#[doc(hidden)] +pub fn with_entry(handle: u32, fun: impl FnOnce(hash_map::Entry<'_, u32, Handle>) -> T) -> T { + fun(unsafe { HANDLES.entry(handle) }) +} + +fn dummy_waker() -> Waker { + struct DummyWaker; + + impl Wake for DummyWaker { + fn wake(self: Arc) {} + } + + static WAKER: Lazy> = Lazy::new(|| Arc::new(DummyWaker)); + + WAKER.clone().into() +} + +/// Poll the specified task until it either completes or can't make immediate +/// progress. +unsafe fn poll(state: *mut FutureState) -> Poll<()> { + loop { + if let Some(futures) = (*state).tasks.as_mut() { + CURRENT = state; + let poll = futures.poll_next_unpin(&mut Context::from_waker(&dummy_waker())); + CURRENT = ptr::null_mut(); + + if SPAWNED.is_empty() { + match poll { + Poll::Ready(Some(())) => (), + Poll::Ready(None) => { + (*state).tasks = None; + break Poll::Ready(()); + } + Poll::Pending => break Poll::Pending, + } + } else { + futures.extend(SPAWNED.drain(..)); + } + } else { + break Poll::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( + future: impl Future + 'static, + fun: impl FnOnce(T) + 'static, +) -> *mut u8 { + let state = Box::into_raw(Box::new(FutureState { + todo: 0, + tasks: Some( + [Box::pin(future.map(fun)) as BoxFuture] + .into_iter() + .collect(), + ), + })); + match unsafe { poll(state) } { + Poll::Ready(()) => ptr::null_mut(), + Poll::Pending => state as _, + } +} + +/// Await the completion of a call to an async-lowered import. +#[doc(hidden)] +pub async unsafe fn await_result( + import: unsafe extern "C" fn(*mut u8, *mut u8) -> i32, + params_layout: Layout, + params: *mut u8, + results: *mut u8, +) { + const STATUS_STARTING: u32 = 0; + const STATUS_STARTED: u32 = 1; + const STATUS_RETURNED: u32 = 2; + const STATUS_DONE: u32 = 3; + + let result = import(params, results) as u32; + let status = result >> 30; + let call = (result & !(0b11 << 30)) as i32; + + if status != STATUS_DONE { + assert!(!CURRENT.is_null()); + (*CURRENT).todo += 1; + } + + match status { + STATUS_STARTING => { + let (tx, rx) = oneshot::channel(); + CALLS.insert(call, tx); + rx.await.unwrap(); + alloc::dealloc(params, params_layout); + } + STATUS_STARTED => { + alloc::dealloc(params, params_layout); + let (tx, rx) = oneshot::channel(); + CALLS.insert(call, tx); + rx.await.unwrap(); + } + STATUS_RETURNED | STATUS_DONE => { + alloc::dealloc(params, params_layout); + } + _ => unreachable!(), + } +} + +/// stream/future read/write results defined by the Component Model ABI. +mod results { + pub const BLOCKED: u32 = 0xffff_ffff; + pub const CLOSED: u32 = 0x8000_0000; + pub const CANCELED: u32 = 0; +} + +/// Await the completion of a future read or write. +#[doc(hidden)] +pub async unsafe fn await_future_result( + import: unsafe extern "C" fn(u32, *mut u8) -> u32, + future: u32, + address: *mut u8, +) -> bool { + let result = import(future, address); + match result { + results::BLOCKED => { + assert!(!CURRENT.is_null()); + (*CURRENT).todo += 1; + let (tx, rx) = oneshot::channel(); + CALLS.insert(future as _, tx); + let v = rx.await.unwrap(); + v == 1 + } + results::CLOSED | results::CANCELED => false, + 1 => true, + _ => unreachable!(), + } +} + +/// Await the completion of a stream read or write. +#[doc(hidden)] +pub async unsafe fn await_stream_result( + import: unsafe extern "C" fn(u32, *mut u8, u32) -> u32, + stream: u32, + address: *mut u8, + count: u32, +) -> Option { + let result = import(stream, address, count); + match result { + results::BLOCKED => { + assert!(!CURRENT.is_null()); + (*CURRENT).todo += 1; + let (tx, rx) = oneshot::channel(); + CALLS.insert(stream as _, tx); + let v = rx.await.unwrap(); + if let results::CLOSED | results::CANCELED = v { + None + } else { + Some(usize::try_from(v).unwrap()) + } + } + results::CLOSED | results::CANCELED => None, + v => Some(usize::try_from(v).unwrap()), + } +} + +/// Call the `subtask.drop` canonical built-in function. +fn subtask_drop(subtask: u32) { + #[cfg(not(target_arch = "wasm32"))] + { + _ = subtask; + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[subtask-drop]"] + fn subtask_drop(_: u32); + } + unsafe { + subtask_drop(subtask); + } + } +} + +/// Handle a progress notification from the host regarding either a call to an +/// async-lowered import or a stream/future read/write operation. +#[doc(hidden)] +pub unsafe fn callback(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + const _EVENT_CALL_STARTING: i32 = 0; + const EVENT_CALL_STARTED: i32 = 1; + const EVENT_CALL_RETURNED: i32 = 2; + const EVENT_CALL_DONE: i32 = 3; + const _EVENT_YIELDED: i32 = 4; + const EVENT_STREAM_READ: i32 = 5; + const EVENT_STREAM_WRITE: i32 = 6; + const EVENT_FUTURE_READ: i32 = 7; + const EVENT_FUTURE_WRITE: i32 = 8; + + match event0 { + EVENT_CALL_STARTED => 0, + EVENT_CALL_RETURNED | EVENT_CALL_DONE | EVENT_STREAM_READ | EVENT_STREAM_WRITE + | EVENT_FUTURE_READ | EVENT_FUTURE_WRITE => { + if let Some(call) = CALLS.remove(&event1) { + _ = call.send(event2 as _); + } + + let state = ctx as *mut FutureState; + let done = poll(state).is_ready(); + + if event0 == EVENT_CALL_DONE { + subtask_drop(event1 as u32); + } + + if matches!( + event0, + EVENT_CALL_DONE + | EVENT_STREAM_READ + | EVENT_STREAM_WRITE + | EVENT_FUTURE_READ + | EVENT_FUTURE_WRITE + ) { + (*state).todo -= 1; + } + + if done && (*state).todo == 0 { + drop(Box::from_raw(state)); + 1 + } else { + 0 + } + } + _ => unreachable!(), + } +} + +/// Represents the Component Model `error-context` type. +pub struct ErrorContext { + handle: u32, +} + +impl ErrorContext { + #[doc(hidden)] + pub fn from_handle(handle: u32) -> Self { + Self { handle } + } + + #[doc(hidden)] + pub fn handle(&self) -> u32 { + self.handle + } +} + +impl Debug for ErrorContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ErrorContext").finish() + } +} + +impl Display for ErrorContext { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Error") + } +} + +impl std::error::Error for ErrorContext {} + +impl Drop for ErrorContext { + fn drop(&mut self) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[error-context-drop]"] + fn error_drop(_: u32); + } + if self.handle != 0 { + unsafe { error_drop(self.handle) } + } + } + } +} + +/// Defer the specified future to be run after the current async-lifted export +/// task has returned a value. +/// +/// The task will remain in a running state until all spawned futures have +/// completed. +pub fn spawn(future: impl Future + 'static) { + unsafe { SPAWNED.push(Box::pin(future)) } +} + +fn task_wait(state: &mut FutureState) { + #[cfg(not(target_arch = "wasm32"))] + { + _ = state; + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-wait]"] + fn wait(_: *mut i32) -> i32; + } + let mut payload = [0i32; 2]; + unsafe { + let event0 = wait(payload.as_mut_ptr()); + callback(state as *mut _ as _, event0, payload[0], payload[1]); + } + } +} + +/// Run the specified future to completion, returning the result. +/// +/// This uses `task.wait` to poll for progress on any in-progress calls to +/// async-lowered imports as necessary. +// TODO: refactor so `'static` bounds aren't necessary +pub fn block_on(future: impl Future + 'static) -> T { + let (tx, mut rx) = oneshot::channel(); + let state = &mut FutureState { + todo: 0, + tasks: Some( + [Box::pin(future.map(move |v| drop(tx.send(v)))) as BoxFuture] + .into_iter() + .collect(), + ), + }; + loop { + match unsafe { poll(state) } { + Poll::Ready(()) => break rx.try_recv().unwrap().unwrap(), + Poll::Pending => task_wait(state), + } + } +} + +fn task_poll(state: &mut FutureState) -> bool { + #[cfg(not(target_arch = "wasm32"))] + { + _ = state; + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-poll]"] + fn poll(_: *mut i32) -> i32; + } + let mut payload = [0i32; 3]; + unsafe { + let got_event = poll(payload.as_mut_ptr()) != 0; + if got_event { + callback(state as *mut _ as _, payload[0], payload[1], payload[2]); + } + got_event + } + } +} + +/// Attempt to run the specified future to completion without blocking, +/// returning the result if it completes. +/// +/// This is similar to `block_on` except that it uses `task.poll` instead of +/// `task.wait` to check for progress on any in-progress calls to async-lowered +/// imports, returning `None` if one or more of those calls remain pending. +// TODO: refactor so `'static` bounds aren't necessary +pub fn poll_future(future: impl Future + 'static) -> Option { + let (tx, mut rx) = oneshot::channel(); + let state = &mut FutureState { + todo: 0, + tasks: Some( + [Box::pin(future.map(move |v| drop(tx.send(v)))) as BoxFuture] + .into_iter() + .collect(), + ), + }; + loop { + match unsafe { poll(state) } { + Poll::Ready(()) => break Some(rx.try_recv().unwrap().unwrap()), + Poll::Pending => { + if !task_poll(state) { + break None; + } + } + } + } +} + +/// Call the `task.yield` canonical built-in function. +/// +/// This yields control to the host temporarily, allowing other tasks to make +/// progress. It's a good idea to call this inside a busy loop which does not +/// otherwise ever yield control the the host. +pub fn task_yield() { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-yield]"] + fn yield_(); + } + unsafe { + yield_(); + } + } +} + +/// Call the `task.backpressure` canonical built-in function. +/// +/// When `enabled` is `true`, this tells the host to defer any new calls to this +/// component instance until further notice (i.e. until `task.backpressure` is +/// called again with `enabled` set to `false`). +pub fn task_backpressure(enabled: bool) { + #[cfg(not(target_arch = "wasm32"))] + { + _ = enabled; + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "$root")] + extern "C" { + #[link_name = "[task-backpressure]"] + fn backpressure(_: i32); + } + unsafe { + backpressure(if enabled { 1 } else { 0 }); + } + } +} diff --git a/crates/guest-rust/rt/src/lib.rs b/crates/guest-rust/rt/src/lib.rs index 406ed61dc..c9a63cf71 100644 --- a/crates/guest-rust/rt/src/lib.rs +++ b/crates/guest-rust/rt/src/lib.rs @@ -1,4 +1,4 @@ -#![no_std] +#![cfg_attr(not(feature = "async"), no_std)] extern crate alloc; @@ -112,3 +112,11 @@ pub fn run_ctors_once() { } } } + +/// Support for using the Component Model Async ABI +#[cfg(not(feature = "async"))] +pub mod async_support {} + +/// Support for using the Component Model Async ABI +#[cfg(feature = "async")] +pub mod async_support; diff --git a/crates/markdown/src/lib.rs b/crates/markdown/src/lib.rs index ae6380003..e41cb7a3a 100644 --- a/crates/markdown/src/lib.rs +++ b/crates/markdown/src/lib.rs @@ -413,28 +413,14 @@ impl InterfaceGenerator<'_> { self.push_str("future"); } }, - TypeDefKind::Stream(s) => match (s.element, s.end) { - (Some(element), Some(end)) => { - self.push_str("stream<"); - self.print_ty(&element); - self.push_str(", "); - self.print_ty(&end); - self.push_str(">"); - } - (None, Some(end)) => { - self.push_str("stream<_, "); - self.print_ty(&end); - self.push_str(">"); - } - (Some(element), None) => { - self.push_str("stream<"); - self.print_ty(&element); - self.push_str(">"); - } - (None, None) => { - self.push_str("stream"); - } - }, + TypeDefKind::Stream(t) => { + self.push_str("stream<"); + self.print_ty(t); + self.push_str(">"); + } + TypeDefKind::ErrorContext => { + self.push_str("error-context"); + } TypeDefKind::Handle(Handle::Own(ty)) => { self.push_str("own<"); self.print_ty(&Type::Id(*ty)); @@ -670,6 +656,21 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { self.type_alias(id, name, &Type::Id(id), docs); } + fn type_future(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs) { + _ = (id, name, ty, docs); + todo!() + } + + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { + _ = (id, name, ty, docs); + todo!() + } + + fn type_error_context(&mut self, id: TypeId, name: &str, docs: &Docs) { + _ = (id, name, docs); + todo!() + } + fn type_builtin(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { self.type_alias(id, name, ty, docs) } diff --git a/crates/moonbit/src/lib.rs b/crates/moonbit/src/lib.rs index 0fb989372..72be26664 100644 --- a/crates/moonbit/src/lib.rs +++ b/crates/moonbit/src/lib.rs @@ -779,6 +779,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut bindgen, + false, ); let src = bindgen.src; @@ -868,6 +869,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut bindgen, + false, ); assert!(!bindgen.needs_cleanup_list); @@ -927,7 +929,7 @@ impl InterfaceGenerator<'_> { (0..sig.results.len()).map(|i| format!("p{i}")).collect(), ); - abi::post_return(bindgen.gen.resolve, func, &mut bindgen); + abi::post_return(bindgen.gen.resolve, func, &mut bindgen, false); let src = bindgen.src; @@ -1497,6 +1499,21 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { // Not needed } + fn type_future(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs) { + _ = (id, name, ty, docs); + todo!() + } + + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { + _ = (id, name, ty, docs); + todo!() + } + + fn type_error_context(&mut self, id: TypeId, name: &str, docs: &Docs) { + _ = (id, name, docs); + todo!() + } + fn type_builtin(&mut self, _id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) { unimplemented!(); } @@ -2579,6 +2596,21 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!(self.src, "{ffi_qualifier}free({address})"); } + + Instruction::Flush { amt } => { + results.extend(operands.iter().take(*amt).map(|v| v.clone())); + } + + Instruction::AsyncMalloc { .. } + | Instruction::AsyncPostCallInterface { .. } + | Instruction::AsyncCallReturn { .. } + | Instruction::FutureLower { .. } + | Instruction::FutureLift { .. } + | Instruction::StreamLower { .. } + | Instruction::StreamLift { .. } + | Instruction::ErrorContextLower { .. } + | Instruction::ErrorContextLift { .. } + | Instruction::AsyncCallWasm { .. } => todo!(), } } diff --git a/crates/moonbit/tests/codegen.rs b/crates/moonbit/tests/codegen.rs index a015cc880..c0857dded 100644 --- a/crates/moonbit/tests/codegen.rs +++ b/crates/moonbit/tests/codegen.rs @@ -2,6 +2,14 @@ use std::path::Path; use std::process::Command; macro_rules! codegen_test { + // TODO: implement support for stream, future, and error-context, and then + // remove these lines: + (streams $name:tt $test:tt) => {}; + (futures $name:tt $test:tt) => {}; + (resources_with_streams $name:tt $test:tt) => {}; + (resources_with_futures $name:tt $test:tt) => {}; + (error_context $name:tt $test:tt) => {}; + ($id:ident $name:tt $test:tt) => { #[test] fn $id() { diff --git a/crates/rust/Cargo.toml b/crates/rust/Cargo.toml index 1fe1d3bc2..ddaa117e2 100644 --- a/crates/rust/Cargo.toml +++ b/crates/rust/Cargo.toml @@ -27,8 +27,14 @@ syn = { workspace = true } prettyplease = { workspace = true } [dev-dependencies] +futures = { workspace = true } wit-bindgen = { path = '../guest-rust' } +wit-bindgen-rt = { path = '../guest-rust/rt' } test-helpers = { path = '../test-helpers' } # For use with the custom attributes test serde = { version = "1.0", features = ["derive"] } serde_json = "1" + +[features] +default = ["async"] +async = ["wit-bindgen-core/async"] diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 1e9dc9ff1..ecbc87a18 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -8,6 +8,8 @@ use wit_bindgen_core::{dealias, uwrite, uwriteln, wit_parser::*, Source}; pub(super) struct FunctionBindgen<'a, 'b> { pub gen: &'b mut InterfaceGenerator<'a>, params: Vec, + async_: bool, + wasm_import_module: &'b str, pub src: Source, blocks: Vec, block_storage: Vec<(Source, Vec<(String, String)>)>, @@ -23,10 +25,14 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { pub(super) fn new( gen: &'b mut InterfaceGenerator<'a>, params: Vec, + async_: bool, + wasm_import_module: &'b str, ) -> FunctionBindgen<'a, 'b> { FunctionBindgen { gen, params, + async_, + wasm_import_module, src: Default::default(), blocks: Vec::new(), block_storage: Vec::new(), @@ -58,14 +64,9 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } - fn declare_import( - &mut self, - module_name: &str, - name: &str, - params: &[WasmType], - results: &[WasmType], - ) -> String { + fn declare_import(&mut self, name: &str, params: &[WasmType], results: &[WasmType]) -> String { // Define the actual function we're calling inline + let tmp = self.tmp(); let mut sig = "(".to_owned(); for param in params.iter() { sig.push_str("_: "); @@ -78,6 +79,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { sig.push_str(" -> "); sig.push_str(wasm_type(*result)); } + let module_name = self.wasm_import_module; uwrite!( self.src, " @@ -85,14 +87,14 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { #[link(wasm_import_module = \"{module_name}\")] extern \"C\" {{ #[link_name = \"{name}\"] - fn wit_import{sig}; + fn wit_import{tmp}{sig}; }} #[cfg(not(target_arch = \"wasm32\"))] - fn wit_import{sig} {{ unreachable!() }} + extern \"C\" fn wit_import{tmp}{sig} {{ unreachable!() }} " ); - "wit_import".to_string() + format!("wit_import{tmp}") } fn let_results(&mut self, amt: usize, results: &mut Vec) { @@ -456,6 +458,45 @@ impl Bindgen for FunctionBindgen<'_, '_> { results.push(result); } + Instruction::FutureLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).into_handle() as i32")) + } + + Instruction::FutureLift { .. } => { + let stream_and_future_support = self.gen.path_to_stream_and_future_support(); + let op = &operands[0]; + results.push(format!( + "{stream_and_future_support}::FutureReader::from_handle({op} as u32)" + )) + } + + Instruction::StreamLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).into_handle() as i32")) + } + + Instruction::StreamLift { .. } => { + let stream_and_future_support = self.gen.path_to_stream_and_future_support(); + let op = &operands[0]; + results.push(format!( + "{stream_and_future_support}::StreamReader::from_handle({op} as u32)" + )) + } + + Instruction::ErrorContextLower { .. } => { + let op = &operands[0]; + results.push(format!("({op}).handle() as i32")) + } + + Instruction::ErrorContextLift { .. } => { + let async_support = self.gen.path_to_async_support(); + let op = &operands[0]; + results.push(format!( + "{async_support}::ErrorContext::from_handle({op} as u32)" + )) + } + Instruction::RecordLower { ty, record, .. } => { self.record_lower(*ty, record, &operands[0], results); } @@ -779,12 +820,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::IterBasePointer => results.push("base".to_string()), Instruction::CallWasm { name, sig, .. } => { - let func = self.declare_import( - self.gen.wasm_import_module.unwrap(), - name, - &sig.params, - &sig.results, - ); + let func = self.declare_import(name, &sig.params, &sig.results); // ... then call the function with all our operands if !sig.results.is_empty() { @@ -797,26 +833,57 @@ impl Bindgen for FunctionBindgen<'_, '_> { self.push_str(");\n"); } + Instruction::AsyncCallWasm { name, size, align } => { + let func = self.declare_import(name, &[WasmType::Pointer; 2], &[WasmType::I32]); + + let async_support = self.gen.path_to_async_support(); + let tmp = self.tmp(); + let layout = format!("layout{tmp}"); + let alloc = self.gen.path_to_std_alloc_module(); + self.push_str(&format!( + "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align});\n", + )); + let operands = operands.join(", "); + uwriteln!( + self.src, + "{async_support}::await_result({func}, {layout}, {operands}).await;" + ); + } + Instruction::CallInterface { func, .. } => { - self.let_results(func.results.len(), results); - match &func.kind { + if self.async_ { + let tmp = self.tmp(); + let result = format!("result{tmp}"); + self.push_str(&format!("let {result} = ")); + results.push(result); + } else { + self.let_results(func.results.len(), results); + }; + let constructor_type = match &func.kind { FunctionKind::Freestanding => { self.push_str(&format!("T::{}", to_rust_ident(&func.name))); + None } FunctionKind::Method(_) | FunctionKind::Static(_) => { self.push_str(&format!("T::{}", to_rust_ident(func.item_name()))); + None } FunctionKind::Constructor(ty) => { - self.push_str(&format!( - "{}::new(T::new", - resolve.types[*ty] - .name - .as_deref() - .unwrap() - .to_upper_camel_case() - )); + let ty = resolve.types[*ty] + .name + .as_deref() + .unwrap() + .to_upper_camel_case(); + let call = if self.async_ { + let async_support = self.gen.path_to_async_support(); + format!("{async_support}::futures::FutureExt::map(T::new") + } else { + format!("{ty}::new(T::new",) + }; + self.push_str(&call); + Some(ty) } - } + }; self.push_str("("); for (i, operand) in operands.iter().enumerate() { if i > 0 { @@ -833,12 +900,87 @@ impl Bindgen for FunctionBindgen<'_, '_> { } } self.push_str(")"); - if let FunctionKind::Constructor(_) = &func.kind { - self.push_str(")"); + if let Some(ty) = constructor_type { + self.push_str(&if self.async_ { + format!(", {ty}::new)") + } else { + ")".into() + }); } self.push_str(";\n"); } + Instruction::AsyncMalloc { size, align } => { + let alloc = self.gen.path_to_std_alloc_module(); + let tmp = self.tmp(); + let ptr = format!("ptr{tmp}"); + let layout = format!("layout{tmp}"); + uwriteln!( + self.src, + "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align}); + let {ptr} = {alloc}::alloc({layout});" + ); + results.push(ptr); + } + + Instruction::AsyncPostCallInterface { func } => { + let result = &operands[0]; + results.push("result".into()); + let params = (0..func.results.len()) + .map(|_| { + let tmp = self.tmp(); + let param = format!("result{}", tmp); + results.push(param.clone()); + param + }) + .collect::>() + .join(", "); + let params = if func.results.len() != 1 { + format!("({params})") + } else { + params + }; + let async_support = self.gen.path_to_async_support(); + // TODO: This relies on `abi::Generator` emitting + // `AsyncCallReturn` immediately after this instruction to + // complete the incomplete expression we generate here. We + // should refactor this so it's less fragile (e.g. have + // `abi::Generator` emit a `AsyncCallReturn` first, which would + // push a closure expression we can consume here). + // + // The async-specific `Instruction`s will probably need to be + // refactored anyway once we start implementing support for + // other languages besides Rust. + uwriteln!( + self.src, + "\ + let result = {async_support}::first_poll({result}, |{params}| {{ + " + ); + } + + Instruction::AsyncCallReturn { name, params } => { + let func = self.declare_import(name, params, &[]); + + uwriteln!( + self.src, + "\ + {func}({}); + }}); + ", + operands.join(", ") + ); + } + + Instruction::Flush { amt } => { + for i in 0..*amt { + let tmp = self.tmp(); + let result = format!("result{}", tmp); + uwriteln!(self.src, "let {result} = {};", operands[i]); + results.push(result); + } + } + Instruction::Return { amt, .. } => { self.emit_cleanup(); match amt { @@ -868,7 +1010,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { let tmp = self.tmp(); uwriteln!( self.src, - "let l{tmp} = i32::from(*{}.add({offset}).cast::());", + "let l{tmp} = i32::from(*{0}.add({offset}).cast::());", operands[0] ); results.push(format!("l{tmp}")); diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 2002016f5..24a6b81db 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -1,7 +1,7 @@ use crate::bindgen::FunctionBindgen; use crate::{ - int_repr, to_rust_ident, to_upper_camel_case, wasm_type, FnSig, Identifier, InterfaceName, - Ownership, RuntimeItem, RustFlagsRepr, RustWasm, + int_repr, to_rust_ident, to_upper_camel_case, wasm_type, AsyncConfig, FnSig, Identifier, + InterfaceName, Ownership, RuntimeItem, RustFlagsRepr, RustWasm, }; use anyhow::Result; use heck::*; @@ -19,7 +19,7 @@ pub struct InterfaceGenerator<'a> { pub in_import: bool, pub sizes: SizeAlign, pub(super) gen: &'a mut RustWasm, - pub wasm_import_module: Option<&'a str>, + pub wasm_import_module: &'a str, pub resolve: &'a Resolve, pub return_pointer_area_size: usize, pub return_pointer_area_align: usize, @@ -156,6 +156,17 @@ impl InterfaceGenerator<'_> { continue; } + let async_ = match &self.gen.opts.async_ { + AsyncConfig::None => false, + AsyncConfig::All => true, + AsyncConfig::Some { exports, .. } => { + exports.contains(&if let Some((_, key)) = interface { + format!("{}#{}", self.resolve.name_world_key(key), func.name) + } else { + func.name.clone() + }) + } + }; let resource = match func.kind { FunctionKind::Freestanding => None, FunctionKind::Method(id) @@ -163,12 +174,13 @@ impl InterfaceGenerator<'_> { | FunctionKind::Static(id) => Some(id), }; - funcs_to_export.push((func, resource)); + funcs_to_export.push((func, resource, async_)); let (trait_name, methods) = traits.get_mut(&resource).unwrap(); - self.generate_guest_export(func, &trait_name); + self.generate_guest_export(func, interface.map(|(_, k)| k), &trait_name, async_); let prev = mem::take(&mut self.src); let mut sig = FnSig { + async_, use_item_name: true, private: true, ..Default::default() @@ -177,7 +189,7 @@ impl InterfaceGenerator<'_> { sig.self_arg = Some("&self".into()); sig.self_is_first_param = true; } - self.print_signature(func, true, &sig); + self.print_signature(func, true, &sig, false); self.src.push_str(";\n"); let trait_method = mem::replace(&mut self.src, prev); methods.push(trait_method); @@ -188,9 +200,9 @@ impl InterfaceGenerator<'_> { self.generate_interface_trait( &name, &methods, - traits.iter().map(|(resource, (trait_name, _methods))| { - (resource.unwrap(), trait_name.as_str()) - }), + traits + .iter() + .map(|(resource, (trait_name, ..))| (resource.unwrap(), trait_name.as_str())), ) } @@ -259,7 +271,7 @@ fn _resource_rep(handle: u32) -> *mut u8 None => { let world = match self.identifier { Identifier::World(w) => w, - Identifier::Interface(..) => unreachable!(), + Identifier::None | Identifier::Interface(..) => unreachable!(), }; let world = self.resolve.worlds[world].name.to_snake_case(); format!("__export_world_{world}_cabi") @@ -292,7 +304,7 @@ macro_rules! {macro_name} {{ " ); - for (func, resource) in funcs_to_export { + for (func, resource, async_) in funcs_to_export { let ty = match resource { None => "$ty".to_string(), Some(id) => { @@ -304,13 +316,13 @@ macro_rules! {macro_name} {{ format!("<$ty as $($path_to_types)*::Guest>::{name}") } }; - self.generate_raw_cabi_export(func, &ty, "$($path_to_types)*"); + self.generate_raw_cabi_export(func, &ty, "$($path_to_types)*", async_); } let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); for name in resources_to_drop { let module = match self.identifier { Identifier::Interface(_, key) => self.resolve.name_world_key(key), - Identifier::World(_) => unreachable!(), + Identifier::None | Identifier::World(_) => unreachable!(), }; let camel = name.to_upper_camel_case(); uwriteln!( @@ -357,9 +369,13 @@ macro_rules! {macro_name} {{ uwriteln!(self.src, "}}"); } - pub fn generate_imports<'a>(&mut self, funcs: impl Iterator) { + pub fn generate_imports<'a>( + &mut self, + funcs: impl Iterator, + interface: Option<&WorldKey>, + ) { for func in funcs { - self.generate_guest_import(func); + self.generate_guest_import(func, interface); } } @@ -459,12 +475,387 @@ macro_rules! {macro_name} {{ map.push((module, module_path)) } - fn generate_guest_import(&mut self, func: &Function) { + fn generate_payloads(&mut self, prefix: &str, func: &Function, interface: Option<&WorldKey>) { + for (index, ty) in func + .find_futures_and_streams(self.resolve) + .into_iter() + .enumerate() + { + let module = format!( + "{prefix}{}", + interface + .map(|name| self.resolve.name_world_key(name)) + .unwrap_or_else(|| "$root".into()) + ); + let func_name = &func.name; + let type_mode = TypeMode { + lifetime: None, + lists_borrowed: false, + style: TypeOwnershipStyle::Owned, + }; + let stream_and_future_support = self.path_to_stream_and_future_support(); + let async_support = self.path_to_async_support(); + + match &self.resolve.types[ty].kind { + TypeDefKind::Future(payload_type) => { + let (name, full_name) = if let Some(payload_type) = payload_type { + ( + { + let old = mem::take(&mut self.src); + self.print_ty(&payload_type, type_mode); + String::from(mem::replace(&mut self.src, old)) + }, + { + let old = mem::take(&mut self.src); + let old_identifier = + mem::replace(&mut self.identifier, Identifier::None); + self.print_ty(&payload_type, type_mode); + self.identifier = old_identifier; + String::from(mem::replace(&mut self.src, old)) + }, + ) + } else { + ("()".into(), "()".into()) + }; + + if self.gen.future_payloads_emitted.insert(full_name) { + let (size, align) = if let Some(payload_type) = payload_type { + ( + self.sizes.size(payload_type), + self.sizes.align(payload_type), + ) + } else { + ( + ArchitectureSize { + bytes: 0, + pointers: 0, + }, + Alignment::default(), + ) + }; + let size = size.size_wasm32(); + let align = align.align_wasm32(); + let (lower, lift) = if let Some(payload_type) = payload_type { + let lower = + self.lower_to_memory("address", "value", &payload_type, &module); + let lift = + self.lift_from_memory("address", "value", &payload_type, &module); + (lower, lift) + } else { + (String::new(), "let value = ();\n".into()) + }; + + uwriteln!( + self.src, + r#" +impl {stream_and_future_support}::FuturePayload for {name} {{ + fn new() -> u32 {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-new-{index}]{func_name}"] + fn new() -> u32; + }} + unsafe {{ new() }} + }} + }} + + async fn write(future: u32, value: Self) -> bool {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[repr(align({align}))] + struct Buffer([::core::mem::MaybeUninit::; {size}]); + 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; + }} + + unsafe {{ {async_support}::await_future_result(wit_import, future, address).await }} + }} + }} + + async fn read(future: u32) -> Option {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + struct Buffer([::core::mem::MaybeUninit::; {size}]); + let mut buffer = Buffer([::core::mem::MaybeUninit::uninit(); {size}]); + let address = buffer.0.as_mut_ptr() as *mut u8; + + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[async][future-read-{index}]{func_name}"] + fn wit_import(_: u32, _: *mut u8) -> u32; + }} + + if unsafe {{ {async_support}::await_future_result(wit_import, future, address).await }} {{ + {lift} + Some(value) + }} else {{ + None + }} + }} + }} + + fn close_writable(writer: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-close-writable-{index}]{func_name}"] + fn drop(_: u32, _: u32); + }} + unsafe {{ drop(writer, 0) }} + }} + }} + + fn close_readable(reader: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-close-readable-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(reader) }} + }} + }} +}} + "#, + ); + } + } + TypeDefKind::Stream(payload_type) => { + let name = { + let old = mem::take(&mut self.src); + self.print_ty(&payload_type, type_mode); + String::from(mem::replace(&mut self.src, old)) + }; + + let full_name = { + let old = mem::take(&mut self.src); + let old_identifier = mem::replace(&mut self.identifier, Identifier::None); + self.print_ty(&payload_type, type_mode); + self.identifier = old_identifier; + String::from(mem::replace(&mut self.src, old)) + }; + + if self.gen.stream_payloads_emitted.insert(full_name) { + let size = self.sizes.size(payload_type).size_wasm32(); + let align = self.sizes.align(payload_type).align_wasm32(); + let alloc = self.path_to_std_alloc_module(); + let (lower_address, lower, lift_address, lift) = + if stream_direct(payload_type) { + let lower_address = "let address = values.as_ptr() as _;".into(); + let lift_address = "let address = values.as_mut_ptr() as _;".into(); + ( + lower_address, + String::new(), + lift_address, + "let value = ();\n".into(), + ) + } else { + let address = format!( + "let address = {alloc}::alloc\ + ({alloc}::Layout::from_size_align_unchecked\ + ({size} * values.len(), {align}));" + ); + let lower = self.lower_to_memory( + "address", + "value", + &payload_type, + &module, + ); + let lower = format!( + r#" +for (index, value) in values.iter().enumerate() {{ + let address = address + (index * size); + {lower} +}} + "# + ); + let lift = self.lift_from_memory( + "address", + "value", + &payload_type, + &module, + ); + let lift = format!( + r#" +for (index, dst) in values.iter_mut().enumerate() {{ + let address = address + (index * size); + {lift} + *dst = value; +}} + "# + ); + (address.clone(), lower, address, lift) + }; + + uwriteln!( + self.src, + r#" +impl {stream_and_future_support}::StreamPayload for {name} {{ + fn new() -> u32 {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-new-{index}]{func_name}"] + fn new() -> u32; + }} + unsafe {{ new() }} + }} + }} + + async fn write(stream: u32, values: &[Self]) -> Option {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + {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; + }} + + unsafe {{ + {async_support}::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await + }} + }} + }} + + async fn read(stream: u32, values: &mut [::core::mem::MaybeUninit::]) -> Option {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + {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; + }} + + let count = unsafe {{ + {async_support}::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await + }}; + #[allow(unused)] + if let Some(count) = count {{ + {lift} + }} + count + }} + }} + + fn close_writable(writer: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-close-writable-{index}]{func_name}"] + fn drop(_: u32, _: u32); + }} + unsafe {{ drop(writer, 0) }} + }} + }} + + fn close_readable(reader: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-close-readable-{index}]{func_name}"] + fn drop(_: u32); + }} + unsafe {{ drop(reader) }} + }} + }} +}} + "# + ); + } + } + _ => unreachable!(), + } + } + } + + fn generate_guest_import(&mut self, func: &Function, interface: Option<&WorldKey>) { if self.gen.skip.contains(&func.name) { return; } - let mut sig = FnSig::default(); + self.generate_payloads("[import-payload]", func, interface); + + let async_ = match &self.gen.opts.async_ { + AsyncConfig::None => false, + AsyncConfig::All => true, + AsyncConfig::Some { imports, .. } => imports.contains(&if let Some(key) = interface { + format!("{}#{}", self.resolve.name_world_key(key), func.name) + } else { + func.name.clone() + }), + }; + let mut sig = FnSig { + async_, + ..Default::default() + }; match func.kind { FunctionKind::Freestanding => {} FunctionKind::Method(id) | FunctionKind::Static(id) | FunctionKind::Constructor(id) => { @@ -479,17 +870,53 @@ macro_rules! {macro_name} {{ } } self.src.push_str("#[allow(unused_unsafe, clippy::all)]\n"); - let params = self.print_signature(func, false, &sig); + let params = self.print_signature(func, false, &sig, true); self.src.push_str("{\n"); self.src.push_str("unsafe {\n"); - let mut f = FunctionBindgen::new(self, params); + self.generate_guest_import_body(&self.wasm_import_module, func, params, async_); + + self.src.push_str("}\n"); + self.src.push_str("}\n"); + + match func.kind { + FunctionKind::Freestanding => {} + FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::Constructor(_) => { + self.src.push_str("}\n"); + } + } + } + + fn lower_to_memory(&mut self, address: &str, value: &str, ty: &Type, module: &str) -> String { + let mut f = FunctionBindgen::new(self, Vec::new(), true, module); + abi::lower_to_memory(f.gen.resolve, &mut f, address.into(), value.into(), ty); + format!("unsafe {{ {} }}", String::from(f.src)) + } + + fn lift_from_memory(&mut self, address: &str, value: &str, ty: &Type, module: &str) -> String { + let mut f = FunctionBindgen::new(self, Vec::new(), true, module); + let result = abi::lift_from_memory(f.gen.resolve, &mut f, address.into(), ty); + format!( + "let {value} = unsafe {{ {}\n{result} }};", + String::from(f.src) + ) + } + + fn generate_guest_import_body( + &mut self, + module: &str, + func: &Function, + params: Vec, + async_: bool, + ) { + let mut f = FunctionBindgen::new(self, params, async_, module); abi::call( f.gen.resolve, AbiVariant::GuestImport, LiftLower::LowerArgsLiftResults, func, &mut f, + async_, ); let FunctionBindgen { needs_cleanup_list, @@ -516,29 +943,28 @@ macro_rules! {macro_name} {{ ); } self.src.push_str(&String::from(src)); - - self.src.push_str("}\n"); - self.src.push_str("}\n"); - - match func.kind { - FunctionKind::Freestanding => {} - FunctionKind::Method(_) | FunctionKind::Static(_) | FunctionKind::Constructor(_) => { - self.src.push_str("}\n"); - } - } } - fn generate_guest_export(&mut self, func: &Function, trait_name: &str) { + fn generate_guest_export( + &mut self, + func: &Function, + interface: Option<&WorldKey>, + trait_name: &str, + async_: bool, + ) { let name_snake = func.name.to_snake_case().replace('.', "_"); + + self.generate_payloads("[export-payload]", func, interface); + uwrite!( self.src, "\ #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn _export_{name_snake}_cabi\ -", + ", ); - let params = self.print_export_sig(func); + let params = self.print_export_sig(func, async_); self.push_str(" {"); if !self.gen.opts.disable_run_ctors_once_workaround { @@ -557,13 +983,14 @@ macro_rules! {macro_name} {{ ); } - let mut f = FunctionBindgen::new(self, params); + let mut f = FunctionBindgen::new(self, params, async_, self.wasm_import_module); abi::call( f.gen.resolve, AbiVariant::GuestExport, LiftLower::LiftArgsLowerResults, func, &mut f, + async_, ); let FunctionBindgen { needs_cleanup_list, @@ -579,20 +1006,32 @@ macro_rules! {macro_name} {{ self.src.push_str(&String::from(src)); self.src.push_str("}\n"); - if abi::guest_export_needs_post_return(self.resolve, func) { + if async_ { + let async_support = self.path_to_async_support(); + uwrite!( + self.src, + "\ + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __callback_{name_snake}(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 {{ + {async_support}::callback(ctx, event0, event1, event2) + }} + " + ); + } else if abi::guest_export_needs_post_return(self.resolve, func) { uwrite!( self.src, "\ #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn __post_return_{name_snake}\ -" + " ); let params = self.print_post_return_sig(func); self.src.push_str("{\n"); - let mut f = FunctionBindgen::new(self, params); - abi::post_return(f.gen.resolve, func, &mut f); + let mut f = FunctionBindgen::new(self, params, async_, self.wasm_import_module); + abi::post_return(f.gen.resolve, func, &mut f, async_); let FunctionBindgen { needs_cleanup_list, src, @@ -606,14 +1045,26 @@ macro_rules! {macro_name} {{ } } - fn generate_raw_cabi_export(&mut self, func: &Function, ty: &str, path_to_self: &str) { + fn generate_raw_cabi_export( + &mut self, + func: &Function, + ty: &str, + path_to_self: &str, + async_: bool, + ) { let name_snake = func.name.to_snake_case().replace('.', "_"); let wasm_module_export_name = match self.identifier { Identifier::Interface(_, key) => Some(self.resolve.name_world_key(key)), Identifier::World(_) => None, + Identifier::None => unreachable!(), }; let export_prefix = self.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]{export_name}") + } else { + export_name.to_string() + }; uwrite!( self.src, "\ @@ -622,7 +1073,7 @@ macro_rules! {macro_name} {{ ", ); - let params = self.print_export_sig(func); + let params = self.print_export_sig(func, async_); self.push_str(" {\n"); uwriteln!( self.src, @@ -631,8 +1082,18 @@ macro_rules! {macro_name} {{ ); self.push_str("}\n"); - if abi::guest_export_needs_post_return(self.resolve, func) { - let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); + let export_prefix = self.gen.opts.export_prefix.as_deref().unwrap_or(""); + if async_ { + uwrite!( + self.src, + "\ + #[export_name = \"{export_prefix}[callback]{export_name}\"] + unsafe extern \"C\" fn _callback_{name_snake}(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 {{ + {path_to_self}::__callback_{name_snake}(ctx, event0, event1, event2) + }} + " + ); + } else if abi::guest_export_needs_post_return(self.resolve, func) { uwrite!( self.src, "\ @@ -651,7 +1112,7 @@ macro_rules! {macro_name} {{ } } - fn print_export_sig(&mut self, func: &Function) -> Vec { + fn print_export_sig(&mut self, func: &Function, async_: bool) -> Vec { self.src.push_str("("); let sig = self.resolve.wasm_signature(AbiVariant::GuestExport, func); let mut params = Vec::new(); @@ -662,13 +1123,18 @@ macro_rules! {macro_name} {{ } self.src.push_str(")"); - match sig.results.len() { - 0 => {} - 1 => { - uwrite!(self.src, " -> {}", wasm_type(sig.results[0])); + if async_ { + self.push_str(" -> *mut u8"); + } else { + match sig.results.len() { + 0 => {} + 1 => { + uwrite!(self.src, " -> {}", wasm_type(sig.results[0])); + } + _ => unimplemented!(), } - _ => unimplemented!(), } + params } @@ -708,7 +1174,7 @@ macro_rules! {macro_name} {{ let resource_methods = funcs.remove(&Some(*id)).unwrap_or(Vec::new()); let trait_name = format!("{path}::Guest{camel}"); - self.generate_stub_impl(&trait_name, "", &resource_methods); + self.generate_stub_impl(&trait_name, "", &resource_methods, interface); } format!("{path}::Guest") } @@ -719,7 +1185,7 @@ macro_rules! {macro_name} {{ }; if !root_methods.is_empty() || !extra_trait_items.is_empty() { - self.generate_stub_impl(&guest_trait, &extra_trait_items, &root_methods); + self.generate_stub_impl(&guest_trait, &extra_trait_items, &root_methods, interface); } } @@ -728,6 +1194,7 @@ macro_rules! {macro_name} {{ trait_name: &str, extra_trait_items: &str, funcs: &[&Function], + interface: Option<(InterfaceId, &WorldKey)>, ) { uwriteln!(self.src, "impl {trait_name} for Stub {{"); self.src.push_str(extra_trait_items); @@ -736,7 +1203,19 @@ macro_rules! {macro_name} {{ if self.gen.skip.contains(&func.name) { continue; } + let async_ = match &self.gen.opts.async_ { + AsyncConfig::None => false, + AsyncConfig::All => true, + AsyncConfig::Some { exports, .. } => { + exports.contains(&if let Some((_, key)) = interface { + format!("{}#{}", self.resolve.name_world_key(key), func.name) + } else { + func.name.clone() + }) + } + }; let mut sig = FnSig { + async_, use_item_name: true, private: true, ..Default::default() @@ -745,8 +1224,14 @@ macro_rules! {macro_name} {{ sig.self_arg = Some("&self".into()); sig.self_is_first_param = true; } - self.print_signature(func, true, &sig); - self.src.push_str("{ unreachable!() }\n"); + self.print_signature(func, true, &sig, false); + let call = if async_ { + let async_support = self.path_to_async_support(); + format!("{{ #[allow(unreachable_code)]{async_support}::futures::future::ready(unreachable!()) }}\n") + } else { + "{ unreachable!() }\n".into() + }; + self.src.push_str(&call); } self.src.push_str("}\n"); @@ -803,12 +1288,22 @@ macro_rules! {macro_name} {{ // } } - fn print_signature(&mut self, func: &Function, params_owned: bool, sig: &FnSig) -> Vec { - let params = self.print_docs_and_params(func, params_owned, sig); + fn print_signature( + &mut self, + func: &Function, + params_owned: bool, + sig: &FnSig, + use_async_sugar: bool, + ) -> Vec { + let params = self.print_docs_and_params(func, params_owned, sig, use_async_sugar); if let FunctionKind::Constructor(_) = &func.kind { - self.push_str(" -> Self") + self.push_str(if sig.async_ && !use_async_sugar { + " -> impl ::core::future::Future" + } else { + " -> Self" + }) } else { - self.print_results(&func.results); + self.print_results(&func.results, sig.async_ && !use_async_sugar); } params } @@ -818,6 +1313,7 @@ macro_rules! {macro_name} {{ func: &Function, params_owned: bool, sig: &FnSig, + use_async_sugar: bool, ) -> Vec { self.rustdoc(&func.docs); self.rustdoc_params(&func.params, "Parameters"); @@ -830,7 +1326,7 @@ macro_rules! {macro_name} {{ if sig.unsafe_ { self.push_str("unsafe "); } - if sig.async_ { + if sig.async_ && use_async_sugar { self.push_str("async "); } self.push_str("fn "); @@ -920,18 +1416,24 @@ macro_rules! {macro_name} {{ params } - fn print_results(&mut self, results: &Results) { + fn print_results(&mut self, results: &Results, async_: bool) { + self.push_str(" -> "); + if async_ { + self.push_str("impl ::core::future::Future {} + 0 => { + self.push_str("()"); + } 1 => { - self.push_str(" -> "); let ty = results.iter_types().next().unwrap(); let mode = self.type_mode_for(ty, TypeOwnershipStyle::Owned, "'INVALID"); assert!(mode.lifetime.is_none()); self.print_ty(ty, mode); } _ => { - self.push_str(" -> ("); + self.push_str("("); for ty in results.iter_types() { let mode = self.type_mode_for(ty, TypeOwnershipStyle::Owned, "'INVALID"); assert!(mode.lifetime.is_none()); @@ -941,6 +1443,10 @@ macro_rules! {macro_name} {{ self.push_str(")") } } + + if async_ { + self.push_str("> + 'static"); + } } /// Calculates the `TypeMode` to be used for the `ty` specified. @@ -1868,6 +2374,17 @@ macro_rules! {macro_name} {{ self.path_from_runtime_module(RuntimeItem::StdAllocModule, "alloc") } + pub fn path_to_stream_and_future_support(&mut self) -> String { + self.path_from_runtime_module( + RuntimeItem::StreamAndFutureSupport, + "stream_and_future_support", + ) + } + + pub fn path_to_async_support(&mut self) -> String { + "::wit_bindgen_rt::async_support".into() + } + fn path_from_runtime_module( &mut self, item: RuntimeItem, @@ -1925,11 +2442,12 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { }} "# ); - self.wasm_import_module.unwrap().to_string() + self.wasm_import_module.to_string() } else { let module = match self.identifier { Identifier::Interface(_, key) => self.resolve.name_world_key(key), Identifier::World(_) => unimplemented!("resource exports from worlds"), + Identifier::None => unreachable!(), }; let box_path = self.path_to_box(); uwriteln!( @@ -2181,6 +2699,48 @@ impl<'a> {camel}Borrow<'a>{{ } } + fn type_future(&mut self, _id: TypeId, name: &str, ty: &Option, docs: &Docs) { + let stream_and_future_support = self.path_to_stream_and_future_support(); + let mode = TypeMode { + style: TypeOwnershipStyle::Owned, + lists_borrowed: false, + lifetime: None, + }; + self.rustdoc(docs); + self.push_str(&format!("pub type {}", name.to_upper_camel_case())); + self.print_generics(mode.lifetime); + self.push_str(" = "); + self.push_str(&format!("{stream_and_future_support}::FutureReader<")); + self.print_optional_ty(ty.as_ref(), mode); + self.push_str(">"); + self.push_str(";\n"); + } + + fn type_stream(&mut self, _id: TypeId, name: &str, ty: &Type, docs: &Docs) { + let stream_and_future_support = self.path_to_stream_and_future_support(); + let mode = TypeMode { + style: TypeOwnershipStyle::Owned, + lists_borrowed: false, + lifetime: None, + }; + self.rustdoc(docs); + self.push_str(&format!("pub type {}", name.to_upper_camel_case())); + self.print_generics(mode.lifetime); + self.push_str(" = "); + self.push_str(&format!("{stream_and_future_support}::StreamReader<")); + self.print_ty(ty, mode); + self.push_str(">"); + self.push_str(";\n"); + } + + fn type_error_context(&mut self, _id: TypeId, name: &str, docs: &Docs) { + let async_support = self.path_to_async_support(); + self.rustdoc(docs); + self.push_str(&format!("pub type {} = ", name.to_upper_camel_case())); + self.push_str(&format!("{async_support}::ErrorContext")); + self.push_str(";\n"); + } + fn type_builtin(&mut self, _id: TypeId, name: &str, ty: &Type, docs: &Docs) { self.rustdoc(docs); self.src @@ -2202,7 +2762,7 @@ impl<'a, 'b> wit_bindgen_core::AnonymousTypeGenerator<'a> for AnonTypeGenerator< self.resolve } - fn anonymous_typ_type(&mut self, _id: TypeId, ty: &Type, _docs: &Docs) { + fn anonymous_type_type(&mut self, _id: TypeId, ty: &Type, _docs: &Docs) { self.interface.print_ty(ty, self.mode); } @@ -2270,18 +2830,52 @@ impl<'a, 'b> wit_bindgen_core::AnonymousTypeGenerator<'a> for AnonTypeGenerator< } fn anonymous_type_future(&mut self, _id: TypeId, ty: &Option, _docs: &Docs) { - self.interface.push_str("Future<"); - self.interface.print_optional_ty(ty.as_ref(), self.mode); + let stream_and_future_support = self.interface.path_to_stream_and_future_support(); + let mode = TypeMode { + style: TypeOwnershipStyle::Owned, + lists_borrowed: false, + lifetime: None, + }; + self.interface + .push_str(&format!("{stream_and_future_support}::FutureReader<")); + self.interface.print_optional_ty(ty.as_ref(), mode); self.interface.push_str(">"); } - fn anonymous_type_stream(&mut self, _id: TypeId, stream: &Stream, _docs: &Docs) { - self.interface.push_str("Stream<"); - self.interface - .print_optional_ty(stream.element.as_ref(), self.mode); - self.interface.push_str(","); + fn anonymous_type_stream(&mut self, _id: TypeId, ty: &Type, _docs: &Docs) { + let stream_and_future_support = self.interface.path_to_stream_and_future_support(); + let mode = TypeMode { + style: TypeOwnershipStyle::Owned, + lists_borrowed: false, + lifetime: None, + }; self.interface - .print_optional_ty(stream.end.as_ref(), self.mode); + .push_str(&format!("{stream_and_future_support}::StreamReader<")); + self.interface.print_ty(ty, mode); self.interface.push_str(">"); } + + fn anonymous_type_error_context(&mut self) { + let async_support = self.interface.path_to_async_support(); + self.interface + .push_str(&format!("{async_support}::ErrorContext")); + } +} + +fn stream_direct(ty: &Type) -> bool { + // TODO: might be able to return `true` for other types if the generated Rust versions of those types are + // guaranteed to be safely transmutable to and from their lowered form. + matches!( + ty, + Type::U8 + | Type::S8 + | Type::U16 + | Type::S16 + | Type::U32 + | Type::S32 + | Type::U64 + | Type::S64 + | Type::F32 + | Type::F64 + ) } diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 50ba0b3d7..22efce8f8 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -45,8 +45,12 @@ struct RustWasm { rt_module: IndexSet, export_macros: Vec<(String, String)>, + /// Interface names to how they should be generated with: GenerationConfiguration, + + future_payloads_emitted: HashSet, + stream_payloads_emitted: HashSet, } #[derive(Default)] @@ -98,6 +102,7 @@ enum RuntimeItem { AsF64, ResourceType, BoxType, + StreamAndFutureSupport, } #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -118,6 +123,52 @@ fn parse_with(s: &str) -> Result<(String, WithOption), String> { Ok((k.to_string(), v)) } +#[derive(Default, Debug, Clone)] +pub enum AsyncConfig { + #[default] + None, + Some { + imports: Vec, + exports: Vec, + }, + All, +} + +#[cfg(feature = "clap")] +fn parse_async(s: &str) -> Result { + Ok(match s { + "none" => AsyncConfig::None, + "all" => AsyncConfig::All, + _ => { + if let Some(values) = s.strip_prefix("some=") { + let mut imports = Vec::new(); + let mut exports = Vec::new(); + for value in values.split(',') { + let error = || { + Err(format!( + "expected string of form `import:` or `export:`; got `{value}`" + )) + }; + if let Some((k, v)) = value.split_once(":") { + match k { + "import" => imports.push(v.into()), + "export" => exports.push(v.into()), + _ => return error(), + } + } else { + return error(); + } + } + AsyncConfig::Some { imports, exports } + } else { + return Err(format!( + "expected string of form `none`, `all`, or `some=[,...]`; got `{s}`" + )); + } + } + }) +} + #[derive(Default, Debug, Clone)] #[cfg_attr(feature = "clap", derive(clap::Args))] pub struct Opts { @@ -235,6 +286,18 @@ pub struct Opts { /// library-based usage of `generate!` prone to breakage. #[cfg_attr(feature = "clap", arg(long))] pub disable_custom_section_link_helpers: bool, + + /// Determines which functions to lift or lower `async`, if any. + /// + /// Accepted values are: + /// - none + /// - all + /// - some=[,...], where each is of the form: + /// - import: or + /// - export: + #[cfg_attr(all(feature = "clap", feature = "async"), arg(long = "async", value_parser = parse_async))] + #[cfg_attr(all(feature = "clap", not(feature = "async")), skip)] + pub async_: AsyncConfig, } impl Opts { @@ -254,7 +317,7 @@ impl RustWasm { fn interface<'a>( &'a mut self, identifier: Identifier<'a>, - wasm_import_module: Option<&'a str>, + wasm_import_module: &'a str, resolve: &'a Resolve, in_import: bool, ) -> InterfaceGenerator<'a> { @@ -383,6 +446,7 @@ impl RustWasm { } self.src.push_str("mod _rt {\n"); + self.src.push_str("#![allow(dead_code, clippy::all)]\n"); let mut emitted = IndexSet::new(); while !self.rt_module.is_empty() { for item in mem::take(&mut self.rt_module) { @@ -392,6 +456,10 @@ impl RustWasm { } } self.src.push_str("}\n"); + if emitted.contains(&RuntimeItem::StreamAndFutureSupport) { + self.src + .push_str("#[allow(unused_imports)]\npub use _rt::stream_and_future_support;\n"); + } } fn emit_runtime_item(&mut self, item: RuntimeItem) { @@ -624,6 +692,13 @@ impl Drop for Resource { "#, ); } + + RuntimeItem::StreamAndFutureSupport => { + self.src.push_str("pub mod stream_and_future_support {"); + self.src + .push_str(include_str!("stream_and_future_support.rs")); + self.src.push_str("}"); + } } } @@ -797,6 +872,7 @@ macro_rules! __export_{world_name}_impl {{ .unwrap(); self.src.push_str("#[doc(hidden)]\n"); + self.src.push_str("#[allow(clippy::octal_escapes)]\n"); self.src.push_str(&format!( "pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; {}] = *b\"\\\n", component_type.len() @@ -972,7 +1048,7 @@ impl WorldGenerator for RustWasm { let wasm_import_module = resolve.name_world_key(name); let mut gen = self.interface( Identifier::Interface(id, name), - Some(&wasm_import_module), + &wasm_import_module, resolve, true, ); @@ -982,7 +1058,7 @@ impl WorldGenerator for RustWasm { } gen.types(id); - gen.generate_imports(resolve.interfaces[id].functions.values()); + gen.generate_imports(resolve.interfaces[id].functions.values(), Some(name)); let docs = &resolve.interfaces[id].docs; @@ -1000,9 +1076,9 @@ impl WorldGenerator for RustWasm { ) { self.import_funcs_called = true; - let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true); + let mut gen = self.interface(Identifier::World(world), "$root", resolve, true); - gen.generate_imports(funcs.iter().map(|(_, func)| *func)); + gen.generate_imports(funcs.iter().map(|(_, func)| *func), None); let src = gen.finish(); self.src.push_str(&src); @@ -1016,7 +1092,13 @@ impl WorldGenerator for RustWasm { _files: &mut Files, ) -> Result<()> { self.interface_last_seen_as_import.insert(id, false); - let mut gen = self.interface(Identifier::Interface(id, name), None, resolve, false); + let wasm_import_module = format!("[export]{}", resolve.name_world_key(name)); + let mut gen = self.interface( + Identifier::Interface(id, name), + &wasm_import_module, + resolve, + false, + ); let (snake, module_path) = gen.start_append_submodule(name); if gen.gen.name_interface(resolve, id, name, true)? { return Ok(()); @@ -1033,7 +1115,12 @@ impl WorldGenerator for RustWasm { if self.opts.stubs { let world_id = self.world.unwrap(); - let mut gen = self.interface(Identifier::World(world_id), None, resolve, false); + let mut gen = self.interface( + Identifier::World(world_id), + &wasm_import_module, + resolve, + false, + ); gen.generate_stub(Some((id, name)), resolve.interfaces[id].functions.values()); let stub = gen.finish(); self.src.push_str(&stub); @@ -1048,14 +1135,14 @@ impl WorldGenerator for RustWasm { funcs: &[(&str, &Function)], _files: &mut Files, ) -> Result<()> { - let mut gen = self.interface(Identifier::World(world), None, resolve, false); + let mut gen = self.interface(Identifier::World(world), "[export]$root", resolve, false); let macro_name = gen.generate_exports(None, funcs.iter().map(|f| f.1))?; let src = gen.finish(); self.src.push_str(&src); self.export_macros.push((macro_name, String::new())); if self.opts.stubs { - let mut gen = self.interface(Identifier::World(world), None, resolve, false); + let mut gen = self.interface(Identifier::World(world), "[export]$root", resolve, false); gen.generate_stub(None, funcs.iter().map(|f| f.1)); let stub = gen.finish(); self.src.push_str(&stub); @@ -1070,7 +1157,7 @@ impl WorldGenerator for RustWasm { types: &[(&str, TypeId)], _files: &mut Files, ) { - let mut gen = self.interface(Identifier::World(world), Some("$root"), resolve, true); + let mut gen = self.interface(Identifier::World(world), "$root", resolve, true); for (name, ty) in types { gen.define_type(name, *ty); } @@ -1220,6 +1307,7 @@ fn compute_module_path(name: &WorldKey, resolve: &Resolve, is_export: bool) -> V } enum Identifier<'a> { + None, World(WorldId), Interface(InterfaceId, &'a WorldKey), } diff --git a/crates/rust/src/stream_and_future_support.rs b/crates/rust/src/stream_and_future_support.rs new file mode 100644 index 000000000..ef1f7b722 --- /dev/null +++ b/crates/rust/src/stream_and_future_support.rs @@ -0,0 +1,546 @@ +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: Sized + 'static { + fn new() -> u32; + async fn write(future: u32, value: Self) -> bool; + async fn read(future: u32) -> Option; + fn close_writable(future: u32); + fn close_readable(future: u32); +} + +/// Represents the writable end of a Component Model `future`. +pub struct FutureWriter { + handle: u32, + _phantom: PhantomData, +} + +impl fmt::Debug for FutureWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureWriter") + .field("handle", &self.handle) + .finish() + } +} + +impl FutureWriter { + /// Write the specified value to this `future`. + pub async fn write(self, v: T) { + async_support::with_entry(self.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(self.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>> + } + 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(self.handle, v).map(drop)), + }, + }) + .await; + } +} + +impl Drop for FutureWriter { + 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 the readable end of a Component Model `future`. +pub struct FutureReader { + handle: u32, + _phantom: PhantomData, +} + +impl fmt::Debug for FutureReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureReader") + .field("handle", &self.handle) + .finish() + } +} + +impl FutureReader { + #[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 IntoFuture for FutureReader { + type Output = Option; + type IntoFuture = Pin + 'static>>; + + /// 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 { + async_support::with_entry(self.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(self.handle).await }) + as Pin>>, + 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 Drop for FutureReader { + 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; + async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; + fn close_writable(future: u32); + fn close_readable(future: u32); +} + +/// Represents the writable end of a Component Model `stream`. +pub struct StreamWriter { + handle: u32, + future: Option + 'static>>>, + _phantom: PhantomData, +} + +impl fmt::Debug for StreamWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamWriter") + .field("handle", &self.handle) + .finish() + } +} + +impl Sink> for StreamWriter { + type Error = Infallible; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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) -> 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); + 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 { + Poll::Ready(()) + } + } + Handle::LocalReady(..) => Poll::Pending, + Handle::LocalClosed => 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; + 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; + } + } + })); + } + }, + }); + Ok(()) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } +} + +impl Drop for StreamWriter { + fn drop(&mut self) { + if self.future.is_some() { + todo!("gracefully handle `StreamWriter::drop` when a write is in progress by calling `stream.cancel-write`"); + } + + 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 the readable end of a Component Model `stream`. +pub struct StreamReader { + handle: u32, + future: Option>> + 'static>>>, + _phantom: PhantomData, +} + +impl fmt::Debug for StreamReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamReader") + .field("handle", &self.handle) + .finish() + } +} + +impl StreamReader { + #[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 Stream for StreamReader { + type Item = Vec; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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; + Box::pin(async move { + let mut buffer = iter::repeat_with(MaybeUninit::uninit) + .take(ceiling(64 * 1024, mem::size_of::())) + .collect::>(); + + if let Some(count) = T::read(handle, &mut buffer).await { + buffer.truncate(count); + Some(unsafe { + mem::transmute::>, Vec>(buffer) + }) + } else { + None + } + }) as Pin>> + } + Handle::LocalOpen => { + let (tx, rx) = oneshot::channel(); + entry.insert(Handle::LocalWaiting(tx)); + Box::pin(rx.map(|v| v.ok().map(|v| *v.downcast().unwrap()))) + } + 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 Drop for StreamReader { + fn drop(&mut self) { + if self.future.is_some() { + todo!("gracefully handle `StreamReader::drop` when a read is in progress by calling `stream.cancel-read`"); + } + + 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() -> (FutureWriter, FutureReader) { + 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() -> (StreamWriter, StreamReader) { + 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 } +} diff --git a/crates/rust/tests/codegen.rs b/crates/rust/tests/codegen.rs index b5757b3df..a5ad0ef88 100644 --- a/crates/rust/tests/codegen.rs +++ b/crates/rust/tests/codegen.rs @@ -51,6 +51,19 @@ mod codegen_tests { #[test] fn works() {} } + + #[cfg(feature = "async")] + mod async_ { + wit_bindgen::generate!({ + path: $test, + stubs, + export_prefix: "[async-prefix]", + generate_all, + }); + + #[test] + fn works() {} + } } }; diff --git a/crates/rust/tests/codegen_no_std.rs b/crates/rust/tests/codegen_no_std.rs index 6c2db154d..362931c92 100644 --- a/crates/rust/tests/codegen_no_std.rs +++ b/crates/rust/tests/codegen_no_std.rs @@ -12,6 +12,16 @@ mod codegen_tests { macro_rules! codegen_test { (wasi_cli $name:tt $test:tt) => {}; (wasi_http $name:tt $test:tt) => {}; + + // TODO: We should be able to support streams, futures, and + // error-contexts in no_std mode if desired, but haven't done the work + // yet. + (streams $name:tt $test:tt) => {}; + (futures $name:tt $test:tt) => {}; + (resources_with_streams $name:tt $test:tt) => {}; + (resources_with_futures $name:tt $test:tt) => {}; + (error_context $name:tt $test:tt) => {}; + ($id:ident $name:tt $test:tt) => { mod $id { wit_bindgen::generate!({ diff --git a/crates/teavm-java/src/lib.rs b/crates/teavm-java/src/lib.rs index 1d0c90593..014bd6ace 100644 --- a/crates/teavm-java/src/lib.rs +++ b/crates/teavm-java/src/lib.rs @@ -501,6 +501,7 @@ impl InterfaceGenerator<'_> { LiftLower::LowerArgsLiftResults, func, &mut bindgen, + false, ); let src = bindgen.src; @@ -570,6 +571,7 @@ impl InterfaceGenerator<'_> { LiftLower::LiftArgsLowerResults, func, &mut bindgen, + false, ); assert!(!bindgen.needs_cleanup_list); @@ -623,7 +625,7 @@ impl InterfaceGenerator<'_> { (0..sig.results.len()).map(|i| format!("p{i}")).collect(), ); - abi::post_return(bindgen.gen.resolve, func, &mut bindgen); + abi::post_return(bindgen.gen.resolve, func, &mut bindgen, false); let src = bindgen.src; @@ -1068,6 +1070,21 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { self.type_name(&Type::Id(id)); } + fn type_future(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs) { + _ = (id, name, ty, docs); + todo!() + } + + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { + _ = (id, name, ty, docs); + todo!() + } + + fn type_error_context(&mut self, id: TypeId, name: &str, docs: &Docs) { + _ = (id, name, docs); + todo!() + } + fn type_builtin(&mut self, _id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) { unimplemented!(); } @@ -2028,6 +2045,21 @@ impl Bindgen for FunctionBindgen<'_, '_> { "Memory.free(org.teavm.interop.Address.fromInt({address}), ({length}) * {size}, {align});" ); } + + Instruction::Flush { amt } => { + results.extend(operands.iter().take(*amt).map(|v| v.clone())); + } + + Instruction::AsyncMalloc { .. } + | Instruction::AsyncPostCallInterface { .. } + | Instruction::AsyncCallReturn { .. } + | Instruction::FutureLower { .. } + | Instruction::FutureLift { .. } + | Instruction::StreamLower { .. } + | Instruction::StreamLift { .. } + | Instruction::ErrorContextLower { .. } + | Instruction::ErrorContextLift { .. } + | Instruction::AsyncCallWasm { .. } => todo!(), } } diff --git a/crates/teavm-java/tests/codegen.rs b/crates/teavm-java/tests/codegen.rs index 16d3f107b..ab637cf04 100644 --- a/crates/teavm-java/tests/codegen.rs +++ b/crates/teavm-java/tests/codegen.rs @@ -30,6 +30,14 @@ macro_rules! codegen_test { (issue929_no_export $name:tt $test:tt) => {}; (issue929_only_methods $name:tt $test:tt) => {}; + // TODO: implement support for stream, future, and error-context, and then + // remove these lines: + (streams $name:tt $test:tt) => {}; + (futures $name:tt $test:tt) => {}; + (resources_with_streams $name:tt $test:tt) => {}; + (resources_with_futures $name:tt $test:tt) => {}; + (error_context $name:tt $test:tt) => {}; + ($id:ident $name:tt $test:tt) => { #[test] fn $id() { diff --git a/crates/test-helpers/src/lib.rs b/crates/test-helpers/src/lib.rs index 1129238bb..1af51bcc9 100644 --- a/crates/test-helpers/src/lib.rs +++ b/crates/test-helpers/src/lib.rs @@ -32,9 +32,10 @@ pub fn test_directory(suite_name: &str, gen_name: &str, wit_name: &str) -> PathB /// Helper function to execute a process during tests and print informative /// information if it fails. pub fn run_command(cmd: &mut Command) { + let command = format!("{cmd:?}"); let output = cmd .output() - .expect("failed to run executable; is it installed"); + .unwrap_or_else(|e| panic!("failed to run executable: {e}; command was `{command}`")); if output.status.success() { return; diff --git a/tests/codegen/error-context.wit b/tests/codegen/error-context.wit new file mode 100644 index 000000000..d76f89685 --- /dev/null +++ b/tests/codegen/error-context.wit @@ -0,0 +1,12 @@ +package foo:foo; + +interface error-contexts { + type foo = error-context; + + bar: func(x: foo, y: error-context, z: future) -> result, error-context>; +} + +world foo { + import error-contexts; + export error-contexts; +} diff --git a/tests/codegen/futures.wit b/tests/codegen/futures.wit new file mode 100644 index 000000000..2d634a400 --- /dev/null +++ b/tests/codegen/futures.wit @@ -0,0 +1,87 @@ +package foo:foo; + +interface futures { + future-param: func(x: future); + future-u8-param: func(x: future); + future-u16-param: func(x: future); + future-u32-param: func(x: future); + future-u64-param: func(x: future); + future-s8-param: func(x: future); + future-s16-param: func(x: future); + future-s32-param: func(x: future); + future-s64-param: func(x: future); + future-f32-param: func(x: future); + future-f64-param: func(x: future); + + future-ret: func(x: future); + future-u8-ret: func() -> future; + future-u16-ret: func() -> future; + future-u32-ret: func() -> future; + future-u64-ret: func() -> future; + future-s8-ret: func() -> future; + future-s16-ret: func() -> future; + future-s32-ret: func() -> future; + future-s64-ret: func() -> future; + future-f32-ret: func() -> future; + future-f64-ret: func() -> future; + + tuple-future: func(x: future>) -> future>; + string-future-arg: func(a: future); + string-future-ret: func() -> future; + tuple-string-future: func(x: future>) -> future>; + string-future: func(x: future) -> future; + + record some-record { + x: string, + y: other-record, + z: future, + c1: u32, + c2: u64, + c3: s32, + c4: s64, + } + record other-record { + a1: u32, + a2: u64, + a3: s32, + a4: s64, + b: string, + c: future, + } + record-future: func(x: future) -> future; + record-future-reverse: func(x: future) -> future; + + variant some-variant { + a(string), + b, + c(u32), + d(future), + } + variant other-variant { + a, + b(u32), + c(string), + } + variant-future: func(x: future) -> future; + + type load-store-all-sizes = future>; + load-store-everything: func(a: load-store-all-sizes) -> load-store-all-sizes; +} + +world the-futures { + import futures; + export futures; +} diff --git a/tests/codegen/resources-with-futures.wit b/tests/codegen/resources-with-futures.wit new file mode 100644 index 000000000..33a2b2aeb --- /dev/null +++ b/tests/codegen/resources-with-futures.wit @@ -0,0 +1,17 @@ +package my:resources; + +interface with-futures { + resource x { + constructor(l: future); + get: func() -> future; + set: func(l: future); + etc: static func(l: future) -> future; + } + + foo: func(x: future) -> future; +} + +world resources { + import with-futures; + export with-futures; +} diff --git a/tests/codegen/resources-with-streams.wit b/tests/codegen/resources-with-streams.wit new file mode 100644 index 000000000..d9e3620fc --- /dev/null +++ b/tests/codegen/resources-with-streams.wit @@ -0,0 +1,17 @@ +package my:resources; + +interface with-streams { + resource x { + constructor(l: stream); + get: func() -> stream; + set: func(l: stream); + etc: static func(l: stream) -> stream; + } + + foo: func(x: stream) -> stream; +} + +world resources { + import with-streams; + export with-streams; +} diff --git a/tests/codegen/streams.wit b/tests/codegen/streams.wit new file mode 100644 index 000000000..fd00239b7 --- /dev/null +++ b/tests/codegen/streams.wit @@ -0,0 +1,85 @@ +package foo:foo; + +interface streams { + stream-u8-param: func(x: stream); + stream-u16-param: func(x: stream); + stream-u32-param: func(x: stream); + stream-u64-param: func(x: stream); + stream-s8-param: func(x: stream); + stream-s16-param: func(x: stream); + stream-s32-param: func(x: stream); + stream-s64-param: func(x: stream); + stream-f32-param: func(x: stream); + stream-f64-param: func(x: stream); + + stream-u8-ret: func() -> stream; + stream-u16-ret: func() -> stream; + stream-u32-ret: func() -> stream; + stream-u64-ret: func() -> stream; + stream-s8-ret: func() -> stream; + stream-s16-ret: func() -> stream; + stream-s32-ret: func() -> stream; + stream-s64-ret: func() -> stream; + stream-f32-ret: func() -> stream; + stream-f64-ret: func() -> stream; + + tuple-stream: func(x: stream>) -> stream>; + string-stream-arg: func(a: stream); + string-stream-ret: func() -> stream; + tuple-string-stream: func(x: stream>) -> stream>; + string-stream: func(x: stream) -> stream; + + record some-record { + x: string, + y: other-record, + z: stream, + c1: u32, + c2: u64, + c3: s32, + c4: s64, + } + record other-record { + a1: u32, + a2: u64, + a3: s32, + a4: s64, + b: string, + c: stream, + } + record-stream: func(x: stream) -> stream; + record-stream-reverse: func(x: stream) -> stream; + + variant some-variant { + a(string), + b, + c(u32), + d(stream), + } + variant other-variant { + a, + b(u32), + c(string), + } + variant-stream: func(x: stream) -> stream; + + type load-store-all-sizes = stream>; + load-store-everything: func(a: load-store-all-sizes) -> load-store-all-sizes; +} + +world the-streams { + import streams; + export streams; +} diff --git a/tests/runtime/flavorful/wasm.rs b/tests/runtime/flavorful/wasm.rs index 058eb2f74..82454cda9 100644 --- a/tests/runtime/flavorful/wasm.rs +++ b/tests/runtime/flavorful/wasm.rs @@ -44,7 +44,6 @@ impl Guest for Component { assert!(errno_result().is_err()); MyErrno::A.to_string(); - format!("{:?}", MyErrno::A); fn assert_error() {} assert_error::(); @@ -107,7 +106,6 @@ impl exports::test::flavorful::test::Guest for Component { fn errno_result() -> Result<(), MyErrno> { MyErrno::A.to_string(); - format!("{:?}", MyErrno::A); fn assert_error() {} assert_error::(); Err(MyErrno::B) diff --git a/tests/runtime/main.rs b/tests/runtime/main.rs index 0b3ae64f3..53bd38d7c 100644 --- a/tests/runtime/main.rs +++ b/tests/runtime/main.rs @@ -229,10 +229,10 @@ fn tests(name: &str, dir_name: &str) -> Result> { if compiler.ends_with("++") { cmd.arg("-Wno-deprecated"); } - println!("{:?}", cmd); + let command = format!("{cmd:?}"); let output = match cmd.output() { Ok(output) => output, - Err(e) => panic!("failed to spawn compiler: {}", e), + Err(e) => panic!("failed to spawn compiler: {e}; command was `{command}`"), }; if !output.status.success() { @@ -301,10 +301,10 @@ fn tests(name: &str, dir_name: &str) -> Result> { cmd.arg(&out_wasm); cmd.arg(format!("{snake}.go")); cmd.current_dir(&out_dir); - + let command = format!("{cmd:?}"); let output = match cmd.output() { Ok(output) => output, - Err(e) => panic!("failed to spawn compiler: {}", e), + Err(e) => panic!("failed to spawn compiler: {e}; command was `{command}`"), }; if !output.status.success() { @@ -571,10 +571,10 @@ fn tests(name: &str, dir_name: &str) -> Result> { .arg("--self-contained") .arg("-o") .arg(&out_wasm); - + let command = format!("{cmd:?}"); let output = match cmd.output() { Ok(output) => output, - Err(e) => panic!("failed to spawn compiler: {}", e), + Err(e) => panic!("failed to spawn compiler: {e}; command was `{command}`"), }; if !output.status.success() { @@ -735,9 +735,10 @@ fn tests(name: &str, dir_name: &str) -> Result> { .arg("/p:UseAppHost=false") .arg("-o") .arg(&out_wasm); + let command = format!("{cmd:?}"); let output = match cmd.output() { Ok(output) => output, - Err(e) => panic!("failed to spawn compiler: {}", e), + Err(e) => panic!("failed to spawn compiler: {e}; command was `{command}`"), }; if !output.status.success() { From 85702e77aeb0baac331565e8ed9e4631d308bd18 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 20 Nov 2024 22:02:41 +0100 Subject: [PATCH 392/672] post merge cleanup --- crates/c/src/lib.rs | 15 --------------- crates/core/src/abi.rs | 6 ++---- crates/core/src/symmetric.rs | 6 +++--- crates/cpp/src/lib.rs | 23 +++++++++++++++++------ crates/cpp/src/wamr.rs | 4 ++-- crates/go/src/bindgen.rs | 2 -- crates/go/src/interface.rs | 2 -- crates/rust/src/bindgen.rs | 34 ---------------------------------- crates/rust/src/lib.rs | 15 +-------------- 9 files changed, 25 insertions(+), 82 deletions(-) diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 223c8cd43..1576069ac 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -751,7 +751,6 @@ pub fn push_ty_name(resolve: &Resolve, ty: &Type, src: &mut String) { | TypeDefKind::Resource | TypeDefKind::Flags(_) | TypeDefKind::Enum(_) - | TypeDefKind::Error | TypeDefKind::Variant(_) => { unimplemented!() } @@ -1000,7 +999,6 @@ impl Return { TypeDefKind::ErrorContext => todo!("return_single for error-context"), TypeDefKind::Resource => todo!("return_single for resource"), TypeDefKind::Unknown => unreachable!(), - TypeDefKind::Error => todo!(), } self.retptrs.push(*orig_ty); @@ -1637,7 +1635,6 @@ impl InterfaceGenerator<'_> { self.free(&Type::Id(*id), "*ptr"); } TypeDefKind::Unknown => unreachable!(), - TypeDefKind::Error => todo!(), } if c_helpers_body_start == self.src.c_helpers.len() { self.src.c_helpers.as_mut_string().truncate(c_helpers_start); @@ -2114,7 +2111,6 @@ impl InterfaceGenerator<'_> { TypeDefKind::Type(ty) => self.contains_droppable_borrow(ty), TypeDefKind::Unknown => false, - TypeDefKind::Error => todo!(), } } else { false @@ -3081,16 +3077,6 @@ impl Bindgen for FunctionBindgen<'_, '_> { uwriteln!(self.src, "free({ptr});"); uwriteln!(self.src, "}}"); } - Instruction::Flush { amt } => { - for i in 0..*amt { - // no easy way to create a temporary? - results.push(operands[i].clone()); - } - } - - Instruction::Flush { amt } => { - results.extend(operands.iter().take(*amt).map(|v| v.clone())); - } Instruction::Flush { amt } => { results.extend(operands.iter().take(*amt).map(|v| v.clone())); @@ -3207,7 +3193,6 @@ pub fn is_arg_by_pointer(resolve: &Resolve, ty: &Type) -> bool { TypeDefKind::ErrorContext => todo!("is_arg_by_pointer for error-context"), TypeDefKind::Resource => todo!("is_arg_by_pointer for resource"), TypeDefKind::Unknown => unreachable!(), - TypeDefKind::Error => todo!(), }, Type::String => true, _ => false, diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 504e0500a..6853ec1bf 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -563,13 +563,13 @@ def_instruction! { /// This cannot be allocated on the (shadow-)stack since it needs to /// remain valid until the callee has finished using the buffers, which /// may be after we pop the current stack frame. - AsyncMalloc { size: usize, align: usize } : [0] => [1], + AsyncMalloc { size: ArchitectureSize, align: Alignment } : [0] => [1], /// Call an async-lowered import. /// /// `size` and `align` are used to deallocate the parameter area /// allocated using `AsyncMalloc` after the callee task returns a value. - AsyncCallWasm { name: &'a str, size: usize, align: usize } : [2] => [0], + AsyncCallWasm { name: &'a str, size: ArchitectureSize, align: Alignment } : [2] => [0], /// Generate code to run after `CallInterface` for an async-lifted export. /// @@ -907,8 +907,6 @@ 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); } diff --git a/crates/core/src/symmetric.rs b/crates/core/src/symmetric.rs index 3cc7c52e8..bf9e710e2 100644 --- a/crates/core/src/symmetric.rs +++ b/crates/core/src/symmetric.rs @@ -37,7 +37,7 @@ fn needs_dealloc2(resolve: &Resolve, tp: &Type) -> bool { TypeDefKind::List(_l) => true, TypeDefKind::Future(_) => todo!(), TypeDefKind::Stream(_) => todo!(), - TypeDefKind::Error => false, + TypeDefKind::ErrorContext => false, TypeDefKind::Type(tp) => needs_dealloc2(resolve, tp), TypeDefKind::Unknown => false, }, @@ -101,7 +101,7 @@ fn has_non_canonical_list2(resolve: &Resolve, ty: &Type, maybe: bool) -> bool { has_non_canonical_list2(resolve, ty, true) } } - TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Error => false, + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext => false, TypeDefKind::Type(ty) => has_non_canonical_list2(resolve, ty, maybe), TypeDefKind::Unknown => false, }, @@ -164,7 +164,7 @@ fn has_non_canonical_list_rust2(resolve: &Resolve, ty: &Type) -> bool { .map_or(false, |ty| has_non_canonical_list_rust2(resolve, ty)) } TypeDefKind::List(_ty) => true, - TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Error => false, + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext => false, TypeDefKind::Type(ty) => has_non_canonical_list_rust2(resolve, ty), TypeDefKind::Unknown => false, }, diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index a6da9f3e2..4d54bd361 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -956,7 +956,7 @@ impl CppInterfaceGenerator<'_> { TypeDefKind::Stream(_) => todo!("generate for stream"), TypeDefKind::Handle(_) => todo!("generate for handle"), TypeDefKind::Unknown => unreachable!(), - TypeDefKind::Error => todo!(), + TypeDefKind::ErrorContext => todo!(), } } @@ -2057,7 +2057,7 @@ impl CppInterfaceGenerator<'_> { TypeDefKind::Stream(_) => todo!(), TypeDefKind::Type(ty) => self.type_name(ty, from_namespace, flavor), TypeDefKind::Unknown => todo!(), - TypeDefKind::Error => todo!(), + TypeDefKind::ErrorContext => todo!(), }, } } @@ -2512,6 +2512,18 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> ) { todo!() } + + fn type_future(&mut self, _id: TypeId, _name: &str, _ty: &Option, _docs: &Docs) { + todo!() + } + + fn type_stream(&mut self, _id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) { + todo!() + } + + fn type_error_context(&mut self, _id: TypeId, _name: &str, _docs: &Docs) { + todo!() + } } struct CabiPostInformation { @@ -2686,7 +2698,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { _ => false, }, TypeDefKind::Unknown => todo!(), - TypeDefKind::Error => todo!(), + TypeDefKind::ErrorContext => todo!(), } } } @@ -3852,11 +3864,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::FutureLift { .. } => todo!(), abi::Instruction::StreamLower { .. } => todo!(), abi::Instruction::StreamLift { .. } => todo!(), - abi::Instruction::ErrorLower { .. } => todo!(), - abi::Instruction::ErrorLift { .. } => todo!(), + abi::Instruction::ErrorContextLower { .. } => todo!(), + abi::Instruction::ErrorContextLift { .. } => todo!(), abi::Instruction::AsyncMalloc { .. } => todo!(), abi::Instruction::AsyncCallWasm { .. } => todo!(), - abi::Instruction::AsyncCallStart { .. } => todo!(), abi::Instruction::AsyncPostCallInterface { .. } => todo!(), abi::Instruction::AsyncCallReturn { .. } => todo!(), abi::Instruction::Flush { amt } => { diff --git a/crates/cpp/src/wamr.rs b/crates/cpp/src/wamr.rs index c42db1f85..e82529b5a 100644 --- a/crates/cpp/src/wamr.rs +++ b/crates/cpp/src/wamr.rs @@ -61,7 +61,7 @@ fn push_wamr(ty: &Type, resolve: &Resolve, params_str: &mut String) { TypeDefKind::Handle(_h) => { params_str.push('i'); } - TypeDefKind::Error => todo!(), + TypeDefKind::ErrorContext => todo!(), }, } } @@ -124,7 +124,7 @@ fn wamr_add_result(sig: &mut WamrSig, resolve: &Resolve, ty: &Type) { TypeDefKind::Handle(_h) => { sig.wamr_result = "i".into(); } - TypeDefKind::Error => todo!(), + TypeDefKind::ErrorContext => todo!(), }, } } diff --git a/crates/go/src/bindgen.rs b/crates/go/src/bindgen.rs index 94fa95607..853f97673 100644 --- a/crates/go/src/bindgen.rs +++ b/crates/go/src/bindgen.rs @@ -395,7 +395,6 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } TypeDefKind::Unknown => unreachable!(), - TypeDefKind::Error => todo!(), } } a => { @@ -681,7 +680,6 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } TypeDefKind::Unknown => unreachable!(), - TypeDefKind::Error => todo!(), } } a => { diff --git a/crates/go/src/interface.rs b/crates/go/src/interface.rs index 57a86480e..39c54aef1 100644 --- a/crates/go/src/interface.rs +++ b/crates/go/src/interface.rs @@ -347,7 +347,6 @@ impl InterfaceGenerator<'_> { src } TypeDefKind::Unknown => unreachable!(), - TypeDefKind::Error => todo!(), } } } @@ -681,7 +680,6 @@ impl InterfaceGenerator<'_> { TypeDefKind::Stream(_) => todo!("anonymous_type for stream"), TypeDefKind::ErrorContext => todo!("anonymous_type for error-context"), TypeDefKind::Unknown => unreachable!(), - TypeDefKind::Error => todo!(), } } diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 0abb09fbd..4f63fa191 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -953,40 +953,6 @@ impl Bindgen for FunctionBindgen<'_, '_> { ); } - Instruction::AsyncCallWasm { name, size, align } => { - let func = self.declare_import(name, &[WasmType::Pointer; 2], &[WasmType::I32]); - - let async_support = self.gen.path_to_async_support(); - let tmp = self.tmp(); - let layout = format!("layout{tmp}"); - let alloc = self.gen.path_to_std_alloc_module(); - self.push_str(&format!( - "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align});\n", - )); - let operands = operands.join(", "); - uwriteln!( - self.src, - "{async_support}::await_result({func}, {layout}, {operands}).await;" - ); - } - - Instruction::AsyncCallWasm { name, size, align } => { - let func = self.declare_import(name, &[WasmType::Pointer; 2], &[WasmType::I32]); - - let async_support = self.gen.path_to_async_support(); - let tmp = self.tmp(); - let layout = format!("layout{tmp}"); - let alloc = self.gen.path_to_std_alloc_module(); - self.push_str(&format!( - "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align});\n", - )); - let operands = operands.join(", "); - uwriteln!( - self.src, - "{async_support}::await_result({func}, {layout}, {operands}).await;" - ); - } - Instruction::CallInterface { func, .. } => { if self.async_ { let tmp = self.tmp(); diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index b9fd17e4d..1770361e2 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -467,7 +467,7 @@ impl RustWasm { // TODO: This is a hack because there are currently functions and types in the `async_support` module that // are useful to applications even if the generated bindings don't use it. We should probably move those // items to a library which the application can add as a dependency. - self.rt_module.insert(RuntimeItem::AsyncSupport); + self.rt_module.insert(RuntimeItem::StreamAndFutureSupport); if self.rt_module.is_empty() { return; @@ -733,19 +733,6 @@ impl Drop for Resource {{ )); } - RuntimeItem::AsyncSupport => { - self.src.push_str("pub mod async_support {"); - self.src.push_str(include_str!("async_support.rs")); - self.src.push_str("}"); - } - - RuntimeItem::StreamAndFutureSupport => { - self.src.push_str("pub mod stream_and_future_support {"); - self.src - .push_str(include_str!("stream_and_future_support.rs")); - self.src.push_str("}"); - } - RuntimeItem::StreamAndFutureSupport => { self.src.push_str("pub mod stream_and_future_support {"); self.src From bbbe3bc63c8df812dfe09e7940d367149fcf252f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 20 Nov 2024 22:59:09 +0100 Subject: [PATCH 393/672] fix test generation --- Cargo.lock | 2 ++ crates/test-rust-wasm/Cargo.toml | 1 + crates/test-rust-wasm/rust-xcrate-test/Cargo.toml | 1 + 3 files changed, 4 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 5362adebb..313e114e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1285,6 +1285,7 @@ dependencies = [ "futures", "once_cell", "wit-bindgen", + "wit-bindgen-rt", ] [[package]] @@ -1522,6 +1523,7 @@ dependencies = [ "once_cell", "rust-xcrate-test", "wit-bindgen", + "wit-bindgen-rt", ] [[package]] diff --git a/crates/test-rust-wasm/Cargo.toml b/crates/test-rust-wasm/Cargo.toml index fff9fb8f0..8123cbdbe 100644 --- a/crates/test-rust-wasm/Cargo.toml +++ b/crates/test-rust-wasm/Cargo.toml @@ -6,6 +6,7 @@ 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" diff --git a/crates/test-rust-wasm/rust-xcrate-test/Cargo.toml b/crates/test-rust-wasm/rust-xcrate-test/Cargo.toml index feea8df0b..f4b5a7e49 100644 --- a/crates/test-rust-wasm/rust-xcrate-test/Cargo.toml +++ b/crates/test-rust-wasm/rust-xcrate-test/Cargo.toml @@ -7,5 +7,6 @@ publish = false [dependencies] wit-bindgen = { path = '../../guest-rust' } +wit-bindgen-rt = { path = '../../guest-rust/rt' } futures = "0.3" once_cell = "1.20" From df459f77eda08ed471b18233095e0133b64aa60c Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Tue, 26 Nov 2024 20:09:46 -0700 Subject: [PATCH 394/672] remove async_support::poll_future It was both unsafe to use and intended only for testing (and not even good for that, it turns out). Signed-off-by: Joel Dice --- crates/guest-rust/rt/src/async_support.rs | 54 ----------------------- 1 file changed, 54 deletions(-) diff --git a/crates/guest-rust/rt/src/async_support.rs b/crates/guest-rust/rt/src/async_support.rs index f326f1cb6..a2a1a285e 100644 --- a/crates/guest-rust/rt/src/async_support.rs +++ b/crates/guest-rust/rt/src/async_support.rs @@ -409,60 +409,6 @@ pub fn block_on(future: impl Future + 'static) -> T { } } -fn task_poll(state: &mut FutureState) -> bool { - #[cfg(not(target_arch = "wasm32"))] - { - _ = state; - unreachable!(); - } - - #[cfg(target_arch = "wasm32")] - { - #[link(wasm_import_module = "$root")] - extern "C" { - #[link_name = "[task-poll]"] - fn poll(_: *mut i32) -> i32; - } - let mut payload = [0i32; 3]; - unsafe { - let got_event = poll(payload.as_mut_ptr()) != 0; - if got_event { - callback(state as *mut _ as _, payload[0], payload[1], payload[2]); - } - got_event - } - } -} - -/// Attempt to run the specified future to completion without blocking, -/// returning the result if it completes. -/// -/// This is similar to `block_on` except that it uses `task.poll` instead of -/// `task.wait` to check for progress on any in-progress calls to async-lowered -/// imports, returning `None` if one or more of those calls remain pending. -// TODO: refactor so `'static` bounds aren't necessary -pub fn poll_future(future: impl Future + 'static) -> Option { - let (tx, mut rx) = oneshot::channel(); - let state = &mut FutureState { - todo: 0, - tasks: Some( - [Box::pin(future.map(move |v| drop(tx.send(v)))) as BoxFuture] - .into_iter() - .collect(), - ), - }; - loop { - match unsafe { poll(state) } { - Poll::Ready(()) => break Some(rx.try_recv().unwrap().unwrap()), - Poll::Pending => { - if !task_poll(state) { - break None; - } - } - } - } -} - /// Call the `task.yield` canonical built-in function. /// /// This yields control to the host temporarily, allowing other tasks to make From 9e6ebb8341d32cc73871e869e63fe8d75a2e3631 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 3 Dec 2024 15:53:35 +0100 Subject: [PATCH 395/672] wasm-tools update (doesn't fix test failure) --- Cargo.lock | 80 ++++++++++++++-------------- crates/cpp/helper-types/wit-common.h | 3 ++ tests/runtime/results/wasm.new.cpp | 4 +- 3 files changed, 45 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index be3ef3f86..556fd47fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1509,10 +1509,10 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.221.2", "wit-bindgen-core", "wit-component", - "wit-parser 0.220.0", + "wit-parser 0.221.2", ] [[package]] @@ -1795,22 +1795,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebf48234b389415b226a4daef6562933d38c7b28a8b8f64c5c4130dad1561ab7" dependencies = [ "leb128", - "wasmparser 0.220.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmparser 0.220.0", ] [[package]] name = "wasm-encoder" -version = "0.220.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#aac81f80af8a8b75aa7b2c9cc51992c4753a0e6f" +version = "0.221.2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" dependencies = [ "leb128", - "wasmparser 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.221.2", ] [[package]] name = "wasm-metadata" -version = "0.220.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#aac81f80af8a8b75aa7b2c9cc51992c4753a0e6f" +version = "0.221.2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" dependencies = [ "anyhow", "indexmap", @@ -1818,8 +1818,8 @@ dependencies = [ "serde_derive", "serde_json", "spdx", - "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", - "wasmparser 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.221.2", + "wasmparser 0.221.2", ] [[package]] @@ -1848,8 +1848,8 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.220.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#aac81f80af8a8b75aa7b2c9cc51992c4753a0e6f" +version = "0.221.2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" dependencies = [ "bitflags", "hashbrown 0.15.1", @@ -1921,7 +1921,7 @@ dependencies = [ "wasmtime-slab", "wasmtime-versioned-export-macros", "wasmtime-winch", - "wat 1.220.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wat 1.220.0", "windows-sys 0.52.0", ] @@ -2176,19 +2176,19 @@ dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.220.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-encoder 0.220.0", ] [[package]] name = "wast" -version = "220.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#aac81f80af8a8b75aa7b2c9cc51992c4753a0e6f" +version = "221.0.2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.221.2", ] [[package]] @@ -2197,15 +2197,15 @@ version = "1.220.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de4f1d7d59614ba690541360102b995c4eb1b9ed373701d5102cc1a968b1c5a3" dependencies = [ - "wast 220.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wast 220.0.0", ] [[package]] name = "wat" -version = "1.220.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#aac81f80af8a8b75aa7b2c9cc51992c4753a0e6f" +version = "1.221.2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" dependencies = [ - "wast 220.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wast 221.0.2", ] [[package]] @@ -2435,11 +2435,11 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.221.2", "wasm-metadata", "wit-bindgen-core", "wit-component", - "wit-parser 0.220.0", + "wit-parser 0.221.2", ] [[package]] @@ -2450,8 +2450,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", - "wasmparser 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.221.2", + "wasmparser 0.221.2", "wasmtime", "wasmtime-wasi", "wit-bindgen-bridge", @@ -2465,7 +2465,7 @@ dependencies = [ "wit-bindgen-rust", "wit-bindgen-teavm-java", "wit-component", - "wit-parser 0.220.0", + "wit-parser 0.221.2", ] [[package]] @@ -2474,7 +2474,7 @@ version = "0.36.0" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser 0.220.0", + "wit-parser 0.221.2", ] [[package]] @@ -2485,7 +2485,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.221.2", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", @@ -2501,12 +2501,12 @@ dependencies = [ "heck 0.5.0", "indexmap", "test-helpers", - "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.221.2", "wasm-metadata", - "wasmparser 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.221.2", "wit-bindgen-core", "wit-component", - "wit-parser 0.220.0", + "wit-parser 0.221.2", ] [[package]] @@ -2601,8 +2601,8 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.220.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#aac81f80af8a8b75aa7b2c9cc51992c4753a0e6f" +version = "0.221.2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" dependencies = [ "anyhow", "bitflags", @@ -2611,11 +2611,11 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-encoder 0.221.2", "wasm-metadata", - "wasmparser 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", - "wat 1.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", - "wit-parser 0.220.0", + "wasmparser 0.221.2", + "wat 1.221.2", + "wit-parser 0.221.2", ] [[package]] @@ -2638,8 +2638,8 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.220.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#aac81f80af8a8b75aa7b2c9cc51992c4753a0e6f" +version = "0.221.2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" dependencies = [ "anyhow", "id-arena", @@ -2650,7 +2650,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.220.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.221.2", ] [[package]] diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h index 722f32c54..68957569b 100644 --- a/crates/cpp/helper-types/wit-common.h +++ b/crates/cpp/helper-types/wit-common.h @@ -63,4 +63,7 @@ template class ResourceTable { return std::move(result); } }; + +/// @brief Replaces void in the error position of a result +struct Void {}; } // namespace wit diff --git a/tests/runtime/results/wasm.new.cpp b/tests/runtime/results/wasm.new.cpp index a432bdbc4..68ea1e46a 100644 --- a/tests/runtime/results/wasm.new.cpp +++ b/tests/runtime/results/wasm.new.cpp @@ -47,10 +47,10 @@ std::expected exports::test::results::test::Va // } } -std::expected exports::test::results::test::EmptyError(uint32_t a) { +std::expected exports::test::results::test::EmptyError(uint32_t a) { return ::test::results::test::EmptyError(a); } std::expected, wit::string> exports::test::results::test::DoubleError(uint32_t a) { - return ::test::results::test::DoubleError(a) + return ::test::results::test::DoubleError(a); } From d3afd513fd07c200bda3709d7c0d7cf59557ccc6 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 5 Dec 2024 12:00:47 -0700 Subject: [PATCH 396/672] add stream/future read/write cancellation support Also, fix some issues with stream/future payload lifting/lowering which I _thought_ I had already tested but actually hadn't. Signed-off-by: Joel Dice --- crates/rust/src/interface.rs | 82 ++++- crates/rust/src/stream_and_future_support.rs | 363 +++++++++++++++---- 2 files changed, 366 insertions(+), 79 deletions(-) diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 24a6b81db..4fa7cfe59 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -537,7 +537,7 @@ macro_rules! {macro_name} {{ let align = align.align_wasm32(); let (lower, lift) = if let Some(payload_type) = payload_type { let lower = - self.lower_to_memory("address", "value", &payload_type, &module); + self.lower_to_memory("address", "&value", &payload_type, &module); let lift = self.lift_from_memory("address", "value", &payload_type, &module); (lower, lift) @@ -617,6 +617,40 @@ impl {stream_and_future_support}::FuturePayload for {name} {{ }} }} + fn cancel_write(writer: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-cancel-write-{index}]{func_name}"] + fn cancel(_: u32) -> u32; + }} + unsafe {{ cancel(writer) }}; + }} + }} + + fn cancel_read(reader: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[future-cancel-read-{index}]{func_name}"] + fn cancel(_: u32) -> u32; + }} + unsafe {{ cancel(reader) }}; + }} + }} + fn close_writable(writer: u32) {{ #[cfg(not(target_arch = "wasm32"))] {{ @@ -686,9 +720,9 @@ impl {stream_and_future_support}::FuturePayload for {name} {{ ) } else { let address = format!( - "let address = {alloc}::alloc\ + "let address = unsafe {{ {alloc}::alloc\ ({alloc}::Layout::from_size_align_unchecked\ - ({size} * values.len(), {align}));" + ({size} * values.len(), {align})) }};" ); let lower = self.lower_to_memory( "address", @@ -699,7 +733,7 @@ impl {stream_and_future_support}::FuturePayload for {name} {{ let lower = format!( r#" for (index, value) in values.iter().enumerate() {{ - let address = address + (index * size); + let address = unsafe {{ address.add(index * {size}) }}; {lower} }} "# @@ -712,10 +746,10 @@ for (index, value) in values.iter().enumerate() {{ ); let lift = format!( r#" -for (index, dst) in values.iter_mut().enumerate() {{ - let address = address + (index * size); +for (index, dst) in values.iter_mut().take(count).enumerate() {{ + let address = unsafe {{ address.add(index * {size}) }}; {lift} - *dst = value; + dst.write(value); }} "# ); @@ -793,6 +827,40 @@ impl {stream_and_future_support}::StreamPayload for {name} {{ }} }} + fn cancel_write(writer: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-cancel-write-{index}]{func_name}"] + fn cancel(_: u32) -> u32; + }} + unsafe {{ cancel(writer) }}; + }} + }} + + fn cancel_read(reader: u32) {{ + #[cfg(not(target_arch = "wasm32"))] + {{ + unreachable!(); + }} + + #[cfg(target_arch = "wasm32")] + {{ + #[link(wasm_import_module = "{module}")] + extern "C" {{ + #[link_name = "[stream-cancel-read-{index}]{func_name}"] + fn cancel(_: u32) -> u32; + }} + unsafe {{ cancel(reader) }}; + }} + }} + fn close_writable(writer: u32) {{ #[cfg(not(target_arch = "wasm32"))] {{ diff --git a/crates/rust/src/stream_and_future_support.rs b/crates/rust/src/stream_and_future_support.rs index ef1f7b722..2540b22b5 100644 --- a/crates/rust/src/stream_and_future_support.rs +++ b/crates/rust/src/stream_and_future_support.rs @@ -20,10 +20,12 @@ use { }; #[doc(hidden)] -pub trait FuturePayload: Sized + 'static { +pub trait FuturePayload: Unpin + Sized + 'static { fn new() -> u32; async fn write(future: u32, value: Self) -> bool; async fn read(future: u32) -> Option; + fn cancel_write(future: u32); + fn cancel_read(future: u32); fn close_writable(future: u32); fn close_readable(future: u32); } @@ -42,47 +44,106 @@ impl fmt::Debug for FutureWriter { } } -impl FutureWriter { - /// Write the specified value to this `future`. - pub async fn write(self, v: T) { - async_support::with_entry(self.handle, |entry| match entry { +/// Represents a write operation which may be canceled prior to completion. +pub struct CancelableWrite { + writer: Option>, + future: Pin>>, +} + +impl Future for CancelableWrite { + 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 CancelableWrite { + /// 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 { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureWriter { + 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 => { - let mut v = Some(v); - Box::pin(future::poll_fn(move |cx| { - async_support::with_entry(self.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>> - } - Handle::LocalWaiting(_) => { - let Handle::LocalWaiting(tx) = entry.insert(Handle::LocalClosed) else { - unreachable!() - }; - _ = tx.send(Box::new(v)); - Box::pin(future::ready(())) + Handle::LocalOpen + | Handle::LocalWaiting(_) + | Handle::Read + | Handle::LocalClosed => unreachable!(), + Handle::LocalReady(..) => { + entry.insert(Handle::LocalOpen); } - Handle::LocalClosed => Box::pin(future::ready(())), - Handle::Read | Handle::LocalReady(..) => unreachable!(), - Handle::Write => Box::pin(T::write(self.handle, v).map(drop)), + Handle::Write => T::cancel_write(writer.handle), }, - }) - .await; + }); + writer + } +} + +impl Drop for CancelableWrite { + fn drop(&mut self) { + if self.writer.is_some() { + self.cancel_mut(); + } + } +} + +impl FutureWriter { + /// Write the specified value to this `future`. + pub fn write(self, v: T) -> CancelableWrite { + 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>> + } + 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)), + }, + }), + } } } @@ -104,6 +165,62 @@ impl Drop for FutureWriter { } } +/// Represents a read operation which may be canceled prior to completion. +pub struct CancelableRead { + reader: Option>, + future: Pin>>>, +} + +impl Future for CancelableRead { + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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 CancelableRead { + /// 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 { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureReader { + 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 Drop for CancelableRead { + fn drop(&mut self) { + if self.reader.is_some() { + self.cancel_mut(); + } + } +} + /// Represents the readable end of a Component Model `future`. pub struct FutureReader { handle: u32, @@ -166,33 +283,37 @@ impl FutureReader { impl IntoFuture for FutureReader { type Output = Option; - type IntoFuture = Pin + 'static>>; + type IntoFuture = CancelableRead; /// 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 { - async_support::with_entry(self.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(self.handle).await }) - as Pin>>, - 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()))) - } - }, - }) + 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>>, + 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()))) + } + }, + }), + } } } @@ -225,8 +346,35 @@ pub trait StreamPayload: Unpin + Sized + 'static { fn new() -> u32; async fn write(stream: u32, values: &[Self]) -> Option; async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; - fn close_writable(future: u32); - fn close_readable(future: u32); + fn cancel_write(stream: u32); + fn cancel_read(stream: u32); + fn close_writable(stream: u32); + fn close_readable(stream: u32); +} + +struct CancelWriteOnDrop { + handle: Option, + _phantom: PhantomData, +} + +impl Drop for CancelWriteOnDrop { + 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`. @@ -236,6 +384,16 @@ pub struct StreamWriter { _phantom: PhantomData, } +impl StreamWriter { + /// 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 fmt::Debug for StreamWriter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("StreamWriter") @@ -271,6 +429,10 @@ impl Sink> for StreamWriter { Handle::LocalOpen => { let handle = self.handle; let mut item = Some(item); + let mut cancel_on_drop = Some(CancelWriteOnDrop:: { + 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!(), @@ -283,11 +445,15 @@ impl Sink> for StreamWriter { )); Poll::Pending } else { + cancel_on_drop.take().unwrap().handle = None; Poll::Ready(()) } } Handle::LocalReady(..) => Poll::Pending, - Handle::LocalClosed => Poll::Ready(()), + Handle::LocalClosed => { + cancel_on_drop.take().unwrap().handle = None; + Poll::Ready(()) + } Handle::LocalWaiting(_) | Handle::Read | Handle::Write => { unreachable!() } @@ -305,6 +471,10 @@ impl Sink> for StreamWriter { Handle::Read | Handle::LocalReady(..) => unreachable!(), Handle::Write => { let handle = self.handle; + let mut cancel_on_drop = CancelWriteOnDrop:: { + handle: Some(handle), + _phantom: PhantomData, + }; self.get_mut().future = Some(Box::pin(async move { let mut offset = 0; while offset < item.len() { @@ -314,6 +484,8 @@ impl Sink> for StreamWriter { break; } } + cancel_on_drop.handle = None; + drop(cancel_on_drop); })); } }, @@ -332,9 +504,7 @@ impl Sink> for StreamWriter { impl Drop for StreamWriter { fn drop(&mut self) { - if self.future.is_some() { - todo!("gracefully handle `StreamWriter::drop` when a write is in progress by calling `stream.cancel-write`"); - } + self.future = None; async_support::with_entry(self.handle, |entry| match entry { Entry::Vacant(_) => unreachable!(), @@ -352,6 +522,31 @@ impl Drop for StreamWriter { } } +struct CancelReadOnDrop { + handle: Option, + _phantom: PhantomData, +} + +impl Drop for CancelReadOnDrop { + 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 { handle: u32, @@ -359,6 +554,16 @@ pub struct StreamReader { _phantom: PhantomData, } +impl StreamReader { + /// 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 fmt::Debug for StreamReader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("StreamReader") @@ -427,25 +632,41 @@ impl Stream for StreamReader { Handle::Write | Handle::LocalWaiting(_) => unreachable!(), Handle::Read => { let handle = me.handle; + let mut cancel_on_drop = CancelReadOnDrop:: { + handle: Some(handle), + _phantom: PhantomData, + }; Box::pin(async move { let mut buffer = iter::repeat_with(MaybeUninit::uninit) .take(ceiling(64 * 1024, mem::size_of::())) .collect::>(); - if let Some(count) = T::read(handle, &mut buffer).await { + let result = if let Some(count) = T::read(handle, &mut buffer).await { buffer.truncate(count); Some(unsafe { mem::transmute::>, Vec>(buffer) }) } else { None - } + }; + cancel_on_drop.handle = None; + drop(cancel_on_drop); + result }) as Pin>> } Handle::LocalOpen => { let (tx, rx) = oneshot::channel(); entry.insert(Handle::LocalWaiting(tx)); - Box::pin(rx.map(|v| v.ok().map(|v| *v.downcast().unwrap()))) + let mut cancel_on_drop = CancelReadOnDrop:: { + 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(..) => { @@ -471,9 +692,7 @@ impl Stream for StreamReader { impl Drop for StreamReader { fn drop(&mut self) { - if self.future.is_some() { - todo!("gracefully handle `StreamReader::drop` when a read is in progress by calling `stream.cancel-read`"); - } + self.future = None; async_support::with_entry(self.handle, |entry| match entry { Entry::Vacant(_) => unreachable!(), From c2da9a946906079783cad94db0b940bc75686e8c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 7 Dec 2024 15:23:43 +0100 Subject: [PATCH 397/672] symmetric executor skeleton --- crates/symmetric_executor/Cargo.toml | 17 + crates/symmetric_executor/generate.sh | 3 + .../symmetric_executor/rust-client/Cargo.toml | 8 + .../symmetric_executor/rust-client/src/lib.rs | 14 + .../rust-client/src/module.rs | 1312 ++++++++++++ crates/symmetric_executor/src/executor.rs | 1902 +++++++++++++++++ crates/symmetric_executor/src/lib.rs | 62 + crates/symmetric_executor/wit/executor.wit | 54 + crates/symmetric_executor/wit/world.wit | 9 + 9 files changed, 3381 insertions(+) create mode 100644 crates/symmetric_executor/Cargo.toml create mode 100644 crates/symmetric_executor/generate.sh create mode 100644 crates/symmetric_executor/rust-client/Cargo.toml create mode 100644 crates/symmetric_executor/rust-client/src/lib.rs create mode 100644 crates/symmetric_executor/rust-client/src/module.rs create mode 100644 crates/symmetric_executor/src/executor.rs create mode 100644 crates/symmetric_executor/src/lib.rs create mode 100644 crates/symmetric_executor/wit/executor.wit create mode 100644 crates/symmetric_executor/wit/world.wit diff --git a/crates/symmetric_executor/Cargo.toml b/crates/symmetric_executor/Cargo.toml new file mode 100644 index 000000000..4f23b7857 --- /dev/null +++ b/crates/symmetric_executor/Cargo.toml @@ -0,0 +1,17 @@ +[workspace] +package.version = "0.1.0" +package.edition = "2021" + +[package] +name = "symmetric_executor" +edition.workspace = true +version.workspace = true + +[dependencies] +futures = "0.3.31" +libc = "0.2.167" +wit-bindgen = { version = "0.36.0", path = "../guest-rust" } +wit-bindgen-rt = { version = "0.36.0", path = "../guest-rust/rt" } + +[lib] +crate-type = ["cdylib"] diff --git a/crates/symmetric_executor/generate.sh b/crates/symmetric_executor/generate.sh new file mode 100644 index 000000000..337f07115 --- /dev/null +++ b/crates/symmetric_executor/generate.sh @@ -0,0 +1,3 @@ +#!/bin/sh +(cd rust-client/src;../../../../target/debug/wit-bindgen rust ../../wit -w module --symmetric --async none) +(cd rsrc;../../../target/debug/wit-bindgen rust ../wit -w executor --symmetric --async none) diff --git a/crates/symmetric_executor/rust-client/Cargo.toml b/crates/symmetric_executor/rust-client/Cargo.toml new file mode 100644 index 000000000..63d5bbb39 --- /dev/null +++ b/crates/symmetric_executor/rust-client/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] + +[package] +name = "symmetric-runtime" +version = "0.1.0" +edition = "2021" + +[dependencies] 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..b93cf3ffd --- /dev/null +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} 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..b12400e9d --- /dev/null +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -0,0 +1,1312 @@ +// Generated by `wit-bindgen` 0.36.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, 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, + } + + 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, + } + + 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, + } + + 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, + } + + 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 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); + } + } + + } + + } +} +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 { + // 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, + } + + /// 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 Resource { + #[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` 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` 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) -> usize { + resource.handle.swap(0, Relaxed) + } + + #[doc(hidden)] + pub fn handle(resource: &Resource) -> usize { + resource.handle.load(Relaxed) + } + } + + impl fmt::Debug for Resource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Resource") + .field("handle", &self.handle) + .finish() + } + } + + impl Drop for Resource { + 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: 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 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; + 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 { + handle: u32, + _phantom: PhantomData, + } + + impl fmt::Debug for FutureWriter { + 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 { + writer: Option>, + future: Pin>>, + } + + impl Future for CancelableWrite { + 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 CancelableWrite { + /// 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 { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureWriter { + 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 Drop for CancelableWrite { + fn drop(&mut self) { + if self.writer.is_some() { + self.cancel_mut(); + } + } + } + + impl FutureWriter { + /// Write the specified value to this `future`. + pub fn write(self, v: T) -> CancelableWrite { + 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>> + } + 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 Drop for FutureWriter { + 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 { + reader: Option>, + future: Pin>>>, + } + + impl Future for CancelableRead { + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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 CancelableRead { + /// 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 { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureReader { + 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 Drop for CancelableRead { + fn drop(&mut self) { + if self.reader.is_some() { + self.cancel_mut(); + } + } + } + + /// Represents the readable end of a Component Model `future`. + pub struct FutureReader { + handle: u32, + _phantom: PhantomData, + } + + impl fmt::Debug for FutureReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureReader") + .field("handle", &self.handle) + .finish() + } + } + + impl FutureReader { + #[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 IntoFuture for FutureReader { + type Output = Option; + type IntoFuture = CancelableRead; + + /// 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>>, + 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 Drop for FutureReader { + 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; + async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; + fn cancel_write(stream: u32); + fn cancel_read(stream: u32); + fn close_writable(stream: u32); + fn close_readable(stream: u32); + } + + struct CancelWriteOnDrop { + handle: Option, + _phantom: PhantomData, + } + + impl Drop for CancelWriteOnDrop { + 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 { + handle: u32, + future: Option + 'static>>>, + _phantom: PhantomData, + } + + impl StreamWriter { + /// 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 fmt::Debug for StreamWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamWriter") + .field("handle", &self.handle) + .finish() + } + } + + impl Sink> for StreamWriter { + type Error = Infallible; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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) -> 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:: { + 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:: { + 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> { + self.poll_ready(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } + } + + impl Drop for StreamWriter { + 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 { + handle: Option, + _phantom: PhantomData, + } + + impl Drop for CancelReadOnDrop { + 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 { + handle: u32, + future: Option>> + 'static>>>, + _phantom: PhantomData, + } + + impl StreamReader { + /// 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 fmt::Debug for StreamReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamReader") + .field("handle", &self.handle) + .finish() + } + } + + impl StreamReader { + #[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 Stream for StreamReader { + type Item = Vec; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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:: { + handle: Some(handle), + _phantom: PhantomData, + }; + Box::pin(async move { + let mut buffer = iter::repeat_with(MaybeUninit::uninit) + .take(ceiling(64 * 1024, mem::size_of::())) + .collect::>(); + + let result = if let Some(count) = T::read(handle, &mut buffer).await { + buffer.truncate(count); + Some(unsafe { + mem::transmute::>, Vec>(buffer) + }) + } else { + None + }; + cancel_on_drop.handle = None; + drop(cancel_on_drop); + result + }) as Pin>> + } + Handle::LocalOpen => { + let (tx, rx) = oneshot::channel(); + entry.insert(Handle::LocalWaiting(tx)); + let mut cancel_on_drop = CancelReadOnDrop:: { + 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 Drop for StreamReader { + 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() -> (FutureWriter, FutureReader) { + 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() -> (StreamWriter, StreamReader) { + 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 } + } + }} +#[allow(unused_imports)] +pub use _rt::stream_and_future_support; + +#[cfg(target_arch = "wasm32")] +#[link_section = "component-type:wit-bindgen:0.36.0:symmetric:runtime@0.1.0:module:encoded world"] +#[doc(hidden)] +#[allow(clippy::octal_escapes)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 695] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xba\x04\x01A\x02\x01\ +A\x02\x01B\x1c\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\x01i\x03\x01@\0\0\x0c\x04\ +\0\x1c[constructor]event-generator\x01\x0d\x01h\x03\x01@\x01\x04self\x0e\0\x0a\x04\ +\0![method]event-generator.subscribe\x01\x0f\x01@\x01\x04self\x0e\x01\0\x04\0\x20\ +[method]event-generator.activate\x01\x10\x01@\0\x01\0\x04\0\x03run\x01\x11\x01i\0\ +\x01i\x01\x01@\x03\x07trigger\x0a\x08callback\x12\x04data\x13\x01\0\x04\0\x08reg\ +ister\x01\x14\x03\0*symmetric:runtime/symmetric-executor@0.1.0\x05\0\x04\0\x1esy\ +mmetric:runtime/module@0.1.0\x04\0\x0b\x0c\x01\0\x06module\x03\0\0\0G\x09produce\ +rs\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/executor.rs b/crates/symmetric_executor/src/executor.rs new file mode 100644 index 000000000..479375f31 --- /dev/null +++ b/crates/symmetric_executor/src/executor.rs @@ -0,0 +1,1902 @@ +// 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, + } + + type _CallbackFunctionRep = Option; + + 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(val: T) -> Self { + Self::type_guard::(); + let val: _CallbackFunctionRep = Some(val); + let ptr: *mut _CallbackFunctionRep = + _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(&self) -> &T { + let ptr = unsafe { &*self.as_ptr::() }; + ptr.as_ref().unwrap() + } + + /// Gets mutable access to the underlying `T` which represents this + /// resource. + pub fn get_mut(&mut self) -> &mut T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.as_mut().unwrap() + } + + /// Consumes this resource and returns the underlying `T`. + pub fn into_inner(self) -> T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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() { + use core::any::TypeId; + static mut LAST_TYPE: Option = None; + unsafe { + assert!(!cfg!(target_feature = "atomics")); + let id = TypeId::of::(); + 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(handle: *mut u8) { + Self::type_guard::(); + let _ = _rt::Box::from_raw(handle as *mut _CallbackFunctionRep); + } + + fn as_ptr(&self) -> *mut _CallbackFunctionRep { + CallbackFunction::type_guard::(); + 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(&self) -> &T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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(&self) -> *mut _CallbackFunctionRep { + CallbackFunction::type_guard::(); + 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, + } + + type _CallbackDataRep = Option; + + 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(val: T) -> Self { + Self::type_guard::(); + let val: _CallbackDataRep = Some(val); + let ptr: *mut _CallbackDataRep = _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(&self) -> &T { + let ptr = unsafe { &*self.as_ptr::() }; + ptr.as_ref().unwrap() + } + + /// Gets mutable access to the underlying `T` which represents this + /// resource. + pub fn get_mut(&mut self) -> &mut T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.as_mut().unwrap() + } + + /// Consumes this resource and returns the underlying `T`. + pub fn into_inner(self) -> T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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() { + use core::any::TypeId; + static mut LAST_TYPE: Option = None; + unsafe { + assert!(!cfg!(target_feature = "atomics")); + let id = TypeId::of::(); + 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(handle: *mut u8) { + Self::type_guard::(); + let _ = _rt::Box::from_raw(handle as *mut _CallbackDataRep); + } + + fn as_ptr(&self) -> *mut _CallbackDataRep { + CallbackData::type_guard::(); + 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(&self) -> &T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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(&self) -> *mut _CallbackDataRep { + CallbackData::type_guard::(); + 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, + } + + type _EventSubscriptionRep = Option; + + 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(val: T) -> Self { + Self::type_guard::(); + let val: _EventSubscriptionRep = Some(val); + let ptr: *mut _EventSubscriptionRep = + _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(&self) -> &T { + let ptr = unsafe { &*self.as_ptr::() }; + ptr.as_ref().unwrap() + } + + /// Gets mutable access to the underlying `T` which represents this + /// resource. + pub fn get_mut(&mut self) -> &mut T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.as_mut().unwrap() + } + + /// Consumes this resource and returns the underlying `T`. + pub fn into_inner(self) -> T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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() { + use core::any::TypeId; + static mut LAST_TYPE: Option = None; + unsafe { + assert!(!cfg!(target_feature = "atomics")); + let id = TypeId::of::(); + 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(handle: *mut u8) { + Self::type_guard::(); + let _ = _rt::Box::from_raw(handle as *mut _EventSubscriptionRep); + } + + fn as_ptr(&self) -> *mut _EventSubscriptionRep { + EventSubscription::type_guard::(); + 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(&self) -> &T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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(&self) -> *mut _EventSubscriptionRep { + EventSubscription::type_guard::(); + 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, + } + + type _EventGeneratorRep = Option; + + 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(val: T) -> Self { + Self::type_guard::(); + let val: _EventGeneratorRep = Some(val); + let ptr: *mut _EventGeneratorRep = + _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(&self) -> &T { + let ptr = unsafe { &*self.as_ptr::() }; + ptr.as_ref().unwrap() + } + + /// Gets mutable access to the underlying `T` which represents this + /// resource. + pub fn get_mut(&mut self) -> &mut T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.as_mut().unwrap() + } + + /// Consumes this resource and returns the underlying `T`. + pub fn into_inner(self) -> T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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() { + use core::any::TypeId; + static mut LAST_TYPE: Option = None; + unsafe { + assert!(!cfg!(target_feature = "atomics")); + let id = TypeId::of::(); + 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(handle: *mut u8) { + Self::type_guard::(); + let _ = _rt::Box::from_raw(handle as *mut _EventGeneratorRep); + } + + fn as_ptr(&self) -> *mut _EventGeneratorRep { + EventGenerator::type_guard::(); + 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(&self) -> &T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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(&self) -> *mut _EventGeneratorRep { + EventGenerator::type_guard::(); + 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_constructor_event_generator_cabi( + ) -> *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() { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + T::run(); + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_register_cabi( + 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( + arg0: usize, + ) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + CallbackFunction::dtor::(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(arg0: usize) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + CallbackData::dtor::(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( + arg0: usize, + ) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + EventSubscription::dtor::(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; + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_drop_eventGenerator_cabi( + arg0: usize, + ) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + EventGenerator::dtor::(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 = "[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 { + // 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, + } + + /// 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 Resource { + #[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` 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` 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) -> usize { + resource.handle.swap(0, Relaxed) + } + + #[doc(hidden)] + pub fn handle(resource: &Resource) -> usize { + resource.handle.load(Relaxed) + } + } + + impl fmt::Debug for Resource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Resource") + .field("handle", &self.handle) + .finish() + } + } + + impl Drop for Resource { + 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(); + } + 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; + 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 { + handle: u32, + _phantom: PhantomData, + } + + impl fmt::Debug for FutureWriter { + 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 { + writer: Option>, + future: Pin>>, + } + + impl Future for CancelableWrite { + 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 CancelableWrite { + /// 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 { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureWriter { + 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 Drop for CancelableWrite { + fn drop(&mut self) { + if self.writer.is_some() { + self.cancel_mut(); + } + } + } + + impl FutureWriter { + /// Write the specified value to this `future`. + pub fn write(self, v: T) -> CancelableWrite { + 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>> + } + 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 Drop for FutureWriter { + 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 { + reader: Option>, + future: Pin>>>, + } + + impl Future for CancelableRead { + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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 CancelableRead { + /// 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 { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureReader { + 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 Drop for CancelableRead { + fn drop(&mut self) { + if self.reader.is_some() { + self.cancel_mut(); + } + } + } + + /// Represents the readable end of a Component Model `future`. + pub struct FutureReader { + handle: u32, + _phantom: PhantomData, + } + + impl fmt::Debug for FutureReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureReader") + .field("handle", &self.handle) + .finish() + } + } + + impl FutureReader { + #[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 IntoFuture for FutureReader { + type Output = Option; + type IntoFuture = CancelableRead; + + /// 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>>, + 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 Drop for FutureReader { + 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; + async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; + fn cancel_write(stream: u32); + fn cancel_read(stream: u32); + fn close_writable(stream: u32); + fn close_readable(stream: u32); + } + + struct CancelWriteOnDrop { + handle: Option, + _phantom: PhantomData, + } + + impl Drop for CancelWriteOnDrop { + 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 { + handle: u32, + future: Option + 'static>>>, + _phantom: PhantomData, + } + + impl StreamWriter { + /// 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 fmt::Debug for StreamWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamWriter") + .field("handle", &self.handle) + .finish() + } + } + + impl Sink> for StreamWriter { + type Error = Infallible; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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) -> 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:: { + 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:: { + 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> { + self.poll_ready(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } + } + + impl Drop for StreamWriter { + 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 { + handle: Option, + _phantom: PhantomData, + } + + impl Drop for CancelReadOnDrop { + 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 { + handle: u32, + future: Option>> + 'static>>>, + _phantom: PhantomData, + } + + impl StreamReader { + /// 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 fmt::Debug for StreamReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamReader") + .field("handle", &self.handle) + .finish() + } + } + + impl StreamReader { + #[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 Stream for StreamReader { + type Item = Vec; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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:: { + handle: Some(handle), + _phantom: PhantomData, + }; + Box::pin(async move { + let mut buffer = iter::repeat_with(MaybeUninit::uninit) + .take(ceiling(64 * 1024, mem::size_of::())) + .collect::>(); + + let result = if let Some(count) = + T::read(handle, &mut buffer).await + { + buffer.truncate(count); + Some(unsafe { + mem::transmute::>, Vec>(buffer) + }) + } else { + None + }; + cancel_on_drop.handle = None; + drop(cancel_on_drop); + result + }) + as Pin>> + } + Handle::LocalOpen => { + let (tx, rx) = oneshot::channel(); + entry.insert(Handle::LocalWaiting(tx)); + let mut cancel_on_drop = CancelReadOnDrop:: { + 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 Drop for StreamReader { + 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() -> (FutureWriter, FutureReader) { + 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() -> (StreamWriter, StreamReader) { + 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; 699] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xbc\x04\x01A\x02\x01\ +A\x02\x01B\x1c\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\x01i\x03\x01@\0\0\x0c\x04\ +\0\x1c[constructor]event-generator\x01\x0d\x01h\x03\x01@\x01\x04self\x0e\0\x0a\x04\ +\0![method]event-generator.subscribe\x01\x0f\x01@\x01\x04self\x0e\x01\0\x04\0\x20\ +[method]event-generator.activate\x01\x10\x01@\0\x01\0\x04\0\x03run\x01\x11\x01i\0\ +\x01i\x01\x01@\x03\x07trigger\x0a\x08callback\x12\x04data\x13\x01\0\x04\0\x08reg\ +ister\x01\x14\x04\0*symmetric:runtime/symmetric-executor@0.1.0\x05\0\x04\0\x20sy\ +mmetric:runtime/executor@0.1.0\x04\0\x0b\x0e\x01\0\x08executor\x03\0\0\0G\x09pro\ +ducers\x01\x0cprocessed-by\x02\x0dwit-component\x070.221.2\x10wit-bindgen-rust\x06\ +0.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..b8057c8ea --- /dev/null +++ b/crates/symmetric_executor/src/lib.rs @@ -0,0 +1,62 @@ +use executor::exports::symmetric::runtime::symmetric_executor; + +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 { + todo!() + } + + fn from_timeout(_nanoseconds: u64) -> symmetric_executor::EventSubscription { + todo!() + } +} + +impl symmetric_executor::GuestEventGenerator for EventGenerator { + fn new() -> Self { + todo!() + } + + fn subscribe(&self) -> symmetric_executor::EventSubscription { + todo!() + } + + fn activate(&self) -> () { + todo!() + } +} + +impl symmetric_executor::Guest for Guest { + type CallbackFunction=Ignore; + type CallbackData=Ignore; + type EventSubscription = EventSubscription; + type EventGenerator= EventGenerator; + + fn run() -> () { + todo!() + } + + fn register( + _trigger: symmetric_executor::EventSubscription, + _callback: symmetric_executor::CallbackFunction, + _data: symmetric_executor::CallbackData, + ) -> () { + todo!() + } +} + +struct EventGenerator { + +} + +struct EventSubscription { + +} diff --git a/crates/symmetric_executor/wit/executor.wit b/crates/symmetric_executor/wit/executor.wit new file mode 100644 index 000000000..5eade9a20 --- /dev/null +++ b/crates/symmetric_executor/wit/executor.wit @@ -0,0 +1,54 @@ +// 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; + } + /// 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); +} diff --git a/crates/symmetric_executor/wit/world.wit b/crates/symmetric_executor/wit/world.wit new file mode 100644 index 000000000..01ff009b6 --- /dev/null +++ b/crates/symmetric_executor/wit/world.wit @@ -0,0 +1,9 @@ +package symmetric:runtime@0.1.0; + +world executor { + export symmetric-executor; +} + +world module { + import symmetric-executor; +} From 0eeacb17ca1201b8e8992eda1a1cf6d01c920485 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 7 Dec 2024 15:35:06 +0100 Subject: [PATCH 398/672] initial sleep trigger implementation --- crates/cpp/tests/symmetric_async/Cargo.toml | 3 +++ crates/cpp/tests/symmetric_async/sleep/Cargo.toml | 10 ++++++++++ crates/cpp/tests/symmetric_async/sleep/build.rs | 9 +++++++++ crates/cpp/tests/symmetric_async/sleep/src/lib.rs | 13 +++++++++++++ 4 files changed, 35 insertions(+) create mode 100644 crates/cpp/tests/symmetric_async/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_async/sleep/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_async/sleep/build.rs create mode 100644 crates/cpp/tests/symmetric_async/sleep/src/lib.rs diff --git a/crates/cpp/tests/symmetric_async/Cargo.toml b/crates/cpp/tests/symmetric_async/Cargo.toml new file mode 100644 index 000000000..6470c5f5a --- /dev/null +++ b/crates/cpp/tests/symmetric_async/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["sleep"] +resolver = "2" 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..df2cbbea3 --- /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 = { version = "0.1.0", 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..0129c5e28 --- /dev/null +++ b/crates/cpp/tests/symmetric_async/sleep/src/lib.rs @@ -0,0 +1,13 @@ +#[link(name = "symmetric_executor")] +extern "C" { + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(nanoseconds: u64) -> *mut (); +} + +#[no_mangle] +unsafe extern "C" fn async_sleep( + args: *const (), + _results: *mut (), +) -> *mut () { + let nanoseconds = *args.cast::(); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(nanoseconds) +} From e498eb6c8ab1d49a579562e3bbc328603f209b4a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 7 Dec 2024 17:12:11 +0100 Subject: [PATCH 399/672] async module skeleton --- crates/cpp/tests/symmetric_async/Cargo.toml | 2 +- .../symmetric_async/async_module/Cargo.toml | 13 + .../async_module/src/async_module.rs | 1016 +++++++ .../symmetric_async/async_module/src/lib.rs | 1 + crates/cpp/tests/symmetric_async/generate.sh | 2 + .../tests/symmetric_async/sleep/src/lib.rs | 2 +- .../symmetric_async/wit/async_module.wit | 14 + .../symmetric_executor/rust-client/Cargo.toml | 6 +- .../rust-client/src/async_support.rs | 40 + .../symmetric_executor/rust-client/src/lib.rs | 16 +- .../rust-client/src/module.rs | 2331 +++++++++-------- crates/symmetric_executor/src/lib.rs | 26 +- 12 files changed, 2309 insertions(+), 1160 deletions(-) create mode 100644 crates/cpp/tests/symmetric_async/async_module/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_async/async_module/src/async_module.rs create mode 100644 crates/cpp/tests/symmetric_async/async_module/src/lib.rs create mode 100644 crates/cpp/tests/symmetric_async/generate.sh create mode 100644 crates/cpp/tests/symmetric_async/wit/async_module.wit create mode 100644 crates/symmetric_executor/rust-client/src/async_support.rs diff --git a/crates/cpp/tests/symmetric_async/Cargo.toml b/crates/cpp/tests/symmetric_async/Cargo.toml index 6470c5f5a..913ec0ab6 100644 --- a/crates/cpp/tests/symmetric_async/Cargo.toml +++ b/crates/cpp/tests/symmetric_async/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["sleep"] +members = [ "async_module","sleep"] resolver = "2" 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..1f3cd95b8 --- /dev/null +++ b/crates/cpp/tests/symmetric_async/async_module/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "async_module" +version = "0.1.0" +edition = "2021" + +[dependencies] +futures = "0.3.31" +sleep = { version = "0.1.0", path = "../sleep" } +wit-bindgen = { version = "0.36.0", path = "../../../../guest-rust" } +wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../../../../symmetric_executor/rust-client" } + +[lib] +crate-type = ["cdylib"] 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..dcf73a4a0 --- /dev/null +++ b/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs @@ -0,0 +1,1016 @@ +// Generated by `wit-bindgen` 0.36.0. DO NOT EDIT! +// Options used: +#[allow(dead_code, clippy::all)] +pub mod test { + pub mod test { + + #[allow(dead_code, 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 sleep(nanoseconds: u64) -> () { + unsafe { + let layout0 = _rt::alloc::Layout::from_size_align_unchecked(8, 8); + let ptr0 = _rt::alloc::alloc(layout0); + *ptr0.add(0).cast::() = _rt::as_i64(&nanoseconds); + let layout1 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); + let ptr1 = _rt::alloc::alloc(layout1); + + #[link(wasm_import_module = "test:test/wait")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "[async]sleep")] + fn testX3AtestX2FwaitX00X5BasyncX5Dsleep(_: *mut u8, _: *mut u8) + -> *mut u8; + } + let layout2 = _rt::alloc::Layout::from_size_align_unchecked(8, 8); + ::wit_bindgen_symmetric_rt::async_support::await_result( + testX3AtestX2FwaitX00X5BasyncX5Dsleep, + layout2, + ptr0, + ptr1, + ) + .await; + _rt::cabi_dealloc(ptr1, 0, 1); + } + } + } + } +} +#[allow(dead_code, clippy::all)] +pub mod exports { + pub mod test { + pub mod test { + + #[allow(dead_code, 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_forward_cabi( + arg0: *mut u8, + arg1: usize, + arg2: *mut u8, + ) -> *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 result1 = T::forward(string0); + let result = ::wit_bindgen_symmetric_rt::async_support::first_poll( + result1, + |result2| { + let vec3 = (result2.into_bytes()).into_boxed_slice(); + let ptr3 = vec3.as_ptr().cast::(); + let len3 = vec3.len(); + ::core::mem::forget(vec3); + + #[link(wasm_import_module = "[export]test:test/string-delay")] + extern "C" { + #[cfg_attr( + target_arch = "wasm32", + link_name = "[task-return]forward" + )] + fn X5BexportX5DtestX3AtestX2Fstring_delayX00X5Btask_returnX5Dforward( + _: *mut u8, + _: usize, + ); + } + X5BexportX5DtestX3AtestX2Fstring_delayX00X5Btask_returnX5Dforward( + ptr3.cast_mut(), + len3, + ); + }, + ); + + result + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __callback_forward( + ctx: *mut u8, + event0: i32, + event1: i32, + event2: i32, + ) -> i32 { + ::wit_bindgen_symmetric_rt::async_support::callback(ctx, event0, event1, event2) + } + pub trait Guest { + fn forward( + s: _rt::String, + ) -> impl ::core::future::Future + 'static; + } + #[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 = "forward")] + #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] + unsafe extern "C" fn testX3AtestX2Fstring_delayX00forward(arg0: *mut u8,arg1: usize,arg2: *mut u8,) -> *mut u8 { + $($path_to_types)*::_export_forward_cabi::<$ty>(arg0, arg1, arg2) + } + #[export_name = "[callback]forward"] + unsafe extern "C" fn _callback_forward(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + $($path_to_types)*::__callback_forward(ctx, event0, event1, event2) + } + };); + } + #[doc(hidden)] + pub(crate) use __export_test_test_string_delay_cabi; + } + } + } +} +mod _rt { + #![allow(dead_code, clippy::all)] + pub use alloc_crate::alloc; + + pub fn as_i64(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 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); + } + + #[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; + 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_symmetric_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; + 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 { + handle: u32, + _phantom: PhantomData, + } + + impl fmt::Debug for FutureWriter { + 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 { + writer: Option>, + future: Pin>>, + } + + impl Future for CancelableWrite { + 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 CancelableWrite { + /// 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 { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureWriter { + 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 Drop for CancelableWrite { + fn drop(&mut self) { + if self.writer.is_some() { + self.cancel_mut(); + } + } + } + + impl FutureWriter { + /// Write the specified value to this `future`. + pub fn write(self, v: T) -> CancelableWrite { + 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>> + } + 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 Drop for FutureWriter { + 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 { + reader: Option>, + future: Pin>>>, + } + + impl Future for CancelableRead { + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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 CancelableRead { + /// 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 { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureReader { + 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 Drop for CancelableRead { + fn drop(&mut self) { + if self.reader.is_some() { + self.cancel_mut(); + } + } + } + + /// Represents the readable end of a Component Model `future`. + pub struct FutureReader { + handle: u32, + _phantom: PhantomData, + } + + impl fmt::Debug for FutureReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureReader") + .field("handle", &self.handle) + .finish() + } + } + + impl FutureReader { + #[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 IntoFuture for FutureReader { + type Output = Option; + type IntoFuture = CancelableRead; + + /// 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>>, + 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 Drop for FutureReader { + 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; + async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; + fn cancel_write(stream: u32); + fn cancel_read(stream: u32); + fn close_writable(stream: u32); + fn close_readable(stream: u32); + } + + struct CancelWriteOnDrop { + handle: Option, + _phantom: PhantomData, + } + + impl Drop for CancelWriteOnDrop { + 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 { + handle: u32, + future: Option + 'static>>>, + _phantom: PhantomData, + } + + impl StreamWriter { + /// 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 fmt::Debug for StreamWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamWriter") + .field("handle", &self.handle) + .finish() + } + } + + impl Sink> for StreamWriter { + type Error = Infallible; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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) -> 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:: { + 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:: { + 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> { + self.poll_ready(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } + } + + impl Drop for StreamWriter { + 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 { + handle: Option, + _phantom: PhantomData, + } + + impl Drop for CancelReadOnDrop { + 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 { + handle: u32, + future: Option>> + 'static>>>, + _phantom: PhantomData, + } + + impl StreamReader { + /// 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 fmt::Debug for StreamReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamReader") + .field("handle", &self.handle) + .finish() + } + } + + impl StreamReader { + #[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 Stream for StreamReader { + type Item = Vec; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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:: { + handle: Some(handle), + _phantom: PhantomData, + }; + Box::pin(async move { + let mut buffer = iter::repeat_with(MaybeUninit::uninit) + .take(ceiling(64 * 1024, mem::size_of::())) + .collect::>(); + + let result = if let Some(count) = + T::read(handle, &mut buffer).await + { + buffer.truncate(count); + Some(unsafe { + mem::transmute::>, Vec>(buffer) + }) + } else { + None + }; + cancel_on_drop.handle = None; + drop(cancel_on_drop); + result + }) + as Pin>> + } + Handle::LocalOpen => { + let (tx, rx) = oneshot::channel(); + entry.insert(Handle::LocalWaiting(tx)); + let mut cancel_on_drop = CancelReadOnDrop:: { + 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 Drop for StreamReader { + 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() -> (FutureWriter, FutureReader) { + 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() -> (StreamWriter, StreamReader) { + 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_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")] +#[link_section = "component-type:wit-bindgen:0.36.0:test:test:async-module: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\x02\x01@\x01\x0bnanosecondsw\x01\0\x04\0\x05sleep\x01\0\x03\0\x0etest\ +:test/wait\x05\0\x01B\x02\x01@\x01\x01ss\0s\x04\0\x07forward\x01\0\x04\0\x16test\ +:test/string-delay\x05\x01\x04\0\x16test:test/async-module\x04\0\x0b\x12\x01\0\x0c\ +async-module\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x07\ +0.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/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..ce4f2425c --- /dev/null +++ b/crates/cpp/tests/symmetric_async/async_module/src/lib.rs @@ -0,0 +1 @@ +mod async_module; diff --git a/crates/cpp/tests/symmetric_async/generate.sh b/crates/cpp/tests/symmetric_async/generate.sh new file mode 100644 index 000000000..575b0c87b --- /dev/null +++ b/crates/cpp/tests/symmetric_async/generate.sh @@ -0,0 +1,2 @@ +#!/bin/sh +(cd async_module/src ; ../../../../../../target/debug/wit-bindgen rust ../../wit/async_module.wit --async all --symmetric) diff --git a/crates/cpp/tests/symmetric_async/sleep/src/lib.rs b/crates/cpp/tests/symmetric_async/sleep/src/lib.rs index 0129c5e28..1c0b699d6 100644 --- a/crates/cpp/tests/symmetric_async/sleep/src/lib.rs +++ b/crates/cpp/tests/symmetric_async/sleep/src/lib.rs @@ -4,7 +4,7 @@ extern "C" { } #[no_mangle] -unsafe extern "C" fn async_sleep( +unsafe extern "C" fn testX3AtestX2FwaitX00X5BasyncX5Dsleep( args: *const (), _results: *mut (), ) -> *mut () { 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..3436d87a3 --- /dev/null +++ b/crates/cpp/tests/symmetric_async/wit/async_module.wit @@ -0,0 +1,14 @@ +package test:test; + +interface string-delay { + forward: func(s: string) -> string; +} + +interface wait { + sleep: func(nanoseconds: u64); +} + +world async-module { + import wait; + export string-delay; +} diff --git a/crates/symmetric_executor/rust-client/Cargo.toml b/crates/symmetric_executor/rust-client/Cargo.toml index 63d5bbb39..c4ca546c7 100644 --- a/crates/symmetric_executor/rust-client/Cargo.toml +++ b/crates/symmetric_executor/rust-client/Cargo.toml @@ -1,8 +1,10 @@ [workspace] [package] -name = "symmetric-runtime" -version = "0.1.0" +name = "wit-bindgen-symmetric-rt" +version = "0.36.0" edition = "2021" [dependencies] +futures = "0.3.31" +wit-bindgen = { version = "0.36.0", path = "../../guest-rust" } 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..240bdf571 --- /dev/null +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -0,0 +1,40 @@ +use futures::{channel::oneshot, task::Waker}; +use std::{alloc::Layout, any::Any, collections::hash_map, future::Future}; + +#[doc(hidden)] +pub enum Handle { + LocalOpen, + LocalReady(Box, Waker), + LocalWaiting(oneshot::Sender>), + LocalClosed, + Read, + Write, +} + +#[doc(hidden)] +pub fn with_entry(h: u32, f: impl FnOnce(hash_map::Entry<'_, u32, Handle>) -> T) -> T { + todo!() +} + +#[doc(hidden)] +pub fn first_poll( + _future: impl Future + 'static, + _fun: impl FnOnce(T) + 'static, +) -> *mut u8 { + todo!() +} + +#[doc(hidden)] +pub async unsafe fn await_result( + _import: unsafe extern "C" fn(*mut u8, *mut u8) -> *mut u8, + _params_layout: Layout, + _params: *mut u8, + _results: *mut u8, +) { + todo!() +} + +#[doc(hidden)] +pub unsafe fn callback(_ctx: *mut u8, _event0: i32, _event1: i32, _event2: i32) -> i32 { + todo!() +} diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index b93cf3ffd..891e3c6eb 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -1,14 +1,2 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +pub mod async_support; +mod module; diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index b12400e9d..96e8b10cf 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -2,1284 +2,1340 @@ // 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, 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, - } - - 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) - } + 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::__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, + } - #[doc(hidden)] - pub fn handle(&self) -> usize{ - _rt::Resource::handle(&self.handle) - } - } + 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) + } - 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); + #[doc(hidden)] + pub fn handle(&self) -> usize { + _rt::Resource::handle(&self.handle) + } } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function(_handle); - } - } - } - - /// This wraps opaque user data, freed by the callback once - /// it returns ready + 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); + } + } + } - #[derive(Debug)] - #[repr(transparent)] - pub struct CallbackData{ - handle: _rt::Resource, - } + /// This wraps opaque user data, freed by the callback once + /// it returns ready - impl CallbackData{ - #[doc(hidden)] - pub unsafe fn from_handle(handle: usize) -> Self { - Self { - handle: _rt::Resource::from_handle(handle), - } - } + #[derive(Debug)] + #[repr(transparent)] + pub struct CallbackData { + handle: _rt::Resource, + } - #[doc(hidden)] - pub fn take_handle(&self) -> usize{ - _rt::Resource::take_handle(&self.handle) - } + impl CallbackData { + #[doc(hidden)] + pub unsafe fn from_handle(handle: usize) -> Self { + Self { + handle: _rt::Resource::from_handle(handle), + } + } - #[doc(hidden)] - pub fn handle(&self) -> usize{ - _rt::Resource::handle(&self.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); + 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); + } + } } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data(_handle); - } - } - } + /// The receiving side of an event - /// The receiving side of an event + #[derive(Debug)] + #[repr(transparent)] + pub struct EventSubscription { + handle: _rt::Resource, + } - #[derive(Debug)] - #[repr(transparent)] - pub struct EventSubscription{ - handle: _rt::Resource, - } + impl EventSubscription { + #[doc(hidden)] + pub unsafe fn from_handle(handle: usize) -> Self { + Self { + handle: _rt::Resource::from_handle(handle), + } + } - 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 take_handle(&self) -> usize{ - _rt::Resource::take_handle(&self.handle) - } + #[doc(hidden)] + pub fn handle(&self) -> usize { + _rt::Resource::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 - 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); + #[derive(Debug)] + #[repr(transparent)] + pub struct EventGenerator { + handle: _rt::Resource, } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription(_handle); - } - } - } + impl EventGenerator { + #[doc(hidden)] + pub unsafe fn from_handle(handle: usize) -> Self { + Self { + handle: _rt::Resource::from_handle(handle), + } + } - /// A user controlled event + #[doc(hidden)] + pub fn take_handle(&self) -> usize { + _rt::Resource::take_handle(&self.handle) + } - #[derive(Debug)] - #[repr(transparent)] - pub struct EventGenerator{ - handle: _rt::Resource, - } + #[doc(hidden)] + pub fn handle(&self) -> usize { + _rt::Resource::handle(&self.handle) + } + } - impl EventGenerator{ - #[doc(hidden)] - pub unsafe fn from_handle(handle: usize) -> Self { - Self { - handle: _rt::Resource::from_handle(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); + } + } + } - #[doc(hidden)] - pub fn take_handle(&self) -> usize{ - _rt::Resource::take_handle(&self.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(), + } + } + } - #[doc(hidden)] - pub fn handle(&self) -> usize{ - _rt::Resource::handle(&self.handle) - } - } + 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, - 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); + _ => panic!("invalid enum discriminant"), + } + } } - 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() + /// 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, } - CallStatus::NotStarted => { - f.debug_tuple("CallStatus::NotStarted").finish() + 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 CallStatus{ - #[doc(hidden)] - pub unsafe fn _lift(val: u8) -> CallStatus{ - if !cfg!(debug_assertions) { - return ::core::mem::transmute(val); - } + impl CallbackState { + #[doc(hidden)] + pub unsafe fn _lift(val: u8) -> CallbackState { + if !cfg!(debug_assertions) { + return ::core::mem::transmute(val); + } - match val { - 0 => CallStatus::Started, - 1 => CallStatus::NotStarted, + match val { + 0 => CallbackState::Pending, + 1 => CallbackState::Ready, - _ => 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() + _ => panic!("invalid enum discriminant"), + } + } } - } - } - } - - 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; + 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) + } + } } - 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; + 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) + } + } } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(_rt::as_i64(&nanoseconds)); - EventSubscription::from_handle(ret as usize) - } - } - } - 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; + 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) + } + } } - 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; + 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) + } + } } - 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, ); + 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, + ); + } } - 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); } - } - } - - } } 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 { - // 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, - } - - /// 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 Resource { - #[doc(hidden)] - pub unsafe fn from_handle(handle: usize) -> Self { - debug_assert!(handle != 0); - Self { - handle: AtomicUsize::new(handle), - _marker: marker::PhantomData, - } - } + #![allow(dead_code, clippy::all)] + + use core::fmt; + use core::marker; + use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; - /// Takes ownership of the handle owned by `resource`. + /// A type which represents a component model resource, either imported or + /// exported into this component. /// - /// Note that this ideally would be `into_handle` taking `Resource` by - /// ownership. The code generator does not enable that in all situations, - /// unfortunately, so this is provided instead. + /// 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. /// - /// 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` 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) -> usize { - resource.handle.swap(0, Relaxed) + /// 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 { + // 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, } - #[doc(hidden)] - pub fn handle(resource: &Resource) -> usize { - resource.handle.load(Relaxed) + /// 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 fmt::Debug for Resource { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Resource") - .field("handle", &self.handle) - .finish() - } - } - - impl Drop for Resource { - 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), + impl Resource { + #[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` 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` 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) -> usize { + resource.handle.swap(0, Relaxed) + } + + #[doc(hidden)] + pub fn handle(resource: &Resource) -> usize { + resource.handle.load(Relaxed) } - } - } - } - 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: 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 fmt::Debug for Resource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Resource") + .field("handle", &self.handle) + .finish() + } } - } - - impl AsI64 for i64 { - #[inline] - fn as_i64(self) -> i64 { - self as i64 + + impl Drop for Resource { + 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), + } + } + } } - } - - impl AsI64 for u64 { - #[inline] - fn as_i64(self) -> i64 { - self as i64 + 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 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; - fn cancel_write(future: u32); - fn cancel_read(future: u32); - fn close_writable(future: u32); - fn close_readable(future: u32); + + pub fn as_i64(t: T) -> i64 { + t.as_i64() } - /// Represents the writable end of a Component Model `future`. - pub struct FutureWriter { - handle: u32, - _phantom: PhantomData, + pub trait AsI64 { + fn as_i64(self) -> i64; } - impl fmt::Debug for FutureWriter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FutureWriter") - .field("handle", &self.handle) - .finish() - } + impl<'a, T: Copy + AsI64> AsI64 for &'a T { + fn as_i64(self) -> i64 { + (*self).as_i64() + } } - /// Represents a write operation which may be canceled prior to completion. - pub struct CancelableWrite { - writer: Option>, - future: Pin>>, + impl AsI64 for i64 { + #[inline] + fn as_i64(self) -> i64 { + self as i64 + } } - impl Future for CancelableWrite { - 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 AsI64 for u64 { + #[inline] + fn as_i64(self) -> i64 { + self as i64 } - } } + pub mod stream_and_future_support { + use crate as wit_bindgen_symmetric_rt; + 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_symmetric_rt::async_support::{self, Handle}, + }; - impl CancelableWrite { - /// 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 { - self.cancel_mut() - } - - fn cancel_mut(&mut self) -> FutureWriter { - 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); + #[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; + 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 { + handle: u32, + _phantom: PhantomData, + } + + impl fmt::Debug for FutureWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureWriter") + .field("handle", &self.handle) + .finish() } - Handle::Write => T::cancel_write(writer.handle), - }, - }); - writer - } - } + } - impl Drop for CancelableWrite { - fn drop(&mut self) { - if self.writer.is_some() { - self.cancel_mut(); + /// Represents a write operation which may be canceled prior to completion. + pub struct CancelableWrite { + writer: Option>, + future: Pin>>, + } + + impl Future for CancelableWrite { + 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 FutureWriter { - /// Write the specified value to this `future`. - pub fn write(self, v: T) -> CancelableWrite { - 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 { + impl CancelableWrite { + /// 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 { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureWriter { + 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 => { - 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!() - } + Handle::LocalOpen + | Handle::LocalWaiting(_) + | Handle::Read + | Handle::LocalClosed => unreachable!(), + Handle::LocalReady(..) => { + entry.insert(Handle::LocalOpen); + } + Handle::Write => T::cancel_write(writer.handle), }, - }) - })) as Pin>> - } - 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)), - }, - }), + }); + writer + } } - } - } - impl Drop for FutureWriter { - 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); + impl Drop for CancelableWrite { + fn drop(&mut self) { + if self.writer.is_some() { + self.cancel_mut(); + } } - }, - }); - } - } - - /// Represents a read operation which may be canceled prior to completion. - pub struct CancelableRead { - reader: Option>, - future: Pin>>>, - } + } - impl Future for CancelableRead { - type Output = Option; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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 FutureWriter { + /// Write the specified value to this `future`. + pub fn write(self, v: T) -> CancelableWrite { + 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>> + } + 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 CancelableRead { - /// 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 { - self.cancel_mut() - } - - fn cancel_mut(&mut self) -> FutureReader { - 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); + impl Drop for FutureWriter { + 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); + } + }, + }); } - Handle::Read => T::cancel_read(reader.handle), - }, - }); - reader - } - } + } - impl Drop for CancelableRead { - fn drop(&mut self) { - if self.reader.is_some() { - self.cancel_mut(); + /// Represents a read operation which may be canceled prior to completion. + pub struct CancelableRead { + reader: Option>, + future: Pin>>>, } - } - } - /// Represents the readable end of a Component Model `future`. - pub struct FutureReader { - handle: u32, - _phantom: PhantomData, - } + impl Future for CancelableRead { + type Output = Option; - impl fmt::Debug for FutureReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FutureReader") - .field("handle", &self.handle) - .finish() - } - } + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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 FutureReader { - #[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); + impl CancelableRead { + /// 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 { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureReader { + 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 } - Handle::Read - | Handle::LocalOpen - | Handle::LocalReady(..) - | Handle::LocalWaiting(_) - | Handle::LocalClosed => { - unreachable!() + } + + impl Drop for CancelableRead { + fn drop(&mut self) { + if self.reader.is_some() { + self.cancel_mut(); + } } - }, - }); + } - Self { - handle, - _phantom: PhantomData, + /// Represents the readable end of a Component Model `future`. + pub struct FutureReader { + handle: u32, + _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); + + impl fmt::Debug for FutureReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureReader") + .field("handle", &self.handle) + .finish() } - Handle::Read | Handle::LocalClosed => { - entry.remove(); + } + + impl FutureReader { + #[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, + } } - Handle::LocalReady(..) | Handle::LocalWaiting(_) | Handle::Write => unreachable!(), - }, - }); - ManuallyDrop::new(self).handle - } - } + #[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!() + } + }, + }); - impl IntoFuture for FutureReader { - type Output = Option; - type IntoFuture = CancelableRead; - - /// 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>>, - 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()))) - } - }, - }), + ManuallyDrop::new(self).handle + } } - } - } - impl Drop for FutureReader { - 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); + impl IntoFuture for FutureReader { + type Output = Option; + type IntoFuture = CancelableRead; + + /// 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>>, + 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()))) + } + }, + }), + } } - Handle::Read | Handle::LocalClosed => { - entry.remove(); - T::close_readable(self.handle); + } + + impl Drop for FutureReader { + 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!(), + }, + }); } - Handle::Write => unreachable!(), - }, - }); - } - } + } - #[doc(hidden)] - pub trait StreamPayload: Unpin + Sized + 'static { - fn new() -> u32; - async fn write(stream: u32, values: &[Self]) -> Option; - async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; - fn cancel_write(stream: u32); - fn cancel_read(stream: u32); - fn close_writable(stream: u32); - fn close_readable(stream: u32); - } + #[doc(hidden)] + pub trait StreamPayload: Unpin + Sized + 'static { + fn new() -> u32; + async fn write(stream: u32, values: &[Self]) -> Option; + async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; + fn cancel_write(stream: u32); + fn cancel_read(stream: u32); + fn close_writable(stream: u32); + fn close_readable(stream: u32); + } - struct CancelWriteOnDrop { - handle: Option, - _phantom: PhantomData, - } + struct CancelWriteOnDrop { + handle: Option, + _phantom: PhantomData, + } - impl Drop for CancelWriteOnDrop { - 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), - }, - }); + impl Drop for CancelWriteOnDrop { + 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 { - handle: u32, - future: Option + 'static>>>, - _phantom: PhantomData, - } + /// Represents the writable end of a Component Model `stream`. + pub struct StreamWriter { + handle: u32, + future: Option + 'static>>>, + _phantom: PhantomData, + } - impl StreamWriter { - /// 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 StreamWriter { + /// 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 fmt::Debug for StreamWriter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StreamWriter") - .field("handle", &self.handle) - .finish() - } - } + impl fmt::Debug for StreamWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamWriter") + .field("handle", &self.handle) + .finish() + } + } - impl Sink> for StreamWriter { - type Error = Infallible; + impl Sink> for StreamWriter { + type Error = Infallible; - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let me = self.get_mut(); + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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) -> 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:: { - 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!() + 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, } - }, - }) - }))); - } - 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:: { - 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; - } + } else { + Poll::Ready(Ok(())) } - cancel_on_drop.handle = None; - drop(cancel_on_drop); - })); } - }, - }); - Ok(()) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.poll_ready(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.poll_ready(cx) - } - } - impl Drop for StreamWriter { - fn drop(&mut self) { - self.future = None; + fn start_send(self: Pin<&mut Self>, item: Vec) -> 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:: { + 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:: { + 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(()) + } - 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); + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) } - Handle::Read => unreachable!(), - Handle::Write | Handle::LocalClosed => { - entry.remove(); - T::close_writable(self.handle); + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) } - }, - }); - } - } + } - struct CancelReadOnDrop { - handle: Option, - _phantom: PhantomData, - } + impl Drop for StreamWriter { + fn drop(&mut self) { + self.future = None; - impl Drop for CancelReadOnDrop { - 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), - }, - }); + 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 the readable end of a Component Model `stream`. - pub struct StreamReader { - handle: u32, - future: Option>> + 'static>>>, - _phantom: PhantomData, - } + struct CancelReadOnDrop { + handle: Option, + _phantom: PhantomData, + } - impl StreamReader { - /// 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 Drop for CancelReadOnDrop { + 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), + }, + }); + } + } + } - impl fmt::Debug for StreamReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StreamReader") - .field("handle", &self.handle) - .finish() - } - } + /// Represents the readable end of a Component Model `stream`. + pub struct StreamReader { + handle: u32, + future: Option>> + 'static>>>, + _phantom: PhantomData, + } - impl StreamReader { - #[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!() + impl StreamReader { + /// 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; } - }, - }); - - 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); + + impl fmt::Debug for StreamReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamReader") + .field("handle", &self.handle) + .finish() } - Handle::Read | Handle::LocalClosed => { - entry.remove(); + } + + impl StreamReader { + #[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, + } } - Handle::LocalReady(..) | Handle::LocalWaiting(_) | Handle::Write => unreachable!(), - }, - }); - ManuallyDrop::new(self).handle - } - } + #[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!() + } + }, + }); - impl Stream for StreamReader { - type Item = Vec; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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:: { - handle: Some(handle), - _phantom: PhantomData, - }; - Box::pin(async move { - let mut buffer = iter::repeat_with(MaybeUninit::uninit) - .take(ceiling(64 * 1024, mem::size_of::())) - .collect::>(); - - let result = if let Some(count) = T::read(handle, &mut buffer).await { - buffer.truncate(count); - Some(unsafe { - mem::transmute::>, Vec>(buffer) - }) - } else { - None - }; - cancel_on_drop.handle = None; - drop(cancel_on_drop); - result - }) as Pin>> - } - Handle::LocalOpen => { - let (tx, rx) = oneshot::channel(); - entry.insert(Handle::LocalWaiting(tx)); - let mut cancel_on_drop = CancelReadOnDrop:: { - 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()))) - } - }, - })); + ManuallyDrop::new(self).handle + } } - match me.future.as_mut().unwrap().as_mut().poll(cx) { - Poll::Ready(v) => { - me.future = None; - Poll::Ready(v) - } - Poll::Pending => Poll::Pending, - } - } - } + impl Stream for StreamReader { + type Item = Vec; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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:: { + handle: Some(handle), + _phantom: PhantomData, + }; + Box::pin(async move { + let mut buffer = iter::repeat_with(MaybeUninit::uninit) + .take(ceiling(64 * 1024, mem::size_of::())) + .collect::>(); + + let result = if let Some(count) = + T::read(handle, &mut buffer).await + { + buffer.truncate(count); + Some(unsafe { + mem::transmute::>, Vec>(buffer) + }) + } else { + None + }; + cancel_on_drop.handle = None; + drop(cancel_on_drop); + result + }) + as Pin>> + } + Handle::LocalOpen => { + let (tx, rx) = oneshot::channel(); + entry.insert(Handle::LocalWaiting(tx)); + let mut cancel_on_drop = CancelReadOnDrop:: { + 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()))) + } + }, + })); + } - impl Drop for StreamReader { - 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); + match me.future.as_mut().unwrap().as_mut().poll(cx) { + Poll::Ready(v) => { + me.future = None; + Poll::Ready(v) + } + Poll::Pending => Poll::Pending, + } } - Handle::Read | Handle::LocalClosed => { - entry.remove(); - T::close_readable(self.handle); + } + + impl Drop for StreamReader { + 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!(), + }, + }); } - Handle::Write => unreachable!(), - }, - }); - } - } + } - /// Creates a new Component Model `future` with the specified payload type. - pub fn new_future() -> (FutureWriter, FutureReader) { - let handle = T::new(); - async_support::with_entry(handle, |entry| match entry { - Entry::Vacant(entry) => { - entry.insert(Handle::LocalOpen); + /// Creates a new Component Model `future` with the specified payload type. + pub fn new_future() -> (FutureWriter, FutureReader) { + 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, + }, + ) } - 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() -> (StreamWriter, StreamReader) { - let handle = T::new(); - async_support::with_entry(handle, |entry| match entry { - Entry::Vacant(entry) => { - entry.insert(Handle::LocalOpen); + /// Creates a new Component Model `stream` with the specified payload type. + pub fn new_stream() -> (StreamWriter, StreamReader) { + 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, + }, + ) } - 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 } + fn ceiling(x: usize, y: usize) -> usize { + (x / y) + if x % y == 0 { 0 } else { 1 } + } } - }} +} #[allow(unused_imports)] pub use _rt::stream_and_future_support; @@ -1307,6 +1363,5 @@ rs\x01\x0cprocessed-by\x02\x0dwit-component\x070.221.2\x10wit-bindgen-rust\x060. #[inline(never)] #[doc(hidden)] pub fn __link_custom_section_describing_imports() { - wit_bindgen::rt::maybe_link_cabi_realloc(); + wit_bindgen::rt::maybe_link_cabi_realloc(); } - diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index b8057c8ea..bb58ecaf8 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -1,3 +1,5 @@ +use std::{ffi::c_int, sync::{atomic::AtomicU32, Arc, Mutex}}; + use executor::exports::symmetric::runtime::symmetric_executor; mod executor; @@ -15,7 +17,7 @@ impl symmetric_executor::GuestEventSubscription for EventSubscription { todo!() } - fn from_timeout(_nanoseconds: u64) -> symmetric_executor::EventSubscription { + fn from_timeout(nanoseconds: u64) -> symmetric_executor::EventSubscription { todo!() } } @@ -45,14 +47,22 @@ impl symmetric_executor::Guest for Guest { } fn register( - _trigger: symmetric_executor::EventSubscription, - _callback: symmetric_executor::CallbackFunction, - _data: symmetric_executor::CallbackData, + trigger: symmetric_executor::EventSubscription, + callback: symmetric_executor::CallbackFunction, + data: symmetric_executor::CallbackData, ) -> () { todo!() } } +type EventFd = c_int; +type Count = u32; + +struct EventInner { + counter: Count, + waiting: Vec, +} + struct EventGenerator { } @@ -60,3 +70,11 @@ struct EventGenerator { struct EventSubscription { } + +enum EventType { + Manual { + last_counter: AtomicU32, + event_fd: EventFd, + object: Arc>, + } +} From 0d1408ed595edf4cbf1176a4759c1e21cbe215f6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 7 Dec 2024 17:52:48 +0100 Subject: [PATCH 400/672] incomplete main implementation --- crates/cpp/tests/symmetric_async/Cargo.toml | 2 +- .../symmetric_async/async_module/src/lib.rs | 20 +++++++++++ .../cpp/tests/symmetric_async/main/Cargo.toml | 8 +++++ .../cpp/tests/symmetric_async/main/build.rs | 9 +++++ .../tests/symmetric_async/main/src/main.rs | 34 +++++++++++++++++++ 5 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 crates/cpp/tests/symmetric_async/main/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_async/main/build.rs create mode 100644 crates/cpp/tests/symmetric_async/main/src/main.rs diff --git a/crates/cpp/tests/symmetric_async/Cargo.toml b/crates/cpp/tests/symmetric_async/Cargo.toml index 913ec0ab6..50596891a 100644 --- a/crates/cpp/tests/symmetric_async/Cargo.toml +++ b/crates/cpp/tests/symmetric_async/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = [ "async_module","sleep"] +members = [ "async_module", "main","sleep"] resolver = "2" diff --git a/crates/cpp/tests/symmetric_async/async_module/src/lib.rs b/crates/cpp/tests/symmetric_async/async_module/src/lib.rs index ce4f2425c..8df42e0dd 100644 --- a/crates/cpp/tests/symmetric_async/async_module/src/lib.rs +++ b/crates/cpp/tests/symmetric_async/async_module/src/lib.rs @@ -1 +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 forward( + s: String, + ) -> String { + match s.as_str() { + "A" => "directly returned".into(), + "B" => { async_module::test::test::wait::sleep(5_000_000_000).await; + "after five seconds".into() + } + _ => { async_module::test::test::wait::sleep(1_000_000_000).await; + "after one second".into() + } + } + } +} 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..6d3dc57b8 --- /dev/null +++ b/crates/cpp/tests/symmetric_async/main/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "main" +version = "0.1.0" +edition = "2021" + +[dependencies] +async_module = { version = "0.1.0", path = "../async_module" } +symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor" } 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..508f5f2cc --- /dev/null +++ b/crates/cpp/tests/symmetric_async/main/src/main.rs @@ -0,0 +1,34 @@ +#[link(name = "async_module")] +extern "C" { + pub fn X5BasyncX5DtestX3AtestX2Fstring_delayX00forward(args: *const (), results: *mut (),) -> *mut (); +} + +fn main() { + let argument1: [usize;2] = ["A".as_ptr()as usize, 1]; + let mut result1: [usize;2] = [0,0]; + let handle1 = unsafe { + X5BasyncX5DtestX3AtestX2Fstring_delayX00forward((&argument1 as *const usize).cast(), 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 argument2: [usize;2] = ["B".as_ptr()as usize, 1]; + let mut result2: [usize;2] = [0,0]; + let handle2 = unsafe { + X5BasyncX5DtestX3AtestX2Fstring_delayX00forward((&argument2 as *const usize).cast(), result2.as_mut_ptr().cast()) + }; + assert_ne!(handle2, core::ptr::null_mut()); + // register cb + + let argument3: [usize;2] = ["C".as_ptr()as usize, 1]; + let mut result3: [usize;2] = [0,0]; + let handle3 = unsafe { + X5BasyncX5DtestX3AtestX2Fstring_delayX00forward((&argument3 as *const usize).cast(), result3.as_mut_ptr().cast()) + }; + assert_ne!(handle3, core::ptr::null_mut()); + // register cb + + // run +} From f10ed058fb01031fae4f1c8afbaca81049eee05c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Dec 2024 00:03:57 +0100 Subject: [PATCH 401/672] module implementation start --- .../async_module/src/async_module.rs | 56 ++++++++++--------- 1 file changed, 30 insertions(+), 26 deletions(-) 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 index dcf73a4a0..00ed62198 100644 --- a/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs +++ b/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs @@ -58,39 +58,43 @@ pub mod exports { #[allow(non_snake_case)] pub unsafe fn _export_forward_cabi( arg0: *mut u8, - arg1: usize, arg2: *mut u8, ) -> *mut u8 { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); - let len0 = arg1; + let arguments = arg0.cast_const().cast::(); + let len0 = unsafe{*(arguments.add(1))}; + let addr0 = unsafe{*arguments} as *mut u8; let string0 = String::from( - std::str::from_utf8(std::slice::from_raw_parts(arg0, len0)).unwrap(), + std::str::from_utf8(std::slice::from_raw_parts(addr0, len0)).unwrap(), ); let result1 = T::forward(string0); let result = ::wit_bindgen_symmetric_rt::async_support::first_poll( result1, - |result2| { + move |result2| { let vec3 = (result2.into_bytes()).into_boxed_slice(); let ptr3 = vec3.as_ptr().cast::(); let len3 = vec3.len(); ::core::mem::forget(vec3); - - #[link(wasm_import_module = "[export]test:test/string-delay")] - extern "C" { - #[cfg_attr( - target_arch = "wasm32", - link_name = "[task-return]forward" - )] - fn X5BexportX5DtestX3AtestX2Fstring_delayX00X5Btask_returnX5Dforward( - _: *mut u8, - _: usize, - ); - } - X5BexportX5DtestX3AtestX2Fstring_delayX00X5Btask_returnX5Dforward( - ptr3.cast_mut(), - len3, - ); + let output = arg2.cast::(); + *unsafe {&mut *output} = ptr3 as usize; + *unsafe {&mut *output.add(1)} = len3; + + // #[link(wasm_import_module = "[export]test:test/string-delay")] + // extern "C" { + // #[cfg_attr( + // target_arch = "wasm32", + // link_name = "[task-return]forward" + // )] + // fn X5BexportX5DtestX3AtestX2Fstring_delayX00X5Btask_returnX5Dforward( + // _: *mut u8, + // _: usize, + // ); + // } + // X5BexportX5DtestX3AtestX2Fstring_delayX00X5Btask_returnX5Dforward( + // ptr3.cast_mut(), + // len3, + // ); }, ); @@ -118,13 +122,13 @@ pub mod exports { #[cfg_attr(target_arch = "wasm32", export_name = "forward")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn testX3AtestX2Fstring_delayX00forward(arg0: *mut u8,arg1: usize,arg2: *mut u8,) -> *mut u8 { - $($path_to_types)*::_export_forward_cabi::<$ty>(arg0, arg1, arg2) - } - #[export_name = "[callback]forward"] - unsafe extern "C" fn _callback_forward(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { - $($path_to_types)*::__callback_forward(ctx, event0, event1, event2) + unsafe extern "C" fn X5BasyncX5DtestX3AtestX2Fstring_delayX00forward(arg0: *mut u8,arg1: *mut u8,) -> *mut u8 { + $($path_to_types)*::_export_forward_cabi::<$ty>(arg0, arg1,) } + // #[export_name = "[callback]forward"] + // unsafe extern "C" fn _callback_forward(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + // $($path_to_types)*::__callback_forward(ctx, event0, event1, event2) + // } };); } #[doc(hidden)] From 7da80c85b3142e7b6562c5cef463f937d424c987 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Dec 2024 01:00:35 +0100 Subject: [PATCH 402/672] more implementation --- .../symmetric_async/async_module/build.rs | 9 +++ .../async_module/src/async_module.rs | 1 + .../rust-client/src/async_support.rs | 61 +++++++++++++++++-- 3 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 crates/cpp/tests/symmetric_async/async_module/build.rs 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 index 00ed62198..3f4fc1f20 100644 --- a/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs +++ b/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs @@ -22,6 +22,7 @@ pub mod test { let ptr1 = _rt::alloc::alloc(layout1); #[link(wasm_import_module = "test:test/wait")] + #[link(name="sleep")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "[async]sleep")] fn testX3AtestX2FwaitX00X5BasyncX5Dsleep(_: *mut u8, _: *mut u8) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 240bdf571..4bc667231 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -1,5 +1,17 @@ -use futures::{channel::oneshot, task::Waker}; -use std::{alloc::Layout, any::Any, collections::hash_map, future::Future}; +use futures::{channel::oneshot, task::Waker, FutureExt}; +use std::{alloc::Layout, any::Any, collections::hash_map, future::Future, pin::Pin, task::{Context, Poll, RawWaker, RawWakerVTable}}; + +use crate::module::symmetric::runtime::symmetric_executor::EventGenerator; + +use super::module::symmetric::runtime::symmetric_executor::EventSubscription; + +type BoxFuture = Pin + 'static>>; + +struct FutureState { + future: BoxFuture, + sender: Option, + active_listener: Option, +} #[doc(hidden)] pub enum Handle { @@ -12,16 +24,53 @@ pub enum Handle { } #[doc(hidden)] -pub fn with_entry(h: u32, f: impl FnOnce(hash_map::Entry<'_, u32, Handle>) -> T) -> T { +pub fn with_entry(_h: u32, _f: impl FnOnce(hash_map::Entry<'_, u32, Handle>) -> T) -> T { todo!() } +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(call: *mut Option) -> Waker { + unsafe { Waker::from_raw(RawWaker::new(call.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).active_listener as *mut Option); + pinned.as_mut().poll(&mut Context::from_waker(&waker)) + .map(|()| { + let dummy = Box::from_raw(state); + if let Some(waker) = dummy.sender { + waker.activate(); + } + }) +} + #[doc(hidden)] pub fn first_poll( - _future: impl Future + 'static, - _fun: impl FnOnce(T) + 'static, + future: impl Future + 'static, + fun: impl FnOnce(T) + 'static, ) -> *mut u8 { - todo!() + let state = Box::into_raw(Box::new(FutureState { + future: Box::pin(future.map(fun)), + sender: None, + active_listener: None, + })); + match unsafe { poll(state) } { + Poll::Ready(()) => core::ptr::null_mut(), + Poll::Pending => { + todo!() + } + } } #[doc(hidden)] From 3de2d9b16c36181866d57c386be4284011505c57 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Dec 2024 01:15:43 +0100 Subject: [PATCH 403/672] more poll impl --- .../rust-client/src/async_support.rs | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 4bc667231..4b333bf8d 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -1,7 +1,14 @@ use futures::{channel::oneshot, task::Waker, FutureExt}; -use std::{alloc::Layout, any::Any, collections::hash_map, future::Future, pin::Pin, task::{Context, Poll, RawWaker, RawWakerVTable}}; +use std::{ + alloc::Layout, + any::Any, + collections::hash_map, + future::Future, + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable}, +}; -use crate::module::symmetric::runtime::symmetric_executor::EventGenerator; +use crate::module::symmetric::runtime::{self, symmetric_executor::EventGenerator}; use super::module::symmetric::runtime::symmetric_executor::EventSubscription; @@ -9,8 +16,8 @@ type BoxFuture = Pin + 'static>>; struct FutureState { future: BoxFuture, - sender: Option, - active_listener: Option, + trigger: Option, + active_subscription: Option, } #[doc(hidden)] @@ -42,17 +49,18 @@ pub fn new_waker(call: *mut Option) -> Waker { unsafe { Waker::from_raw(RawWaker::new(call.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).active_listener as *mut Option); - pinned.as_mut().poll(&mut Context::from_waker(&waker)) - .map(|()| { - let dummy = Box::from_raw(state); - if let Some(waker) = dummy.sender { - waker.activate(); - } - }) + let waker = new_waker(&mut (&mut *state).active_subscription as *mut Option); + pinned + .as_mut() + .poll(&mut Context::from_waker(&waker)) + .map(|()| { + let dummy = Box::from_raw(state); + if let Some(waker) = dummy.trigger { + waker.activate(); + } + }) } #[doc(hidden)] @@ -62,13 +70,21 @@ pub fn first_poll( ) -> *mut u8 { let state = Box::into_raw(Box::new(FutureState { future: Box::pin(future.map(fun)), - sender: None, - active_listener: None, + trigger: None, + active_subscription: None, })); match unsafe { poll(state) } { Poll::Ready(()) => core::ptr::null_mut(), Poll::Pending => { - todo!() + let trigger = EventGenerator::default(); + let subscription = unsafe { &mut *state }.active_subscription.take(); + assert!(!subscription.is_none()); + runtime::symmetric_executor::register(subscription.unwrap(), callback, unsafe { + runtime::symmetric_executor::CallbackData::from_handle(state.cast()) + }); + let handle = trigger.subscribe().0.take_handle() as *mut (); + unsafe { &mut *state }.trigger.replace(trigger); + handle } } } From f9c50fc74b95df63acdadc89c033b1072c87c8ec Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Dec 2024 15:30:42 +0100 Subject: [PATCH 404/672] poll logic --- .../async_module/src/async_module.rs | 2 +- .../rust-client/src/async_support.rs | 55 ++++++++++++------- .../symmetric_executor/rust-client/src/lib.rs | 14 +++++ 3 files changed, 50 insertions(+), 21 deletions(-) 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 index 3f4fc1f20..d45beca53 100644 --- a/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs +++ b/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs @@ -99,7 +99,7 @@ pub mod exports { }, ); - result + result.cast() } #[doc(hidden)] #[allow(non_snake_case)] diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 4b333bf8d..9549d2c5c 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -8,16 +8,21 @@ use std::{ task::{Context, Poll, RawWaker, RawWakerVTable}, }; -use crate::module::symmetric::runtime::{self, symmetric_executor::EventGenerator}; - -use super::module::symmetric::runtime::symmetric_executor::EventSubscription; +use crate::module::symmetric::runtime::{ + self, + symmetric_executor::{ + self, CallbackData, CallbackFunction, CallbackState, EventGenerator, EventSubscription, + }, +}; type BoxFuture = Pin + 'static>>; struct FutureState { future: BoxFuture, - trigger: Option, - active_subscription: Option, + // signal to activate once the current async future has finished + completion_event: Option, + // the event this future should wake on + waiting_for: Option, } #[doc(hidden)] @@ -51,40 +56,50 @@ pub fn new_waker(call: *mut Option) -> Waker { unsafe fn poll(state: *mut FutureState) -> Poll<()> { let mut pinned = std::pin::pin!(&mut (*state).future); - let waker = new_waker(&mut (&mut *state).active_subscription as *mut Option); + let waker = new_waker(&mut (&mut *state).waiting_for as *mut Option); pinned .as_mut() .poll(&mut Context::from_waker(&waker)) .map(|()| { - let dummy = Box::from_raw(state); - if let Some(waker) = dummy.trigger { + let state_owned = Box::from_raw(state); + if let Some(waker) = &state_owned.completion_event { waker.activate(); } + drop(state_owned); }) } +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::(); + let waiting_for = unsafe { &mut *state }.waiting_for.take(); + super::register(waiting_for.unwrap(), symmetric_callback, obj); + CallbackState::Pending + } + } +} + #[doc(hidden)] pub fn first_poll( future: impl Future + 'static, fun: impl FnOnce(T) + 'static, -) -> *mut u8 { +) -> *mut () { let state = Box::into_raw(Box::new(FutureState { future: Box::pin(future.map(fun)), - trigger: None, - active_subscription: None, + completion_event: None, + waiting_for: None, })); match unsafe { poll(state) } { Poll::Ready(()) => core::ptr::null_mut(), Poll::Pending => { - let trigger = EventGenerator::default(); - let subscription = unsafe { &mut *state }.active_subscription.take(); - assert!(!subscription.is_none()); - runtime::symmetric_executor::register(subscription.unwrap(), callback, unsafe { - runtime::symmetric_executor::CallbackData::from_handle(state.cast()) - }); - let handle = trigger.subscribe().0.take_handle() as *mut (); - unsafe { &mut *state }.trigger.replace(trigger); - handle + 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 } } } diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index 891e3c6eb..e1bd52586 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -1,2 +1,16 @@ +use module::symmetric::runtime::symmetric_executor::{ + self, CallbackData, CallbackFunction, CallbackState, EventSubscription, +}; + 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); +} From 3d823f4b8b4004493130d5ce7517921578c55543 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Dec 2024 16:22:13 +0100 Subject: [PATCH 405/672] fix linking and full main implementation --- .../symmetric_async/async_module/Cargo.toml | 1 + .../cpp/tests/symmetric_async/main/Cargo.toml | 1 + .../tests/symmetric_async/main/src/main.rs | 60 ++++++++++++++----- .../rust-client/src/async_support.rs | 4 +- .../symmetric_executor/rust-client/src/lib.rs | 3 +- .../rust-client/src/module.rs | 4 ++ 6 files changed, 57 insertions(+), 16 deletions(-) diff --git a/crates/cpp/tests/symmetric_async/async_module/Cargo.toml b/crates/cpp/tests/symmetric_async/async_module/Cargo.toml index 1f3cd95b8..9589e932d 100644 --- a/crates/cpp/tests/symmetric_async/async_module/Cargo.toml +++ b/crates/cpp/tests/symmetric_async/async_module/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" [dependencies] futures = "0.3.31" sleep = { version = "0.1.0", path = "../sleep" } +symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor" } wit-bindgen = { version = "0.36.0", path = "../../../../guest-rust" } wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../../../../symmetric_executor/rust-client" } diff --git a/crates/cpp/tests/symmetric_async/main/Cargo.toml b/crates/cpp/tests/symmetric_async/main/Cargo.toml index 6d3dc57b8..455e9ed0f 100644 --- a/crates/cpp/tests/symmetric_async/main/Cargo.toml +++ b/crates/cpp/tests/symmetric_async/main/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" [dependencies] async_module = { version = "0.1.0", path = "../async_module" } symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor" } +wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../../../../symmetric_executor/rust-client" } diff --git a/crates/cpp/tests/symmetric_async/main/src/main.rs b/crates/cpp/tests/symmetric_async/main/src/main.rs index 508f5f2cc..2bfef7dbb 100644 --- a/crates/cpp/tests/symmetric_async/main/src/main.rs +++ b/crates/cpp/tests/symmetric_async/main/src/main.rs @@ -1,34 +1,66 @@ +use wit_bindgen_symmetric_rt::{CallbackState, EventSubscription}; + #[link(name = "async_module")] extern "C" { - pub fn X5BasyncX5DtestX3AtestX2Fstring_delayX00forward(args: *const (), results: *mut (),) -> *mut (); + pub fn X5BasyncX5DtestX3AtestX2Fstring_delayX00forward( + args: *const (), + 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::() }; + 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 argument1: [usize;2] = ["A".as_ptr()as usize, 1]; - let mut result1: [usize;2] = [0,0]; + let argument1: [usize; 2] = ["A".as_ptr() as usize, 1]; + let mut result1: [usize; 2] = [0, 0]; let handle1 = unsafe { - X5BasyncX5DtestX3AtestX2Fstring_delayX00forward((&argument1 as *const usize).cast(), result1.as_mut_ptr().cast()) + X5BasyncX5DtestX3AtestX2Fstring_delayX00forward( + (&argument1 as *const usize).cast(), + 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 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 argument2: [usize;2] = ["B".as_ptr()as usize, 1]; - let mut result2: [usize;2] = [0,0]; + let argument2: [usize; 2] = ["B".as_ptr() as usize, 1]; + let mut result2: [usize; 2] = [0, 0]; let handle2 = unsafe { - X5BasyncX5DtestX3AtestX2Fstring_delayX00forward((&argument2 as *const usize).cast(), result2.as_mut_ptr().cast()) + X5BasyncX5DtestX3AtestX2Fstring_delayX00forward( + (&argument2 as *const usize).cast(), + result2.as_mut_ptr().cast(), + ) }; assert_ne!(handle2, core::ptr::null_mut()); - // register cb + wit_bindgen_symmetric_rt::register( + unsafe { EventSubscription::from_handle(handle2 as usize) }, + print_result, + result2.as_mut_ptr().cast(), + ); - let argument3: [usize;2] = ["C".as_ptr()as usize, 1]; - let mut result3: [usize;2] = [0,0]; + let argument3: [usize; 2] = ["C".as_ptr() as usize, 1]; + let mut result3: [usize; 2] = [0, 0]; let handle3 = unsafe { - X5BasyncX5DtestX3AtestX2Fstring_delayX00forward((&argument3 as *const usize).cast(), result3.as_mut_ptr().cast()) + X5BasyncX5DtestX3AtestX2Fstring_delayX00forward( + (&argument3 as *const usize).cast(), + result3.as_mut_ptr().cast(), + ) }; assert_ne!(handle3, core::ptr::null_mut()); - // register cb + wit_bindgen_symmetric_rt::register( + unsafe { EventSubscription::from_handle(handle3 as usize) }, + print_result, + result3.as_mut_ptr().cast(), + ); - // run + wit_bindgen_symmetric_rt::run(); } diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 9549d2c5c..d138d09fe 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -96,7 +96,9 @@ pub fn first_poll( 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); + 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 diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index e1bd52586..b268499db 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -1,6 +1,7 @@ use module::symmetric::runtime::symmetric_executor::{ - self, CallbackData, CallbackFunction, CallbackState, EventSubscription, + self, CallbackData, CallbackFunction, }; +pub use module::symmetric::runtime::symmetric_executor::{CallbackState, EventSubscription,run}; pub mod async_support; mod module; diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index 96e8b10cf..c843f1f7f 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -360,6 +360,10 @@ pub mod symmetric { 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", From 8b001bcdc6245460809062f50661db3a94fe926a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Dec 2024 17:49:10 +0100 Subject: [PATCH 406/672] timeout creation --- .../async_module/src/async_module.rs | 32 ++++++++-------- .../symmetric_async/async_module/src/lib.rs | 10 ++--- .../rust-client/src/async_support.rs | 38 +++++++++++++++---- .../symmetric_executor/rust-client/src/lib.rs | 6 +-- .../rust-client/src/module.rs | 5 +-- crates/symmetric_executor/src/lib.rs | 33 ++++++++++------ 6 files changed, 77 insertions(+), 47 deletions(-) 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 index d45beca53..5ea6409c1 100644 --- a/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs +++ b/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs @@ -11,32 +11,34 @@ pub mod test { static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; - use super::super::super::_rt; + // use super::super::super::_rt; #[allow(unused_unsafe, clippy::all)] pub async fn sleep(nanoseconds: u64) -> () { unsafe { - let layout0 = _rt::alloc::Layout::from_size_align_unchecked(8, 8); - let ptr0 = _rt::alloc::alloc(layout0); - *ptr0.add(0).cast::() = _rt::as_i64(&nanoseconds); - let layout1 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); - let ptr1 = _rt::alloc::alloc(layout1); + //let layout0 = _rt::alloc::Layout::from_size_align_unchecked(8, 8); + let ptr0 = (&nanoseconds) as *const u64; + //_rt::alloc::alloc(layout0); + // *ptr0.add(0).cast::() = _rt::as_i64(&nanoseconds); + // let layout1 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); + let ptr1 = core::ptr::null_mut(); + // _rt::alloc::alloc(layout1); #[link(wasm_import_module = "test:test/wait")] - #[link(name="sleep")] + #[link(name = "sleep")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "[async]sleep")] fn testX3AtestX2FwaitX00X5BasyncX5Dsleep(_: *mut u8, _: *mut u8) -> *mut u8; } - let layout2 = _rt::alloc::Layout::from_size_align_unchecked(8, 8); + // let layout2 = _rt::alloc::Layout::from_size_align_unchecked(8, 8); ::wit_bindgen_symmetric_rt::async_support::await_result( testX3AtestX2FwaitX00X5BasyncX5Dsleep, - layout2, - ptr0, + // layout2, + ptr0.cast_mut().cast(), ptr1, ) .await; - _rt::cabi_dealloc(ptr1, 0, 1); + // _rt::cabi_dealloc(ptr1, 0, 1); } } } @@ -64,8 +66,8 @@ pub mod exports { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); let arguments = arg0.cast_const().cast::(); - let len0 = unsafe{*(arguments.add(1))}; - let addr0 = unsafe{*arguments} as *mut u8; + let len0 = unsafe { *(arguments.add(1)) }; + let addr0 = unsafe { *arguments } as *mut u8; let string0 = String::from( std::str::from_utf8(std::slice::from_raw_parts(addr0, len0)).unwrap(), ); @@ -78,8 +80,8 @@ pub mod exports { let len3 = vec3.len(); ::core::mem::forget(vec3); let output = arg2.cast::(); - *unsafe {&mut *output} = ptr3 as usize; - *unsafe {&mut *output.add(1)} = len3; + *unsafe { &mut *output } = ptr3 as usize; + *unsafe { &mut *output.add(1) } = len3; // #[link(wasm_import_module = "[export]test:test/string-delay")] // extern "C" { diff --git a/crates/cpp/tests/symmetric_async/async_module/src/lib.rs b/crates/cpp/tests/symmetric_async/async_module/src/lib.rs index 8df42e0dd..d0e8b5fdb 100644 --- a/crates/cpp/tests/symmetric_async/async_module/src/lib.rs +++ b/crates/cpp/tests/symmetric_async/async_module/src/lib.rs @@ -5,15 +5,15 @@ async_module::export!(Guest with_types_in async_module); struct Guest; impl async_module::exports::test::test::string_delay::Guest for Guest { - async fn forward( - s: String, - ) -> String { + async fn forward(s: String) -> String { match s.as_str() { "A" => "directly returned".into(), - "B" => { async_module::test::test::wait::sleep(5_000_000_000).await; + "B" => { + async_module::test::test::wait::sleep(5_000_000_000).await; "after five seconds".into() } - _ => { async_module::test::test::wait::sleep(1_000_000_000).await; + _ => { + async_module::test::test::wait::sleep(1_000_000_000).await; "after one second".into() } } diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index d138d09fe..377bbaab4 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -50,8 +50,8 @@ static VTABLE: RawWakerVTable = RawWakerVTable::new( |_| {}, ); -pub fn new_waker(call: *mut Option) -> Waker { - unsafe { Waker::from_raw(RawWaker::new(call.cast(), &VTABLE)) } +pub fn new_waker(waiting_for_ptr: *mut Option) -> Waker { + unsafe { Waker::from_raw(RawWaker::new(waiting_for_ptr.cast(), &VTABLE)) } } unsafe fn poll(state: *mut FutureState) -> Poll<()> { @@ -69,6 +69,18 @@ unsafe fn poll(state: *mut FutureState) -> Poll<()> { }) } +async fn wait_on(wait_for: &EventSubscription) { + std::future::poll_fn(move |cx| { + if wait_for.ready() { + Poll::Ready(()) + } else { + todo!(); + Poll::Pending + } + }) + .await +} + extern "C" fn symmetric_callback(obj: *mut ()) -> symmetric_executor::CallbackState { match unsafe { poll(obj.cast()) } { Poll::Ready(_) => CallbackState::Ready, @@ -81,6 +93,12 @@ extern "C" fn symmetric_callback(obj: *mut ()) -> symmetric_executor::CallbackSt } } +/// 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( future: impl Future + 'static, @@ -106,14 +124,20 @@ pub fn first_poll( } } +/// Await the completion of a call to an async-lowered import. #[doc(hidden)] pub async unsafe fn await_result( - _import: unsafe extern "C" fn(*mut u8, *mut u8) -> *mut u8, - _params_layout: Layout, - _params: *mut u8, - _results: *mut u8, + function: unsafe extern "C" fn(*mut u8, *mut u8) -> *mut u8, + // _params_layout: Layout, + params: *mut u8, + results: *mut u8, ) { - todo!() + let wait_for = function(params, results); + if !wait_for.is_null() { + let wait_for = unsafe { EventSubscription::from_handle(wait_for as usize) }; + wait_on(&wait_for).await; + let _ = wait_for.take_handle(); + } } #[doc(hidden)] diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index b268499db..644a404f1 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -1,7 +1,5 @@ -use module::symmetric::runtime::symmetric_executor::{ - self, CallbackData, CallbackFunction, -}; -pub use module::symmetric::runtime::symmetric_executor::{CallbackState, EventSubscription,run}; +use module::symmetric::runtime::symmetric_executor::{self, CallbackData, CallbackFunction}; +pub use module::symmetric::runtime::symmetric_executor::{run, CallbackState, EventSubscription}; pub mod async_support; mod module; diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index c843f1f7f..c748976b2 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -360,10 +360,7 @@ pub mod symmetric { 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"), - )] + #[cfg_attr(not(target_arch = "wasm32"), link(name = "symmetric_executor"))] extern "C" { #[cfg_attr( target_arch = "wasm32", diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index bb58ecaf8..049957ea2 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -1,6 +1,9 @@ -use std::{ffi::c_int, sync::{atomic::AtomicU32, Arc, Mutex}}; +use std::{ + ffi::c_int, + sync::{atomic::AtomicU32, Arc, Mutex}, time::{Duration, SystemTime}, +}; -use executor::exports::symmetric::runtime::symmetric_executor; +use executor::exports::symmetric::runtime::symmetric_executor::{self, CallbackData, CallbackState}; mod executor; @@ -14,11 +17,15 @@ impl symmetric_executor::GuestCallbackData for Ignore {} impl symmetric_executor::GuestEventSubscription for EventSubscription { fn ready(&self) -> bool { - todo!() + match &self.inner { + EventType::Triggered { last_counter: _, event_fd: _, object: _ } => todo!(), + EventType::SystemTime(system_time) => *system_time <= SystemTime::now(), + } } fn from_timeout(nanoseconds: u64) -> symmetric_executor::EventSubscription { - todo!() + let when = SystemTime::now() + Duration::from_nanos(nanoseconds); + symmetric_executor::EventSubscription::new(EventSubscription{ inner: EventType::SystemTime(when), callback: None }) } } @@ -37,10 +44,10 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { } impl symmetric_executor::Guest for Guest { - type CallbackFunction=Ignore; - type CallbackData=Ignore; + type CallbackFunction = Ignore; + type CallbackData = Ignore; type EventSubscription = EventSubscription; - type EventGenerator= EventGenerator; + type EventGenerator = EventGenerator; fn run() -> () { todo!() @@ -63,18 +70,20 @@ struct EventInner { waiting: Vec, } -struct EventGenerator { +struct EventGenerator {} -} +type CallbackEntry = (fn(*mut ()) -> CallbackState, CallbackData); struct EventSubscription { - + inner: EventType, + callback: Option, } enum EventType { - Manual { + Triggered { last_counter: AtomicU32, event_fd: EventFd, object: Arc>, - } + }, + SystemTime(SystemTime), } From 9ca80e0167531d544f82f1381c9f1c4567080017 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Dec 2024 17:58:50 +0100 Subject: [PATCH 407/672] implement wait_for --- .../rust-client/src/async_support.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 377bbaab4..b680a2af5 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -74,7 +74,15 @@ async fn wait_on(wait_for: &EventSubscription) { if wait_for.ready() { Poll::Ready(()) } else { - todo!(); + let data = cx.waker().data(); + // dangerous duplication? + let wait_for_copy = unsafe { EventSubscription::from_handle(wait_for.handle()) }; + let old_waiting_for = unsafe { &mut *(data.cast::>().cast_mut()) } + .replace(wait_for_copy); + // don't free the old subscription we found + if let Some(subscription) = old_waiting_for { + subscription.take_handle(); + } Poll::Pending } }) From 41776f343fc1aa2e77f24bf0414e40382a2534a1 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Dec 2024 18:06:17 +0100 Subject: [PATCH 408/672] event creation --- crates/symmetric_executor/src/lib.rs | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 049957ea2..eb92e88a7 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -1,9 +1,12 @@ use std::{ ffi::c_int, - sync::{atomic::AtomicU32, Arc, Mutex}, time::{Duration, SystemTime}, + sync::{atomic::AtomicU32, Arc, Mutex}, + time::{Duration, SystemTime}, }; -use executor::exports::symmetric::runtime::symmetric_executor::{self, CallbackData, CallbackState}; +use executor::exports::symmetric::runtime::symmetric_executor::{ + self, CallbackData, CallbackState, +}; mod executor; @@ -18,20 +21,30 @@ impl symmetric_executor::GuestCallbackData for Ignore {} impl symmetric_executor::GuestEventSubscription for EventSubscription { fn ready(&self) -> bool { match &self.inner { - EventType::Triggered { last_counter: _, event_fd: _, object: _ } => todo!(), + EventType::Triggered { + last_counter: _, + event_fd: _, + object: _, + } => todo!(), EventType::SystemTime(system_time) => *system_time <= SystemTime::now(), } } 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), callback: None }) + symmetric_executor::EventSubscription::new(EventSubscription { + inner: EventType::SystemTime(when), + callback: None, + }) } } impl symmetric_executor::GuestEventGenerator for EventGenerator { fn new() -> Self { - todo!() + Self(Arc::new(Mutex::new(EventInner { + counter: 0, + waiting: Default::default(), + }))) } fn subscribe(&self) -> symmetric_executor::EventSubscription { @@ -70,7 +83,7 @@ struct EventInner { waiting: Vec, } -struct EventGenerator {} +struct EventGenerator(Arc>); type CallbackEntry = (fn(*mut ()) -> CallbackState, CallbackData); From 4c19fbd4556d375d5b84247c1097f68c0f5c20ba Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Dec 2024 18:11:24 +0100 Subject: [PATCH 409/672] subscribe --- crates/symmetric_executor/src/lib.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index eb92e88a7..92b1cf909 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -48,7 +48,16 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { } fn subscribe(&self) -> symmetric_executor::EventSubscription { - todo!() + let event_fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }; + self.0.lock().unwrap().waiting.push(event_fd); + symmetric_executor::EventSubscription::new(EventSubscription { + inner: EventType::Triggered { + last_counter: AtomicU32::new(0), + event_fd, + object: Arc::clone(&self.0), + }, + callback: None, + }) } fn activate(&self) -> () { From dddc8cd4dd9e973ba4af9a014c234c22eaabf9e7 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Dec 2024 18:26:03 +0100 Subject: [PATCH 410/672] implement register (incomplete) --- crates/symmetric_executor/src/lib.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 92b1cf909..20f67e2bd 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -1,5 +1,6 @@ use std::{ ffi::c_int, + mem::transmute, sync::{atomic::AtomicU32, Arc, Mutex}, time::{Duration, SystemTime}, }; @@ -65,6 +66,14 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { } } +struct Executor { + active_tasks: Vec, +} + +static EXECUTOR: Mutex = Mutex::new(Executor { + active_tasks: Vec::new(), +}); + impl symmetric_executor::Guest for Guest { type CallbackFunction = Ignore; type CallbackData = Ignore; @@ -80,7 +89,11 @@ impl symmetric_executor::Guest for Guest { callback: symmetric_executor::CallbackFunction, data: symmetric_executor::CallbackData, ) -> () { - todo!() + let mut subscr = unsafe { *(trigger.take_handle() as *mut EventSubscription) }; + let cb: fn(*mut ()) -> CallbackState = unsafe { transmute(callback.take_handle()) }; + // let data = data.take_handle() as *mut (); + subscr.callback.replace((cb, data)); + EXECUTOR.lock().unwrap().active_tasks.push(subscr); } } From 1a71fcfc5f459321ad279959f5ac642e456a106c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Dec 2024 22:01:53 +0100 Subject: [PATCH 411/672] move subscription object out --- crates/symmetric_executor/src/lib.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 20f67e2bd..9527eff9d 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -89,9 +89,15 @@ impl symmetric_executor::Guest for Guest { callback: symmetric_executor::CallbackFunction, data: symmetric_executor::CallbackData, ) -> () { - let mut subscr = unsafe { *(trigger.take_handle() as *mut EventSubscription) }; + // TODO: Tidy this mess up + let mut subscr = EventSubscription { + inner: EventType::SystemTime(std::time::UNIX_EPOCH), + callback: None, + }; + std::mem::swap(&mut subscr, unsafe { + &mut *(trigger.take_handle() as *mut EventSubscription) + }); let cb: fn(*mut ()) -> CallbackState = unsafe { transmute(callback.take_handle()) }; - // let data = data.take_handle() as *mut (); subscr.callback.replace((cb, data)); EXECUTOR.lock().unwrap().active_tasks.push(subscr); } From 200a8c785f65c8451a754a6c37e91632694dac9e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Dec 2024 22:57:33 +0100 Subject: [PATCH 412/672] implement wait and triggering --- crates/symmetric_executor/src/lib.rs | 138 +++++++++++++++++++++++++-- 1 file changed, 128 insertions(+), 10 deletions(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 9527eff9d..674cede38 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -6,7 +6,7 @@ use std::{ }; use executor::exports::symmetric::runtime::symmetric_executor::{ - self, CallbackData, CallbackState, + self, CallbackData, CallbackState, GuestEventSubscription, }; mod executor; @@ -23,10 +23,18 @@ impl symmetric_executor::GuestEventSubscription for EventSubscription { fn ready(&self) -> bool { match &self.inner { EventType::Triggered { - last_counter: _, + last_counter, event_fd: _, - object: _, - } => todo!(), + event, + } => { + let current_counter = event.lock().unwrap().counter; + let active = + current_counter != last_counter.load(std::sync::atomic::Ordering::Acquire); + if active { + last_counter.store(current_counter, std::sync::atomic::Ordering::Release); + } + active + } EventType::SystemTime(system_time) => *system_time <= SystemTime::now(), } } @@ -55,14 +63,32 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { inner: EventType::Triggered { last_counter: AtomicU32::new(0), event_fd, - object: Arc::clone(&self.0), + event: Arc::clone(&self.0), }, callback: None, }) } - fn activate(&self) -> () { - todo!() + fn activate(&self) { + if let Ok(mut event) = self.0.lock() { + event.counter += 1; + let file_signal: u64 = 1; + event.waiting.iter().for_each(|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() + ); + } + }); + } } } @@ -80,8 +106,100 @@ impl symmetric_executor::Guest for Guest { type EventSubscription = EventSubscription; type EventGenerator = EventGenerator; - fn run() -> () { - todo!() + fn run() { + loop { + let mut wait = libc::timeval { + tv_sec: i64::MAX, + tv_usec: 999999, + }; + let mut tvptr = core::ptr::null_mut(); + let mut maxfd = 0; + let now = SystemTime::now(); + let mut rfds = core::mem::MaybeUninit::::uninit(); + let rfd_ptr = unsafe { core::ptr::from_mut(rfds.assume_init_mut()) }; + unsafe { libc::FD_ZERO(rfd_ptr) }; + { + let mut ex = EXECUTOR.lock().unwrap(); + ex.active_tasks.iter_mut().for_each(|task| { + if task.ready() { + task.callback.take_if(|(f, data)| { + matches!((f)(data.handle() as *mut ()), CallbackState::Ready) + }); + } else { + match &task.inner { + EventType::Triggered { + last_counter: _, + event_fd, + event: _, + } => { + unsafe { libc::FD_SET(*event_fd, rfd_ptr) }; + if *event_fd > maxfd { + maxfd = *event_fd + 1; + } + } + EventType::SystemTime(system_time) => { + if *system_time > now { + let diff = system_time.duration_since(now).unwrap_or_default(); //.as_micros(); + 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; + // timeoutindex = n; + } + tvptr = core::ptr::from_mut(&mut wait); + } else { + task.callback.take_if(|(f, data)| { + matches!( + (f)(data.handle() as *mut ()), + CallbackState::Ready + ) + }); + } + } + } + } + }); + ex.active_tasks.retain(|task| task.callback.is_some()); + if ex.active_tasks.is_empty() { + break; + } + } + // with no work left the break should have occured + assert!(!tvptr.is_null() || maxfd > 0); + 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( @@ -124,7 +242,7 @@ enum EventType { Triggered { last_counter: AtomicU32, event_fd: EventFd, - object: Arc>, + event: Arc>, }, SystemTime(SystemTime), } From c64b08e811ea7bafa542f8171c02f9e3a300d47b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Dec 2024 23:17:44 +0100 Subject: [PATCH 413/672] fix crash, still some leaks --- crates/symmetric_executor/src/lib.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 674cede38..2b4644358 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -6,7 +6,7 @@ use std::{ }; use executor::exports::symmetric::runtime::symmetric_executor::{ - self, CallbackData, CallbackState, GuestEventSubscription, + self, CallbackState, GuestEventSubscription, }; mod executor; @@ -122,8 +122,8 @@ impl symmetric_executor::Guest for Guest { let mut ex = EXECUTOR.lock().unwrap(); ex.active_tasks.iter_mut().for_each(|task| { if task.ready() { - task.callback.take_if(|(f, data)| { - matches!((f)(data.handle() as *mut ()), CallbackState::Ready) + task.callback.take_if(|CallbackEntry(f, data)| { + matches!((f)(*data), CallbackState::Ready) }); } else { match &task.inner { @@ -151,11 +151,8 @@ impl symmetric_executor::Guest for Guest { } tvptr = core::ptr::from_mut(&mut wait); } else { - task.callback.take_if(|(f, data)| { - matches!( - (f)(data.handle() as *mut ()), - CallbackState::Ready - ) + task.callback.take_if(|CallbackEntry(f, data)| { + matches!((f)(*data), CallbackState::Ready) }); } } @@ -216,7 +213,8 @@ impl symmetric_executor::Guest for Guest { &mut *(trigger.take_handle() as *mut EventSubscription) }); let cb: fn(*mut ()) -> CallbackState = unsafe { transmute(callback.take_handle()) }; - subscr.callback.replace((cb, data)); + let data = data.take_handle() as *mut (); + subscr.callback.replace(CallbackEntry(cb, data)); EXECUTOR.lock().unwrap().active_tasks.push(subscr); } } @@ -231,7 +229,9 @@ struct EventInner { struct EventGenerator(Arc>); -type CallbackEntry = (fn(*mut ()) -> CallbackState, CallbackData); +struct CallbackEntry(fn(*mut ()) -> CallbackState, *mut ()); + +unsafe impl Send for CallbackEntry {} struct EventSubscription { inner: EventType, From fdbac208cd932890e0ed6877aaa9fb87a6a6809e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Dec 2024 23:33:11 +0100 Subject: [PATCH 414/672] note problem and fix warnings --- crates/cpp/tests/symmetric_async/Cargo.lock | 622 ++++++++++++++++++ .../async_module/src/async_module.rs | 2 +- .../tests/symmetric_async/sleep/src/lib.rs | 4 +- crates/symmetric_executor/Cargo.lock | 587 +++++++++++++++++ .../symmetric_executor/rust-client/Cargo.lock | 585 ++++++++++++++++ .../rust-client/src/async_support.rs | 4 +- crates/symmetric_executor/src/lib.rs | 2 + 7 files changed, 1801 insertions(+), 5 deletions(-) create mode 100644 crates/cpp/tests/symmetric_async/Cargo.lock create mode 100644 crates/symmetric_executor/Cargo.lock create mode 100644 crates/symmetric_executor/rust-client/Cargo.lock diff --git a/crates/cpp/tests/symmetric_async/Cargo.lock b/crates/cpp/tests/symmetric_async/Cargo.lock new file mode 100644 index 000000000..d801f4b8c --- /dev/null +++ b/crates/cpp/tests/symmetric_async/Cargo.lock @@ -0,0 +1,622 @@ +# 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 = "async_module" +version = "0.1.0" +dependencies = [ + "futures", + "sleep", + "symmetric_executor", + "wit-bindgen", + "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 = "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 = "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 = "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 = "sleep" +version = "0.1.0" +dependencies = [ + "symmetric_executor", +] + +[[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 = "symmetric_executor" +version = "0.1.0" +dependencies = [ + "futures", + "libc", + "wit-bindgen", + "wit-bindgen-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 = "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/cpp/tests/symmetric_async/async_module/src/async_module.rs b/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs index 5ea6409c1..c331bab94 100644 --- a/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs +++ b/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs @@ -184,7 +184,7 @@ mod _rt { wit_bindgen::rt::run_ctors_once(); } pub use alloc_crate::string::String; - pub use alloc_crate::vec::Vec; + // pub use alloc_crate::vec::Vec; pub mod stream_and_future_support { use { futures::{ diff --git a/crates/cpp/tests/symmetric_async/sleep/src/lib.rs b/crates/cpp/tests/symmetric_async/sleep/src/lib.rs index 1c0b699d6..2d5ba58e8 100644 --- a/crates/cpp/tests/symmetric_async/sleep/src/lib.rs +++ b/crates/cpp/tests/symmetric_async/sleep/src/lib.rs @@ -1,6 +1,8 @@ #[link(name = "symmetric_executor")] extern "C" { - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(nanoseconds: u64) -> *mut (); + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout( + nanoseconds: u64, + ) -> *mut (); } #[no_mangle] diff --git a/crates/symmetric_executor/Cargo.lock b/crates/symmetric_executor/Cargo.lock new file mode 100644 index 000000000..9089207e4 --- /dev/null +++ b/crates/symmetric_executor/Cargo.lock @@ -0,0 +1,587 @@ +# 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 = "symmetric_executor" +version = "0.1.0" +dependencies = [ + "futures", + "libc", + "wit-bindgen", + "wit-bindgen-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 = "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-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.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/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index b680a2af5..618a9d718 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -1,6 +1,5 @@ use futures::{channel::oneshot, task::Waker, FutureExt}; use std::{ - alloc::Layout, any::Any, collections::hash_map, future::Future, @@ -9,9 +8,8 @@ use std::{ }; use crate::module::symmetric::runtime::{ - self, symmetric_executor::{ - self, CallbackData, CallbackFunction, CallbackState, EventGenerator, EventSubscription, + self, CallbackState, EventGenerator, EventSubscription, }, }; diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 2b4644358..fc457e5b8 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -205,11 +205,13 @@ impl symmetric_executor::Guest for Guest { data: symmetric_executor::CallbackData, ) -> () { // TODO: Tidy this mess up + // Note: Trigger is consumed, callback and data are managed elsewhere let mut subscr = EventSubscription { inner: EventType::SystemTime(std::time::UNIX_EPOCH), callback: None, }; std::mem::swap(&mut subscr, unsafe { + // TODO: This should be handle to free the resource, but it is used later? &mut *(trigger.take_handle() as *mut EventSubscription) }); let cb: fn(*mut ()) -> CallbackState = unsafe { transmute(callback.take_handle()) }; From 9924843a904e930453db546c7178045c88800226 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 9 Dec 2024 00:53:43 +0100 Subject: [PATCH 415/672] native stream test, work in progress --- crates/cpp/tests/symmetric_stream/Cargo.lock | 594 +++++++++ crates/cpp/tests/symmetric_stream/Cargo.toml | 3 + crates/cpp/tests/symmetric_stream/generate.sh | 2 + .../tests/symmetric_stream/main/Cargo.toml | 6 + .../tests/symmetric_stream/main/src/main.rs | 3 + .../tests/symmetric_stream/source/Cargo.toml | 6 + .../tests/symmetric_stream/source/src/lib.rs | 14 + .../tests/symmetric_stream/stream/Cargo.toml | 9 + .../tests/symmetric_stream/stream/src/lib.rs | 25 + .../stream/src/stream_world.rs | 1070 +++++++++++++++++ .../symmetric_stream/wit/async_stream.wit | 14 + 11 files changed, 1746 insertions(+) create mode 100644 crates/cpp/tests/symmetric_stream/Cargo.lock create mode 100644 crates/cpp/tests/symmetric_stream/Cargo.toml create mode 100755 crates/cpp/tests/symmetric_stream/generate.sh create mode 100644 crates/cpp/tests/symmetric_stream/main/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_stream/main/src/main.rs create mode 100644 crates/cpp/tests/symmetric_stream/source/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_stream/source/src/lib.rs create mode 100644 crates/cpp/tests/symmetric_stream/stream/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_stream/stream/src/lib.rs create mode 100644 crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs create mode 100644 crates/cpp/tests/symmetric_stream/wit/async_stream.wit diff --git a/crates/cpp/tests/symmetric_stream/Cargo.lock b/crates/cpp/tests/symmetric_stream/Cargo.lock new file mode 100644 index 000000000..cbfa2e997 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream/Cargo.lock @@ -0,0 +1,594 @@ +# 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 = "main" +version = "0.1.0" + +[[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 = "source" +version = "0.1.0" + +[[package]] +name = "spdx" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae30cc7bfe3656d60ee99bf6836f472b0c53dddcbf335e253329abb16e535a2" +dependencies = [ + "smallvec", +] + +[[package]] +name = "stream" +version = "0.1.0" +dependencies = [ + "futures", + "wit-bindgen", + "wit-bindgen-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 = "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-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/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/generate.sh b/crates/cpp/tests/symmetric_stream/generate.sh new file mode 100755 index 000000000..583a3096d --- /dev/null +++ b/crates/cpp/tests/symmetric_stream/generate.sh @@ -0,0 +1,2 @@ +#!/bin/sh +(cd stream/src ; ../../../../../../target/debug/wit-bindgen rust ../../wit/async_stream.wit --async all --symmetric) 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..e7eb37abc --- /dev/null +++ b/crates/cpp/tests/symmetric_stream/main/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "main" +version = "0.1.0" +edition = "2021" + +[dependencies] 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..e7a11a969 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} 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..d031419f5 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream/source/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "source" +version = "0.1.0" +edition = "2021" + +[dependencies] 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..b93cf3ffd --- /dev/null +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} 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..ef7d5ceee --- /dev/null +++ b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "stream" +version = "0.1.0" +edition = "2021" + +[dependencies] +futures = "0.3.31" +wit-bindgen = { version = "0.36.0", path = "../../../../guest-rust" } +wit-bindgen-rt = { version = "0.36.0", path = "../../../../guest-rust/rt" } 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..cb49e7833 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs @@ -0,0 +1,25 @@ +use stream_world::{stream_and_future_support, test::test::stream_source::create}; +use wit_bindgen_rt::async_support; +use futures::{StreamExt, SinkExt}; + +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 { + async fn create() -> stream_and_future_support::StreamReader { + let (mut writer, reader) = stream_and_future_support::new_stream(); + let mut input = create().await; + + async_support::spawn(async move { + while let Some(values) = input.next().await { + for value in values { + writer.feed(vec![value, value+1]).await.unwrap(); + } + } + }); + return 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..2eb45c71c --- /dev/null +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -0,0 +1,1070 @@ +// Generated by `wit-bindgen` 0.36.0. DO NOT EDIT! +// Options used: +#[allow(dead_code, clippy::all)] +pub mod test { + pub mod test { + + #[allow(dead_code, 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; + + impl _rt::stream_and_future_support::StreamPayload for u32{ + fn new() -> u32 { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + extern "C" { + #[link_name = "[stream-new-0]create"] + fn new() -> u32; + } + unsafe { new() } + } + } + + async fn write(stream: u32, values: &[Self]) -> Option { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + let address = values.as_ptr() as _; + + #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + extern "C" { + #[link_name = "[async][stream-write-0]create"] + fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; + } + + unsafe { + ::wit_bindgen_rt::async_support::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await + } + } + } + + async fn read(stream: u32, values: &mut [::core::mem::MaybeUninit::]) -> Option { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + let address = values.as_mut_ptr() as _; + #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + extern "C" { + #[link_name = "[async][stream-read-0]create"] + fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; + } + + let count = unsafe { + ::wit_bindgen_rt::async_support::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await + }; + #[allow(unused)] + if let Some(count) = count { + let value = (); + + } + count + } + } + + fn cancel_write(writer: u32) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + extern "C" { + #[link_name = "[stream-cancel-write-0]create"] + fn cancel(_: u32) -> u32; + } + unsafe { cancel(writer) }; + } + } + + fn cancel_read(reader: u32) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + extern "C" { + #[link_name = "[stream-cancel-read-0]create"] + fn cancel(_: u32) -> u32; + } + unsafe { cancel(reader) }; + } + } + + fn close_writable(writer: u32) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + extern "C" { + #[link_name = "[stream-close-writable-0]create"] + fn drop(_: u32, _: u32); + } + unsafe { drop(writer, 0) } + } + } + + fn close_readable(reader: u32) { + #[cfg(not(target_arch = "wasm32"))] + { + unreachable!(); + } + + #[cfg(target_arch = "wasm32")] + { + #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + extern "C" { + #[link_name = "[stream-close-readable-0]create"] + fn drop(_: u32); + } + unsafe { drop(reader) } + } + } + } + + #[allow(unused_unsafe, clippy::all)] + pub async fn create() -> _rt::stream_and_future_support::StreamReader{ + unsafe { + let layout0 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); + let ptr0 = _rt::alloc::alloc(layout0); + let layout1 = _rt::alloc::Layout::from_size_align_unchecked(core::mem::size_of::<*const u8>(), core::mem::size_of::<*const u8>()); + let ptr1 = _rt::alloc::alloc(layout1); + + #[link(wasm_import_module = "test:test/stream-source")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "[async]create")] + fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(_: *mut u8, _: *mut u8, ) -> i32; + } + let layout2 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); + ::wit_bindgen_rt::async_support::await_result(testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate, layout2, ptr0, ptr1).await; + let l3 = *ptr1.add(0).cast::<*mut u8>(); + let result4 = _rt::stream_and_future_support::StreamReader::from_handle(l3 as u32); + _rt::cabi_dealloc(ptr1, core::mem::size_of::<*const u8>(), core::mem::size_of::<*const u8>()); + result4 + } + } + + } + + } +} +#[allow(dead_code, clippy::all)] +pub mod exports { + pub mod test { + pub mod test { + + #[allow(dead_code, 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() -> *mut u8 {#[cfg(target_arch="wasm32")] + _rt::run_ctors_once();let result0 = T::create(); + let result = ::wit_bindgen_rt::async_support::first_poll(result0, |result1| { + + + #[link(wasm_import_module = "[export]test:test/stream-test")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "[task-return]create")] + fn X5BexportX5DtestX3AtestX2Fstream_testX00X5Btask_returnX5Dcreate(_: i32, ); + } + X5BexportX5DtestX3AtestX2Fstream_testX00X5Btask_returnX5Dcreate((result1).into_handle() as i32); + }); + + result + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __callback_create(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + ::wit_bindgen_rt::async_support::callback(ctx, event0, event1, event2) + } + pub trait Guest { + fn create() -> impl ::core::future::Future> + 'static; + } + #[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>() + } + #[export_name = "[callback]create"] + unsafe extern "C" fn _callback_create(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + $($path_to_types)*::__callback_create(ctx, event0, event1, event2) + } + };); + } + #[doc(hidden)] + pub(crate) use __export_test_test_stream_test_cabi; + + } + + } +} +} +mod _rt { + #![allow(dead_code, clippy::all)] + 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; + 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 { + handle: u32, + _phantom: PhantomData, + } + + impl fmt::Debug for FutureWriter { + 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 { + writer: Option>, + future: Pin>>, + } + + impl Future for CancelableWrite { + 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 CancelableWrite { + /// 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 { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureWriter { + 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 Drop for CancelableWrite { + fn drop(&mut self) { + if self.writer.is_some() { + self.cancel_mut(); + } + } + } + + impl FutureWriter { + /// Write the specified value to this `future`. + pub fn write(self, v: T) -> CancelableWrite { + 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>> + } + 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 Drop for FutureWriter { + 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 { + reader: Option>, + future: Pin>>>, + } + + impl Future for CancelableRead { + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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 CancelableRead { + /// 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 { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureReader { + 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 Drop for CancelableRead { + fn drop(&mut self) { + if self.reader.is_some() { + self.cancel_mut(); + } + } + } + + /// Represents the readable end of a Component Model `future`. + pub struct FutureReader { + handle: u32, + _phantom: PhantomData, + } + + impl fmt::Debug for FutureReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureReader") + .field("handle", &self.handle) + .finish() + } + } + + impl FutureReader { + #[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 IntoFuture for FutureReader { + type Output = Option; + type IntoFuture = CancelableRead; + + /// 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>>, + 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 Drop for FutureReader { + 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; + async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; + fn cancel_write(stream: u32); + fn cancel_read(stream: u32); + fn close_writable(stream: u32); + fn close_readable(stream: u32); + } + + struct CancelWriteOnDrop { + handle: Option, + _phantom: PhantomData, + } + + impl Drop for CancelWriteOnDrop { + 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 { + handle: u32, + future: Option + 'static>>>, + _phantom: PhantomData, + } + + impl StreamWriter { + /// 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 fmt::Debug for StreamWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamWriter") + .field("handle", &self.handle) + .finish() + } + } + + impl Sink> for StreamWriter { + type Error = Infallible; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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) -> 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:: { + 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:: { + 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> { + self.poll_ready(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } + } + + impl Drop for StreamWriter { + 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 { + handle: Option, + _phantom: PhantomData, + } + + impl Drop for CancelReadOnDrop { + 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 { + handle: u32, + future: Option>> + 'static>>>, + _phantom: PhantomData, + } + + impl StreamReader { + /// 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 fmt::Debug for StreamReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamReader") + .field("handle", &self.handle) + .finish() + } + } + + impl StreamReader { + #[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 Stream for StreamReader { + type Item = Vec; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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:: { + handle: Some(handle), + _phantom: PhantomData, + }; + Box::pin(async move { + let mut buffer = iter::repeat_with(MaybeUninit::uninit) + .take(ceiling(64 * 1024, mem::size_of::())) + .collect::>(); + + let result = if let Some(count) = T::read(handle, &mut buffer).await { + buffer.truncate(count); + Some(unsafe { + mem::transmute::>, Vec>(buffer) + }) + } else { + None + }; + cancel_on_drop.handle = None; + drop(cancel_on_drop); + result + }) as Pin>> + } + Handle::LocalOpen => { + let (tx, rx) = oneshot::channel(); + entry.insert(Handle::LocalWaiting(tx)); + let mut cancel_on_drop = CancelReadOnDrop:: { + 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 Drop for StreamReader { + 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() -> (FutureWriter, FutureReader) { + 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() -> (StreamWriter, StreamReader) { + 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 } + } + }pub use alloc_crate::alloc; + 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); + } + + #[cfg(target_arch = "wasm32")] + pub fn run_ctors_once() { + wit_bindgen::rt::run_ctors_once(); + } + 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_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")] +#[link_section = "component-type:wit-bindgen:0.36.0:test:test:stream-world:encoded world"] +#[doc(hidden)] +#[allow(clippy::octal_escapes)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 262] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x83\x01\x01A\x02\x01\ +A\x04\x01B\x03\x01fy\x01@\0\0\0\x04\0\x06create\x01\x01\x03\0\x17test:test/strea\ +m-source\x05\0\x01B\x03\x01fy\x01@\0\0\0\x04\0\x06create\x01\x01\x04\0\x15test:t\ +est/stream-test\x05\x01\x04\0\x16test:test/stream-world\x04\0\x0b\x12\x01\0\x0cs\ +tream-world\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x07\ +0.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/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; +} + +interface stream-test { + create: func() -> stream; +} + +world stream-world { + import stream-source; + export stream-test; +} From 289d2f84e3f627f3669a5587ffbaac0aefb1db11 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 9 Dec 2024 15:57:58 +0100 Subject: [PATCH 416/672] more stream prototype implementation --- crates/cpp/tests/symmetric_stream/Cargo.lock | 4 ++++ .../cpp/tests/symmetric_stream/main/Cargo.toml | 1 + crates/cpp/tests/symmetric_stream/main/build.rs | 9 +++++++++ .../cpp/tests/symmetric_stream/main/src/main.rs | 14 +++++++++++++- .../cpp/tests/symmetric_stream/source/Cargo.toml | 3 +++ .../cpp/tests/symmetric_stream/source/src/lib.rs | 16 ++++------------ .../cpp/tests/symmetric_stream/stream/Cargo.toml | 4 ++++ .../cpp/tests/symmetric_stream/stream/build.rs | 9 +++++++++ .../symmetric_stream/stream/src/stream_world.rs | 5 +++-- 9 files changed, 50 insertions(+), 15 deletions(-) create mode 100644 crates/cpp/tests/symmetric_stream/main/build.rs create mode 100644 crates/cpp/tests/symmetric_stream/stream/build.rs diff --git a/crates/cpp/tests/symmetric_stream/Cargo.lock b/crates/cpp/tests/symmetric_stream/Cargo.lock index cbfa2e997..989d700e1 100644 --- a/crates/cpp/tests/symmetric_stream/Cargo.lock +++ b/crates/cpp/tests/symmetric_stream/Cargo.lock @@ -225,6 +225,9 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "main" version = "0.1.0" +dependencies = [ + "stream", +] [[package]] name = "memchr" @@ -379,6 +382,7 @@ name = "stream" version = "0.1.0" dependencies = [ "futures", + "source", "wit-bindgen", "wit-bindgen-rt", ] diff --git a/crates/cpp/tests/symmetric_stream/main/Cargo.toml b/crates/cpp/tests/symmetric_stream/main/Cargo.toml index e7eb37abc..b860a81d4 100644 --- a/crates/cpp/tests/symmetric_stream/main/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/main/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] +stream = { version = "0.1.0", path = "../stream" } 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 index e7a11a969..0b1d1b166 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -1,3 +1,15 @@ +// use wit_bindgen_symmetric_rt::{CallbackState, EventSubscription}; + +#[link(name = "stream")] +extern "C" { + pub fn testX3AtestX2Fstream_testX00X5BasyncX5Dcreate( + args: *const (), + results: *mut (), + ) -> *mut (); +} + fn main() { - println!("Hello, world!"); + let mut result_stream: *mut () = core::ptr::null_mut(); + let handle = unsafe { testX3AtestX2Fstream_testX00X5BasyncX5Dcreate(core::ptr::null_mut(), (&mut result_stream as *mut *mut ()).cast()) }; + assert!(handle.is_null()); } diff --git a/crates/cpp/tests/symmetric_stream/source/Cargo.toml b/crates/cpp/tests/symmetric_stream/source/Cargo.toml index d031419f5..a53b889b2 100644 --- a/crates/cpp/tests/symmetric_stream/source/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/source/Cargo.toml @@ -4,3 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] + +[lib] +crate-type = ["cdylib"] diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index b93cf3ffd..063dc70f4 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -1,14 +1,6 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; +#[allow(non_snake_case)] - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } +#[no_mangle] +pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(_args:*mut u8, _results:*mut u8) -> *mut u8 { + todo!() } diff --git a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml index ef7d5ceee..a54e7c8c9 100644 --- a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml @@ -5,5 +5,9 @@ edition = "2021" [dependencies] futures = "0.3.31" +source = { version = "0.1.0", path = "../source" } wit-bindgen = { version = "0.36.0", path = "../../../../guest-rust" } wit-bindgen-rt = { version = "0.36.0", path = "../../../../guest-rust/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/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 2eb45c71c..50b5b432f 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -158,6 +158,7 @@ pub mod test { let ptr1 = _rt::alloc::alloc(layout1); #[link(wasm_import_module = "test:test/stream-source")] + #[link(name="source")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "[async]create")] fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(_: *mut u8, _: *mut u8, ) -> i32; @@ -200,7 +201,7 @@ pub mod exports { #[cfg_attr(target_arch = "wasm32", link_name = "[task-return]create")] fn X5BexportX5DtestX3AtestX2Fstream_testX00X5Btask_returnX5Dcreate(_: i32, ); } - X5BexportX5DtestX3AtestX2Fstream_testX00X5Btask_returnX5Dcreate((result1).into_handle() as i32); + // X5BexportX5DtestX3AtestX2Fstream_testX00X5Btask_returnX5Dcreate((result1).into_handle() as i32); }); result @@ -220,7 +221,7 @@ pub mod exports { #[cfg_attr(target_arch = "wasm32", export_name = "create")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn testX3AtestX2Fstream_testX00create() -> *mut u8 { + unsafe extern "C" fn testX3AtestX2Fstream_testX00X5BasyncX5Dcreate(args: *mut u8, results: *mut u8) -> *mut u8 { $($path_to_types)*::_export_create_cabi::<$ty>() } #[export_name = "[callback]create"] From 685cdd94c7cc898e5d8f79cca033a84daa8b30c6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 9 Dec 2024 16:12:05 +0100 Subject: [PATCH 417/672] ongoing transition to symmetric ABI --- crates/cpp/tests/symmetric_stream/Cargo.lock | 9 ++ .../tests/symmetric_stream/stream/Cargo.toml | 1 + .../stream/src/stream_world.rs | 86 +++++-------------- .../rust-client/src/async_support.rs | 23 +++++ 4 files changed, 56 insertions(+), 63 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/Cargo.lock b/crates/cpp/tests/symmetric_stream/Cargo.lock index 989d700e1..2d07da533 100644 --- a/crates/cpp/tests/symmetric_stream/Cargo.lock +++ b/crates/cpp/tests/symmetric_stream/Cargo.lock @@ -385,6 +385,7 @@ dependencies = [ "source", "wit-bindgen", "wit-bindgen-rt", + "wit-bindgen-symmetric-rt", ] [[package]] @@ -562,6 +563,14 @@ dependencies = [ "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" diff --git a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml index a54e7c8c9..d6bb64454 100644 --- a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml @@ -8,6 +8,7 @@ futures = "0.3.31" source = { version = "0.1.0", path = "../source" } wit-bindgen = { version = "0.36.0", path = "../../../../guest-rust" } wit-bindgen-rt = { version = "0.36.0", path = "../../../../guest-rust/rt" } +wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../../../../symmetric_executor/rust-client" } [lib] crate-type = ["cdylib"] diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 50b5b432f..4111ca7d7 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -12,15 +12,10 @@ pub mod test { super::super::super::__link_custom_section_describing_imports; use super::super::super::_rt; + use _rt::stream_and_future_support::WitStream; impl _rt::stream_and_future_support::StreamPayload for u32{ - fn new() -> u32 { - #[cfg(not(target_arch = "wasm32"))] - { - unreachable!(); - } - - #[cfg(target_arch = "wasm32")] + fn new() -> *mut WitStream { { #[link(wasm_import_module = "[import-payload]test:test/stream-source")] extern "C" { @@ -31,13 +26,7 @@ pub mod test { } } - async fn write(stream: u32, values: &[Self]) -> Option { - #[cfg(not(target_arch = "wasm32"))] - { - unreachable!(); - } - - #[cfg(target_arch = "wasm32")] + async fn write(stream: *mut WitStream, values: &[Self]) -> Option { { let address = values.as_ptr() as _; @@ -53,13 +42,7 @@ pub mod test { } } - async fn read(stream: u32, values: &mut [::core::mem::MaybeUninit::]) -> Option { - #[cfg(not(target_arch = "wasm32"))] - { - unreachable!(); - } - - #[cfg(target_arch = "wasm32")] + async fn read(stream: *mut WitStream, values: &mut [::core::mem::MaybeUninit::]) -> Option { { let address = values.as_mut_ptr() as _; #[link(wasm_import_module = "[import-payload]test:test/stream-source")] @@ -80,13 +63,7 @@ pub mod test { } } - fn cancel_write(writer: u32) { - #[cfg(not(target_arch = "wasm32"))] - { - unreachable!(); - } - - #[cfg(target_arch = "wasm32")] + fn cancel_write(writer: *mut WitStream) { { #[link(wasm_import_module = "[import-payload]test:test/stream-source")] extern "C" { @@ -97,13 +74,7 @@ pub mod test { } } - fn cancel_read(reader: u32) { - #[cfg(not(target_arch = "wasm32"))] - { - unreachable!(); - } - - #[cfg(target_arch = "wasm32")] + fn cancel_read(reader: *mut WitStream) { { #[link(wasm_import_module = "[import-payload]test:test/stream-source")] extern "C" { @@ -114,13 +85,7 @@ pub mod test { } } - fn close_writable(writer: u32) { - #[cfg(not(target_arch = "wasm32"))] - { - unreachable!(); - } - - #[cfg(target_arch = "wasm32")] + fn close_writable(writer: *mut WitStream) { { #[link(wasm_import_module = "[import-payload]test:test/stream-source")] extern "C" { @@ -131,13 +96,7 @@ pub mod test { } } - fn close_readable(reader: u32) { - #[cfg(not(target_arch = "wasm32"))] - { - unreachable!(); - } - - #[cfg(target_arch = "wasm32")] + fn close_readable(reader: *mut WitStream) { { #[link(wasm_import_module = "[import-payload]test:test/stream-source")] extern "C" { @@ -258,8 +217,9 @@ mod _rt { pin::Pin, task::{Context, Poll}, }, - wit_bindgen_rt::async_support::{self, Handle}, + wit_bindgen_symmetric_rt::async_support::{self, Handle}, }; + pub use wit_bindgen_symmetric_rt::async_support::Stream as WitStream; #[doc(hidden)] pub trait FuturePayload: Unpin + Sized + 'static { @@ -585,17 +545,17 @@ mod _rt { #[doc(hidden)] pub trait StreamPayload: Unpin + Sized + 'static { - fn new() -> u32; - async fn write(stream: u32, values: &[Self]) -> Option; - async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; - fn cancel_write(stream: u32); - fn cancel_read(stream: u32); - fn close_writable(stream: u32); - fn close_readable(stream: u32); + fn new() -> *mut WitStream; + async fn write(stream: *mut WitStream, values: &[Self]) -> Option; + async fn read(stream: *mut WitStream, values: &mut [MaybeUninit]) -> Option; + fn cancel_write(stream: *mut WitStream); + fn cancel_read(stream: *mut WitStream); + fn close_writable(stream: *mut WitStream); + fn close_readable(stream: *mut WitStream); } struct CancelWriteOnDrop { - handle: Option, + handle: Option<*mut WitStream>, _phantom: PhantomData, } @@ -621,7 +581,7 @@ mod _rt { /// Represents the writable end of a Component Model `stream`. pub struct StreamWriter { - handle: u32, + handle: *mut WitStream, future: Option + 'static>>>, _phantom: PhantomData, } @@ -765,7 +725,7 @@ mod _rt { } struct CancelReadOnDrop { - handle: Option, + handle: Option<*mut WitStream>, _phantom: PhantomData, } @@ -791,7 +751,7 @@ mod _rt { /// Represents the readable end of a Component Model `stream`. pub struct StreamReader { - handle: u32, + handle: *mut WitStream, future: Option>> + 'static>>>, _phantom: PhantomData, } @@ -816,7 +776,7 @@ mod _rt { impl StreamReader { #[doc(hidden)] - pub fn from_handle(handle: u32) -> Self { + pub fn from_handle(handle: *mut WitStream) -> Self { async_support::with_entry(handle, |entry| match entry { Entry::Vacant(entry) => { entry.insert(Handle::Read); @@ -843,7 +803,7 @@ mod _rt { } #[doc(hidden)] - pub fn into_handle(self) -> u32 { + pub fn into_handle(self) -> *mut WitStream { async_support::with_entry(self.handle, |entry| match entry { Entry::Vacant(_) => unreachable!(), Entry::Occupied(mut entry) => match entry.get() { diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 618a9d718..b28753acd 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -33,6 +33,29 @@ pub enum Handle { Write, } +#[repr(C)] +pub struct StreamVtable { + // magic value for EOF(-1) and block(-MAX) + // asynchronous function, if this blocks wait for read ready event + pub read: fn(stream: *mut (), buf: *mut (), size: usize) -> isize, + pub close_read: fn(stream: *mut ()), + + pub write: fn(stream: *mut (), buf: *mut (), size: usize) -> isize, + pub close_write: fn(stream: *mut ()), + // post WASI 0.3, CPB + // pub allocate: fn(stream: *mut ()) -> (*mut (), isize), + // pub publish: fn(stream: *mut (), size: usize), +} + +#[repr(C)] +pub struct Stream { + vtable: *const StreamVtable, + read_ready_event_send: *mut (), + write_ready_event_send: *mut (), + read_addr: *mut (), + read_size: usize, +} + #[doc(hidden)] pub fn with_entry(_h: u32, _f: impl FnOnce(hash_map::Entry<'_, u32, Handle>) -> T) -> T { todo!() From 1d8d440d0533ae953ba83b14cd5163735ba64352 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 11 Dec 2024 15:51:18 +0100 Subject: [PATCH 418/672] compiling with functionality added, todo in 987 hit --- crates/cpp/tests/symmetric_stream/Cargo.lock | 12 +- .../tests/symmetric_stream/main/Cargo.toml | 1 + .../tests/symmetric_stream/stream/Cargo.toml | 2 +- .../tests/symmetric_stream/stream/src/lib.rs | 2 +- .../stream/src/stream_world.rs | 1035 +++++++------- .../rust-client/src/async_support.rs | 20 +- .../rust-client/src/module.rs | 1232 +++++++++-------- 7 files changed, 1180 insertions(+), 1124 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/Cargo.lock b/crates/cpp/tests/symmetric_stream/Cargo.lock index 2d07da533..5ad8d108b 100644 --- a/crates/cpp/tests/symmetric_stream/Cargo.lock +++ b/crates/cpp/tests/symmetric_stream/Cargo.lock @@ -227,6 +227,7 @@ name = "main" version = "0.1.0" dependencies = [ "stream", + "symmetric_executor", ] [[package]] @@ -384,10 +385,19 @@ dependencies = [ "futures", "source", "wit-bindgen", - "wit-bindgen-rt", "wit-bindgen-symmetric-rt", ] +[[package]] +name = "symmetric_executor" +version = "0.1.0" +dependencies = [ + "futures", + "libc", + "wit-bindgen", + "wit-bindgen-rt", +] + [[package]] name = "syn" version = "2.0.90" diff --git a/crates/cpp/tests/symmetric_stream/main/Cargo.toml b/crates/cpp/tests/symmetric_stream/main/Cargo.toml index b860a81d4..030412058 100644 --- a/crates/cpp/tests/symmetric_stream/main/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/main/Cargo.toml @@ -5,3 +5,4 @@ edition = "2021" [dependencies] stream = { version = "0.1.0", path = "../stream" } +symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor" } diff --git a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml index d6bb64454..e6c7045a0 100644 --- a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" futures = "0.3.31" source = { version = "0.1.0", path = "../source" } wit-bindgen = { version = "0.36.0", path = "../../../../guest-rust" } -wit-bindgen-rt = { version = "0.36.0", path = "../../../../guest-rust/rt" } +#wit-bindgen-rt = { version = "0.36.0", path = "../../../../guest-rust/rt" } wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../../../../symmetric_executor/rust-client" } [lib] diff --git a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs index cb49e7833..2be1ed261 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs @@ -1,5 +1,5 @@ use stream_world::{stream_and_future_support, test::test::stream_source::create}; -use wit_bindgen_rt::async_support; +use wit_bindgen_symmetric_rt::async_support; use futures::{StreamExt, SinkExt}; mod stream_world; diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 4111ca7d7..990645fb2 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -17,93 +17,100 @@ pub mod test { impl _rt::stream_and_future_support::StreamPayload for u32{ fn new() -> *mut WitStream { { - #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - extern "C" { - #[link_name = "[stream-new-0]create"] - fn new() -> u32; - } - unsafe { new() } + // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + // extern "C" { + // #[link_name = "[stream-new-0]create"] + // fn new() -> u32; + // } + // unsafe { new() } + todo!() } } async fn write(stream: *mut WitStream, values: &[Self]) -> Option { { - let address = values.as_ptr() as _; + // let address = values.as_ptr() as _; - #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - extern "C" { - #[link_name = "[async][stream-write-0]create"] - fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; - } - - unsafe { - ::wit_bindgen_rt::async_support::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await - } + // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + // extern "C" { + // #[link_name = "[async][stream-write-0]create"] + // fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; + // } + + // unsafe { + // ::wit_bindgen_symmetric_rt::async_support::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await + // } + todo!() } } async fn read(stream: *mut WitStream, values: &mut [::core::mem::MaybeUninit::]) -> Option { { - let address = values.as_mut_ptr() as _; - #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - extern "C" { - #[link_name = "[async][stream-read-0]create"] - fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; - } - - let count = unsafe { - ::wit_bindgen_rt::async_support::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await - }; - #[allow(unused)] - if let Some(count) = count { - let value = (); - - } - count + // let address = values.as_mut_ptr() as _; + // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + // extern "C" { + // #[link_name = "[async][stream-read-0]create"] + // fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; + // } + + // let count = unsafe { + // ::wit_bindgen_symmetric_rt::async_support::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await + // }; + // #[allow(unused)] + // if let Some(count) = count { + // let value = (); + + // } + // count + todo!() } } fn cancel_write(writer: *mut WitStream) { { - #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - extern "C" { - #[link_name = "[stream-cancel-write-0]create"] - fn cancel(_: u32) -> u32; - } - unsafe { cancel(writer) }; + // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + // extern "C" { + // #[link_name = "[stream-cancel-write-0]create"] + // fn cancel(_: u32) -> u32; + // } + // unsafe { cancel(writer) }; + todo!() } } fn cancel_read(reader: *mut WitStream) { { - #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - extern "C" { - #[link_name = "[stream-cancel-read-0]create"] - fn cancel(_: u32) -> u32; - } - unsafe { cancel(reader) }; + // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + // extern "C" { + // #[link_name = "[stream-cancel-read-0]create"] + // fn cancel(_: u32) -> u32; + // } + // unsafe { cancel(reader) }; + todo!() } } fn close_writable(writer: *mut WitStream) { { - #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - extern "C" { - #[link_name = "[stream-close-writable-0]create"] - fn drop(_: u32, _: u32); - } - unsafe { drop(writer, 0) } + // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + // extern "C" { + // #[link_name = "[stream-close-writable-0]create"] + // fn drop(_: u32, _: u32); + // } + // unsafe { drop(writer, 0) } + todo!() } } fn close_readable(reader: *mut WitStream) { { - #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - extern "C" { - #[link_name = "[stream-close-readable-0]create"] - fn drop(_: u32); - } - unsafe { drop(reader) } + // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + // extern "C" { + // #[link_name = "[stream-close-readable-0]create"] + // fn drop(_: u32); + // } + // unsafe { drop(reader) } + todo!() } } } @@ -120,12 +127,12 @@ pub mod test { #[link(name="source")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "[async]create")] - fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(_: *mut u8, _: *mut u8, ) -> i32; + fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(_: *mut u8, _: *mut u8, ) -> *mut u8; } - let layout2 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); - ::wit_bindgen_rt::async_support::await_result(testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate, layout2, ptr0, ptr1).await; + // let layout2 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); + ::wit_bindgen_symmetric_rt::async_support::await_result(testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate, ptr0, ptr1).await; let l3 = *ptr1.add(0).cast::<*mut u8>(); - let result4 = _rt::stream_and_future_support::StreamReader::from_handle(l3 as u32); + let result4 = _rt::stream_and_future_support::StreamReader::from_handle(l3.cast()); _rt::cabi_dealloc(ptr1, core::mem::size_of::<*const u8>(), core::mem::size_of::<*const u8>()); result4 } @@ -150,9 +157,9 @@ pub mod exports { use super::super::super::super::_rt; #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_create_cabi() -> *mut u8 {#[cfg(target_arch="wasm32")] + pub unsafe fn _export_create_cabi(_args: *mut u8, _results: *mut u8) -> *mut u8 {#[cfg(target_arch="wasm32")] _rt::run_ctors_once();let result0 = T::create(); - let result = ::wit_bindgen_rt::async_support::first_poll(result0, |result1| { + let result = ::wit_bindgen_symmetric_rt::async_support::first_poll(result0, |result1| { #[link(wasm_import_module = "[export]test:test/stream-test")] @@ -163,12 +170,12 @@ pub mod exports { // X5BexportX5DtestX3AtestX2Fstream_testX00X5Btask_returnX5Dcreate((result1).into_handle() as i32); }); - result + result.cast() } #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn __callback_create(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { - ::wit_bindgen_rt::async_support::callback(ctx, event0, event1, event2) + ::wit_bindgen_symmetric_rt::async_support::callback(ctx, event0, event1, event2) } pub trait Guest { fn create() -> impl ::core::future::Future> + 'static; @@ -181,12 +188,12 @@ pub mod exports { #[cfg_attr(target_arch = "wasm32", export_name = "create")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] unsafe extern "C" fn testX3AtestX2Fstream_testX00X5BasyncX5Dcreate(args: *mut u8, results: *mut u8) -> *mut u8 { - $($path_to_types)*::_export_create_cabi::<$ty>() - } - #[export_name = "[callback]create"] - unsafe extern "C" fn _callback_create(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { - $($path_to_types)*::__callback_create(ctx, event0, event1, event2) + $($path_to_types)*::_export_create_cabi::<$ty>(args,results) } + // #[export_name = "[callback]create"] + // unsafe extern "C" fn _callback_create(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + // $($path_to_types)*::__callback_create(ctx, event0, event1, event2) + // } };); } #[doc(hidden)] @@ -276,21 +283,22 @@ mod _rt { } fn cancel_mut(&mut self) -> FutureWriter { - 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 + // 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 + todo!() } } @@ -305,65 +313,67 @@ mod _rt { impl FutureWriter { /// Write the specified value to this `future`. pub fn write(self, v: T) -> CancelableWrite { - 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>> - } - 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)), - }, - }), - } + // 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>> + // } + // 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)), + // }, + // }), + // } + todo!() } } impl Drop for FutureWriter { 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); - } - }, - }); + // 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); + // } + // }, + // }); + todo!() } } @@ -397,21 +407,22 @@ mod _rt { } fn cancel_mut(&mut self) -> FutureReader { - 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 + // 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 + todo!() } } @@ -440,46 +451,48 @@ mod _rt { impl FutureReader { #[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, - } + // 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, + // } + todo!() } #[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 + // 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 + todo!() } } @@ -491,55 +504,57 @@ mod _rt { /// 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>>, - 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()))) - } - }, - }), - } + // 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>>, + // 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()))) + // } + // }, + // }), + // } + todo!() } } impl Drop for FutureReader { 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!(), - }, - }); + // 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!(), + // }, + // }); + todo!() } } @@ -561,21 +576,22 @@ mod _rt { impl Drop for CancelWriteOnDrop { 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), - }, - }); - } + // 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), + // }, + // }); + // } + todo!() } } @@ -624,75 +640,76 @@ mod _rt { } fn start_send(self: Pin<&mut Self>, item: Vec) -> 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:: { - 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:: { - 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(()) + // 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:: { + // 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:: { + // 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(()) + todo!() } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { @@ -706,21 +723,22 @@ mod _rt { impl Drop for StreamWriter { 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); - } - }, - }); + // 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); + // } + // }, + // }); + todo!() } } @@ -731,21 +749,22 @@ mod _rt { impl Drop for CancelReadOnDrop { 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), - }, - }); - } + // 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), + // }, + // }); + // } + todo!() } } @@ -777,47 +796,49 @@ mod _rt { impl StreamReader { #[doc(hidden)] pub fn from_handle(handle: *mut WitStream) -> 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, - } + // 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, + // } + todo!() } #[doc(hidden)] pub fn into_handle(self) -> *mut WitStream { - 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 + // 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 + todo!() } } @@ -825,141 +846,145 @@ mod _rt { type Item = Vec; fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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:: { - handle: Some(handle), - _phantom: PhantomData, - }; - Box::pin(async move { - let mut buffer = iter::repeat_with(MaybeUninit::uninit) - .take(ceiling(64 * 1024, mem::size_of::())) - .collect::>(); - - let result = if let Some(count) = T::read(handle, &mut buffer).await { - buffer.truncate(count); - Some(unsafe { - mem::transmute::>, Vec>(buffer) - }) - } else { - None - }; - cancel_on_drop.handle = None; - drop(cancel_on_drop); - result - }) as Pin>> - } - Handle::LocalOpen => { - let (tx, rx) = oneshot::channel(); - entry.insert(Handle::LocalWaiting(tx)); - let mut cancel_on_drop = CancelReadOnDrop:: { - 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, - } + // 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:: { + // handle: Some(handle), + // _phantom: PhantomData, + // }; + // Box::pin(async move { + // let mut buffer = iter::repeat_with(MaybeUninit::uninit) + // .take(ceiling(64 * 1024, mem::size_of::())) + // .collect::>(); + + // let result = if let Some(count) = T::read(handle, &mut buffer).await { + // buffer.truncate(count); + // Some(unsafe { + // mem::transmute::>, Vec>(buffer) + // }) + // } else { + // None + // }; + // cancel_on_drop.handle = None; + // drop(cancel_on_drop); + // result + // }) as Pin>> + // } + // Handle::LocalOpen => { + // let (tx, rx) = oneshot::channel(); + // entry.insert(Handle::LocalWaiting(tx)); + // let mut cancel_on_drop = CancelReadOnDrop:: { + // 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, + // } + todo!() } } impl Drop for StreamReader { 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!(), - }, - }); + // 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!(), + // }, + // }); + todo!() } } /// Creates a new Component Model `future` with the specified payload type. pub fn new_future() -> (FutureWriter, FutureReader) { - 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, - }, - ) + // 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, + // }, + // ) + todo!() } /// Creates a new Component Model `stream` with the specified payload type. pub fn new_stream() -> (StreamWriter, StreamReader) { - 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, - }, - ) + // 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, + // }, + // ) + todo!() } fn ceiling(x: usize, y: usize) -> usize { diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index b28753acd..e4bc5de61 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -1,7 +1,7 @@ use futures::{channel::oneshot, task::Waker, FutureExt}; use std::{ any::Any, - collections::hash_map, + collections::hash_map::{self, OccupiedEntry}, future::Future, pin::Pin, task::{Context, Poll, RawWaker, RawWakerVTable}, @@ -56,10 +56,16 @@ pub struct Stream { read_size: usize, } -#[doc(hidden)] -pub fn with_entry(_h: u32, _f: impl FnOnce(hash_map::Entry<'_, u32, Handle>) -> T) -> T { - todo!() -} +// pub enum Entry<'a, K, V> { +// Vacant(), +// Occupied(&'a mut Stream), +// } + +// #[doc(hidden)] +// pub fn with_entry(h: *mut (), f: impl FnOnce(Entry<'_, u32, Handle>) -> T) -> T { +// let entry = unsafe { &mut *(h.cast::()) }; +// f(hash_map::Entry::Occupied(entry)) +// } static VTABLE: RawWakerVTable = RawWakerVTable::new( |_| RawWaker::new(core::ptr::null(), &VTABLE), @@ -173,3 +179,7 @@ pub async unsafe fn await_result( pub unsafe fn callback(_ctx: *mut u8, _event0: i32, _event1: i32, _event2: i32) -> i32 { todo!() } + +pub fn spawn(_future: impl Future + 'static) { +todo!() +} \ No newline at end of file diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index c748976b2..f51256d37 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -572,338 +572,338 @@ mod _rt { wit_bindgen_symmetric_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; - 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 { - handle: u32, - _phantom: PhantomData, - } - - impl fmt::Debug for FutureWriter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FutureWriter") - .field("handle", &self.handle) - .finish() - } - } + // #[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; + // 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 { + // handle: u32, + // _phantom: PhantomData, + // } + + // impl fmt::Debug for FutureWriter { + // 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 { - writer: Option>, - future: Pin>>, - } - - impl Future for CancelableWrite { - 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 CancelableWrite { - /// 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 { - self.cancel_mut() - } - - fn cancel_mut(&mut self) -> FutureWriter { - 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 Drop for CancelableWrite { - fn drop(&mut self) { - if self.writer.is_some() { - self.cancel_mut(); - } - } - } - - impl FutureWriter { - /// Write the specified value to this `future`. - pub fn write(self, v: T) -> CancelableWrite { - 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>> - } - 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 Drop for FutureWriter { - 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 { - reader: Option>, - future: Pin>>>, - } - - impl Future for CancelableRead { - type Output = Option; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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 CancelableRead { - /// 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 { - self.cancel_mut() - } - - fn cancel_mut(&mut self) -> FutureReader { - 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 Drop for CancelableRead { - fn drop(&mut self) { - if self.reader.is_some() { - self.cancel_mut(); - } - } - } - - /// Represents the readable end of a Component Model `future`. - pub struct FutureReader { - handle: u32, - _phantom: PhantomData, - } - - impl fmt::Debug for FutureReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FutureReader") - .field("handle", &self.handle) - .finish() - } - } - - impl FutureReader { - #[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 IntoFuture for FutureReader { - type Output = Option; - type IntoFuture = CancelableRead; - - /// 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>>, - 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 Drop for FutureReader { - 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!(), - }, - }); - } - } + // pub struct CancelableWrite { + // writer: Option>, + // future: Pin>>, + // } + + // impl Future for CancelableWrite { + // 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 CancelableWrite { + // /// 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 { + // self.cancel_mut() + // } + + // fn cancel_mut(&mut self) -> FutureWriter { + // 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 Drop for CancelableWrite { + // fn drop(&mut self) { + // if self.writer.is_some() { + // self.cancel_mut(); + // } + // } + // } + + // impl FutureWriter { + // /// Write the specified value to this `future`. + // pub fn write(self, v: T) -> CancelableWrite { + // 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>> + // } + // 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 Drop for FutureWriter { + // 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 { + // reader: Option>, + // future: Pin>>>, + // } + + // impl Future for CancelableRead { + // type Output = Option; + + // fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // 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 CancelableRead { + // /// 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 { + // self.cancel_mut() + // } + + // fn cancel_mut(&mut self) -> FutureReader { + // 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 Drop for CancelableRead { + // fn drop(&mut self) { + // if self.reader.is_some() { + // self.cancel_mut(); + // } + // } + // } + + // /// Represents the readable end of a Component Model `future`. + // pub struct FutureReader { + // handle: u32, + // _phantom: PhantomData, + // } + + // impl fmt::Debug for FutureReader { + // fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // f.debug_struct("FutureReader") + // .field("handle", &self.handle) + // .finish() + // } + // } + + // impl FutureReader { + // #[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 IntoFuture for FutureReader { + // type Output = Option; + // type IntoFuture = CancelableRead; + + // /// 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>>, + // 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 Drop for FutureReader { + // 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 { @@ -923,21 +923,22 @@ mod _rt { impl Drop for CancelWriteOnDrop { 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), - }, - }); - } + todo!() + // 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), + // }, + // }); + // } } } @@ -986,75 +987,76 @@ mod _rt { } fn start_send(self: Pin<&mut Self>, item: Vec) -> 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:: { - 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:: { - 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(()) + // 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:: { + // 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:: { + // 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(()) + todo!() } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { @@ -1069,20 +1071,21 @@ mod _rt { impl Drop for StreamWriter { 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); - } - }, - }); + todo!() + + // 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); + // } + // }, + // }); } } @@ -1093,21 +1096,23 @@ mod _rt { impl Drop for CancelReadOnDrop { 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), - }, - }); - } + todo!() + + // 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), + // }, + // }); + // } } } @@ -1139,49 +1144,51 @@ mod _rt { impl StreamReader { #[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, - } + todo!() + // 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 + // 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 + todo!() } } @@ -1189,147 +1196,150 @@ mod _rt { type Item = Vec; fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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:: { - handle: Some(handle), - _phantom: PhantomData, - }; - Box::pin(async move { - let mut buffer = iter::repeat_with(MaybeUninit::uninit) - .take(ceiling(64 * 1024, mem::size_of::())) - .collect::>(); - - let result = if let Some(count) = - T::read(handle, &mut buffer).await - { - buffer.truncate(count); - Some(unsafe { - mem::transmute::>, Vec>(buffer) - }) - } else { - None - }; - cancel_on_drop.handle = None; - drop(cancel_on_drop); - result - }) - as Pin>> - } - Handle::LocalOpen => { - let (tx, rx) = oneshot::channel(); - entry.insert(Handle::LocalWaiting(tx)); - let mut cancel_on_drop = CancelReadOnDrop:: { - 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, - } + // 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:: { + // handle: Some(handle), + // _phantom: PhantomData, + // }; + // Box::pin(async move { + // let mut buffer = iter::repeat_with(MaybeUninit::uninit) + // .take(ceiling(64 * 1024, mem::size_of::())) + // .collect::>(); + + // let result = if let Some(count) = + // T::read(handle, &mut buffer).await + // { + // buffer.truncate(count); + // Some(unsafe { + // mem::transmute::>, Vec>(buffer) + // }) + // } else { + // None + // }; + // cancel_on_drop.handle = None; + // drop(cancel_on_drop); + // result + // }) + // as Pin>> + // } + // Handle::LocalOpen => { + // let (tx, rx) = oneshot::channel(); + // entry.insert(Handle::LocalWaiting(tx)); + // let mut cancel_on_drop = CancelReadOnDrop:: { + // 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, + // } + todo!() } } impl Drop for StreamReader { 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!(), - }, - }); + // 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!(), + // }, + // }); + todo!() } } /// Creates a new Component Model `future` with the specified payload type. - pub fn new_future() -> (FutureWriter, FutureReader) { - 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, - }, - ) - } + // pub fn new_future() -> (FutureWriter, FutureReader) { + // 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() -> (StreamWriter, StreamReader) { - 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, - }, - ) + // 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, + // }, + // ) + todo!() } fn ceiling(x: usize, y: usize) -> usize { From 61e9b2081331402abc0111dd9dc8fbe4354091b7 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 11 Dec 2024 16:09:00 +0100 Subject: [PATCH 419/672] Stream new needs implementation --- .../tests/symmetric_stream/main/src/main.rs | 7 +- .../tests/symmetric_stream/stream/src/lib.rs | 4 +- .../stream/src/stream_world.rs | 1882 +++++++++-------- .../rust-client/src/async_support.rs | 39 +- 4 files changed, 988 insertions(+), 944 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index 0b1d1b166..b6c0aad62 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -10,6 +10,11 @@ extern "C" { fn main() { let mut result_stream: *mut () = core::ptr::null_mut(); - let handle = unsafe { testX3AtestX2Fstream_testX00X5BasyncX5Dcreate(core::ptr::null_mut(), (&mut result_stream as *mut *mut ()).cast()) }; + let handle = unsafe { + testX3AtestX2Fstream_testX00X5BasyncX5Dcreate( + core::ptr::null_mut(), + (&mut result_stream as *mut *mut ()).cast(), + ) + }; assert!(handle.is_null()); } diff --git a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs index 2be1ed261..56e66f6d1 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs @@ -1,6 +1,6 @@ +use futures::{SinkExt, StreamExt}; use stream_world::{stream_and_future_support, test::test::stream_source::create}; use wit_bindgen_symmetric_rt::async_support; -use futures::{StreamExt, SinkExt}; mod stream_world; @@ -16,7 +16,7 @@ impl stream_world::exports::test::test::stream_test::Guest for MyStruct { async_support::spawn(async move { while let Some(values) = input.next().await { for value in values { - writer.feed(vec![value, value+1]).await.unwrap(); + writer.feed(vec![value, value + 1]).await.unwrap(); } } }); diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 990645fb2..5f22b1e98 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -2,187 +2,220 @@ // Options used: #[allow(dead_code, clippy::all)] pub mod test { - pub mod test { - - #[allow(dead_code, 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; - use _rt::stream_and_future_support::WitStream; - - impl _rt::stream_and_future_support::StreamPayload for u32{ - fn new() -> *mut WitStream { - { - // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - // extern "C" { - // #[link_name = "[stream-new-0]create"] - // fn new() -> u32; - // } - // unsafe { new() } - todo!() - } - } - - async fn write(stream: *mut WitStream, values: &[Self]) -> Option { - { - // let address = values.as_ptr() as _; - - // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - // extern "C" { - // #[link_name = "[async][stream-write-0]create"] - // fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; - // } - - // unsafe { - // ::wit_bindgen_symmetric_rt::async_support::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await - // } - todo!() - } - } - - async fn read(stream: *mut WitStream, values: &mut [::core::mem::MaybeUninit::]) -> Option { - { - // let address = values.as_mut_ptr() as _; - // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - // extern "C" { - // #[link_name = "[async][stream-read-0]create"] - // fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; - // } - - // let count = unsafe { - // ::wit_bindgen_symmetric_rt::async_support::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await - // }; - // #[allow(unused)] - // if let Some(count) = count { - // let value = (); - - // } - // count - todo!() - } - } - - fn cancel_write(writer: *mut WitStream) { - { - // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - // extern "C" { - // #[link_name = "[stream-cancel-write-0]create"] - // fn cancel(_: u32) -> u32; - // } - // unsafe { cancel(writer) }; - todo!() - } - } - - fn cancel_read(reader: *mut WitStream) { - { - // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - // extern "C" { - // #[link_name = "[stream-cancel-read-0]create"] - // fn cancel(_: u32) -> u32; - // } - // unsafe { cancel(reader) }; - todo!() - } - } - - fn close_writable(writer: *mut WitStream) { - { - // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - // extern "C" { - // #[link_name = "[stream-close-writable-0]create"] - // fn drop(_: u32, _: u32); - // } - // unsafe { drop(writer, 0) } - todo!() - } - } + pub mod test { - fn close_readable(reader: *mut WitStream) { - { - // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - // extern "C" { - // #[link_name = "[stream-close-readable-0]create"] - // fn drop(_: u32); - // } - // unsafe { drop(reader) } - todo!() - } - } - } + #[allow(dead_code, 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; + use _rt::stream_and_future_support::WitStream; + + impl _rt::stream_and_future_support::StreamPayload for u32 { + fn new() -> *mut WitStream { + { + // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + // extern "C" { + // #[link_name = "[stream-new-0]create"] + // fn new() -> u32; + // } + // unsafe { new() } + todo!() + } + } + + async fn write(stream: *mut WitStream, values: &[Self]) -> Option { + { + // let address = values.as_ptr() as _; + + // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + // extern "C" { + // #[link_name = "[async][stream-write-0]create"] + // fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; + // } + + // unsafe { + // ::wit_bindgen_symmetric_rt::async_support::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await + // } + todo!() + } + } + + async fn read( + stream: *mut WitStream, + values: &mut [::core::mem::MaybeUninit], + ) -> Option { + { + // let address = values.as_mut_ptr() as _; + // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + // extern "C" { + // #[link_name = "[async][stream-read-0]create"] + // fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; + // } + + // let count = unsafe { + // ::wit_bindgen_symmetric_rt::async_support::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await + // }; + // #[allow(unused)] + // if let Some(count) = count { + // let value = (); + + // } + // count + todo!() + } + } + + fn cancel_write(writer: *mut WitStream) { + { + // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + // extern "C" { + // #[link_name = "[stream-cancel-write-0]create"] + // fn cancel(_: u32) -> u32; + // } + // unsafe { cancel(writer) }; + todo!() + } + } + + fn cancel_read(reader: *mut WitStream) { + { + // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + // extern "C" { + // #[link_name = "[stream-cancel-read-0]create"] + // fn cancel(_: u32) -> u32; + // } + // unsafe { cancel(reader) }; + todo!() + } + } + + fn close_writable(writer: *mut WitStream) { + { + // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + // extern "C" { + // #[link_name = "[stream-close-writable-0]create"] + // fn drop(_: u32, _: u32); + // } + // unsafe { drop(writer, 0) } + todo!() + } + } + + fn close_readable(reader: *mut WitStream) { + { + // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] + // extern "C" { + // #[link_name = "[stream-close-readable-0]create"] + // fn drop(_: u32); + // } + // unsafe { drop(reader) } + todo!() + } + } + } - #[allow(unused_unsafe, clippy::all)] - pub async fn create() -> _rt::stream_and_future_support::StreamReader{ - unsafe { - let layout0 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); - let ptr0 = _rt::alloc::alloc(layout0); - let layout1 = _rt::alloc::Layout::from_size_align_unchecked(core::mem::size_of::<*const u8>(), core::mem::size_of::<*const u8>()); - let ptr1 = _rt::alloc::alloc(layout1); - - #[link(wasm_import_module = "test:test/stream-source")] - #[link(name="source")] - extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "[async]create")] - fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(_: *mut u8, _: *mut u8, ) -> *mut u8; - } - // let layout2 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); - ::wit_bindgen_symmetric_rt::async_support::await_result(testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate, ptr0, ptr1).await; - let l3 = *ptr1.add(0).cast::<*mut u8>(); - let result4 = _rt::stream_and_future_support::StreamReader::from_handle(l3.cast()); - _rt::cabi_dealloc(ptr1, core::mem::size_of::<*const u8>(), core::mem::size_of::<*const u8>()); - result4 + #[allow(unused_unsafe, clippy::all)] + pub async fn create() -> _rt::stream_and_future_support::StreamReader { + unsafe { + let layout0 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); + let ptr0 = _rt::alloc::alloc(layout0); + let layout1 = _rt::alloc::Layout::from_size_align_unchecked( + core::mem::size_of::<*const u8>(), + core::mem::size_of::<*const u8>(), + ); + let ptr1 = _rt::alloc::alloc(layout1); + + #[link(wasm_import_module = "test:test/stream-source")] + #[link(name = "source")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "[async]create")] + fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate( + _: *mut u8, + _: *mut u8, + ) -> *mut u8; + } + // let layout2 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); + ::wit_bindgen_symmetric_rt::async_support::await_result( + testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate, + ptr0, + ptr1, + ) + .await; + let l3 = *ptr1.add(0).cast::<*mut u8>(); + let result4 = + _rt::stream_and_future_support::StreamReader::from_handle(l3.cast()); + _rt::cabi_dealloc( + ptr1, + core::mem::size_of::<*const u8>(), + core::mem::size_of::<*const u8>(), + ); + result4 + } + } } - } - } - - } } #[allow(dead_code, clippy::all)] pub mod exports { - pub mod test { pub mod test { - - #[allow(dead_code, 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(_args: *mut u8, _results: *mut u8) -> *mut u8 {#[cfg(target_arch="wasm32")] - _rt::run_ctors_once();let result0 = T::create(); - let result = ::wit_bindgen_symmetric_rt::async_support::first_poll(result0, |result1| { - - - #[link(wasm_import_module = "[export]test:test/stream-test")] - extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "[task-return]create")] - fn X5BexportX5DtestX3AtestX2Fstream_testX00X5Btask_returnX5Dcreate(_: i32, ); - } - // X5BexportX5DtestX3AtestX2Fstream_testX00X5Btask_returnX5Dcreate((result1).into_handle() as i32); - }); - - result.cast() - } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn __callback_create(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { - ::wit_bindgen_symmetric_rt::async_support::callback(ctx, event0, event1, event2) - } - pub trait Guest { - fn create() -> impl ::core::future::Future> + 'static; - } - #[doc(hidden)] - - macro_rules! __export_test_test_stream_test_cabi{ + pub mod test { + + #[allow(dead_code, 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( + _args: *mut u8, + _results: *mut u8, + ) -> *mut u8 { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + let result0 = T::create(); + let result = + ::wit_bindgen_symmetric_rt::async_support::first_poll(result0, |result1| { + #[link(wasm_import_module = "[export]test:test/stream-test")] + extern "C" { + #[cfg_attr( + target_arch = "wasm32", + link_name = "[task-return]create" + )] + fn X5BexportX5DtestX3AtestX2Fstream_testX00X5Btask_returnX5Dcreate( + _: i32, + ); + } + // X5BexportX5DtestX3AtestX2Fstream_testX00X5Btask_returnX5Dcreate((result1).into_handle() as i32); + }); + + result.cast() + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn __callback_create( + ctx: *mut u8, + event0: i32, + event1: i32, + event2: i32, + ) -> i32 { + ::wit_bindgen_symmetric_rt::async_support::callback(ctx, event0, event1, event2) + } + pub trait Guest { + fn create() -> impl ::core::future::Future< + Output = _rt::stream_and_future_support::StreamReader, + > + 'static; + } + #[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")] @@ -196,814 +229,810 @@ pub mod exports { // } };); } - #[doc(hidden)] - pub(crate) use __export_test_test_stream_test_cabi; - + #[doc(hidden)] + pub(crate) use __export_test_test_stream_test_cabi; + } + } } - - } -} } mod _rt { - #![allow(dead_code, clippy::all)] - 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_symmetric_rt::async_support::{self, Handle}, - }; - pub use wit_bindgen_symmetric_rt::async_support::Stream as WitStream; - - #[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; - 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 { - handle: u32, - _phantom: PhantomData, - } + #![allow(dead_code, clippy::all)] + pub mod stream_and_future_support { + pub use wit_bindgen_symmetric_rt::async_support::Stream as WitStream; + 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_symmetric_rt::async_support::{self, Handle}, + }; - impl fmt::Debug for FutureWriter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FutureWriter") - .field("handle", &self.handle) - .finish() - } - } + #[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; + fn cancel_write(future: u32); + fn cancel_read(future: u32); + fn close_writable(future: u32); + fn close_readable(future: u32); + } - /// Represents a write operation which may be canceled prior to completion. - pub struct CancelableWrite { - writer: Option>, - future: Pin>>, - } + /// Represents the writable end of a Component Model `future`. + pub struct FutureWriter { + handle: u32, + _phantom: PhantomData, + } - impl Future for CancelableWrite { - type Output = (); + impl fmt::Debug for FutureWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureWriter") + .field("handle", &self.handle) + .finish() + } + } - 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, + /// Represents a write operation which may be canceled prior to completion. + pub struct CancelableWrite { + writer: Option>, + future: Pin>>, } - } - } - impl CancelableWrite { - /// 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 { - self.cancel_mut() - } + impl Future for CancelableWrite { + 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, + } + } + } - fn cancel_mut(&mut self) -> FutureWriter { - // 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 - todo!() - } - } + impl CancelableWrite { + /// 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 { + self.cancel_mut() + } - impl Drop for CancelableWrite { - fn drop(&mut self) { - if self.writer.is_some() { - self.cancel_mut(); + fn cancel_mut(&mut self) -> FutureWriter { + // 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 + todo!() + } } - } - } - impl FutureWriter { - /// Write the specified value to this `future`. - pub fn write(self, v: T) -> CancelableWrite { - // 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>> - // } - // 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)), - // }, - // }), - // } - todo!() - } - } + impl Drop for CancelableWrite { + fn drop(&mut self) { + if self.writer.is_some() { + self.cancel_mut(); + } + } + } - impl Drop for FutureWriter { - 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); - // } - // }, - // }); - todo!() - } - } + impl FutureWriter { + /// Write the specified value to this `future`. + pub fn write(self, v: T) -> CancelableWrite { + // 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>> + // } + // 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)), + // }, + // }), + // } + todo!() + } + } - /// Represents a read operation which may be canceled prior to completion. - pub struct CancelableRead { - reader: Option>, - future: Pin>>>, - } + impl Drop for FutureWriter { + 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); + // } + // }, + // }); + todo!() + } + } - impl Future for CancelableRead { - type Output = Option; + /// Represents a read operation which may be canceled prior to completion. + pub struct CancelableRead { + reader: Option>, + future: Pin>>>, + } - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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 Future for CancelableRead { + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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 CancelableRead { - /// 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 { - self.cancel_mut() - } + impl CancelableRead { + /// 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 { + self.cancel_mut() + } - fn cancel_mut(&mut self) -> FutureReader { - // 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 - todo!() - } - } + fn cancel_mut(&mut self) -> FutureReader { + // 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 + todo!() + } + } + + impl Drop for CancelableRead { + fn drop(&mut self) { + if self.reader.is_some() { + self.cancel_mut(); + } + } + } - impl Drop for CancelableRead { - fn drop(&mut self) { - if self.reader.is_some() { - self.cancel_mut(); + /// Represents the readable end of a Component Model `future`. + pub struct FutureReader { + handle: u32, + _phantom: PhantomData, } - } - } - /// Represents the readable end of a Component Model `future`. - pub struct FutureReader { - handle: u32, - _phantom: PhantomData, - } + impl fmt::Debug for FutureReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureReader") + .field("handle", &self.handle) + .finish() + } + } - impl fmt::Debug for FutureReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FutureReader") - .field("handle", &self.handle) - .finish() - } - } + impl FutureReader { + #[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, + // } + todo!() + } - impl FutureReader { - #[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, - // } - todo!() - } + #[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 + todo!() + } + } - #[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 - todo!() - } - } + impl IntoFuture for FutureReader { + type Output = Option; + type IntoFuture = CancelableRead; + + /// 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>>, + // 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()))) + // } + // }, + // }), + // } + todo!() + } + } - impl IntoFuture for FutureReader { - type Output = Option; - type IntoFuture = CancelableRead; - - /// 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>>, - // 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()))) - // } - // }, - // }), - // } - todo!() - } - } + impl Drop for FutureReader { + 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!(), + // }, + // }); + todo!() + } + } - impl Drop for FutureReader { - 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!(), - // }, - // }); - todo!() - } - } + #[doc(hidden)] + pub trait StreamPayload: Unpin + Sized + 'static { + fn new() -> *mut WitStream; + async fn write(stream: *mut WitStream, values: &[Self]) -> Option; + async fn read( + stream: *mut WitStream, + values: &mut [MaybeUninit], + ) -> Option; + fn cancel_write(stream: *mut WitStream); + fn cancel_read(stream: *mut WitStream); + fn close_writable(stream: *mut WitStream); + fn close_readable(stream: *mut WitStream); + } - #[doc(hidden)] - pub trait StreamPayload: Unpin + Sized + 'static { - fn new() -> *mut WitStream; - async fn write(stream: *mut WitStream, values: &[Self]) -> Option; - async fn read(stream: *mut WitStream, values: &mut [MaybeUninit]) -> Option; - fn cancel_write(stream: *mut WitStream); - fn cancel_read(stream: *mut WitStream); - fn close_writable(stream: *mut WitStream); - fn close_readable(stream: *mut WitStream); - } + struct CancelWriteOnDrop { + handle: Option<*mut WitStream>, + _phantom: PhantomData, + } - struct CancelWriteOnDrop { - handle: Option<*mut WitStream>, - _phantom: PhantomData, - } + impl Drop for CancelWriteOnDrop { + 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), + // }, + // }); + // } + todo!() + } + } - impl Drop for CancelWriteOnDrop { - 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), - // }, - // }); - // } - todo!() - } - } + /// Represents the writable end of a Component Model `stream`. + pub struct StreamWriter { + handle: *mut WitStream, + future: Option + 'static>>>, + _phantom: PhantomData, + } - /// Represents the writable end of a Component Model `stream`. - pub struct StreamWriter { - handle: *mut WitStream, - future: Option + 'static>>>, - _phantom: PhantomData, - } + impl StreamWriter { + /// 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 StreamWriter { - /// 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 fmt::Debug for StreamWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamWriter") + .field("handle", &self.handle) + .finish() + } + } - impl fmt::Debug for StreamWriter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StreamWriter") - .field("handle", &self.handle) - .finish() - } - } + impl Sink> for StreamWriter { + type Error = Infallible; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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(())) + } + } - impl Sink> for StreamWriter { - type Error = Infallible; + fn start_send(self: Pin<&mut Self>, item: Vec) -> 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:: { + // 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:: { + // 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(()) + todo!() + } - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let me = self.get_mut(); + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } - if let Some(future) = &mut me.future { - match future.as_mut().poll(cx) { - Poll::Ready(_) => { - me.future = None; - Poll::Ready(Ok(())) + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) } - Poll::Pending => Poll::Pending, - } - } else { - Poll::Ready(Ok(())) } - } - fn start_send(self: Pin<&mut Self>, item: Vec) -> 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:: { - // 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:: { - // 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(()) - todo!() - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.poll_ready(cx) - } + impl Drop for StreamWriter { + 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); + // } + // }, + // }); + todo!() + } + } - fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.poll_ready(cx) - } - } + struct CancelReadOnDrop { + handle: Option<*mut WitStream>, + _phantom: PhantomData, + } - impl Drop for StreamWriter { - 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); - // } - // }, - // }); - todo!() - } - } + impl Drop for CancelReadOnDrop { + 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), + // }, + // }); + // } + todo!() + } + } - struct CancelReadOnDrop { - handle: Option<*mut WitStream>, - _phantom: PhantomData, - } + /// Represents the readable end of a Component Model `stream`. + pub struct StreamReader { + handle: *mut WitStream, + future: Option>> + 'static>>>, + _phantom: PhantomData, + } - impl Drop for CancelReadOnDrop { - 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), - // }, - // }); - // } - todo!() - } - } + impl StreamReader { + /// 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; + } + } - /// Represents the readable end of a Component Model `stream`. - pub struct StreamReader { - handle: *mut WitStream, - future: Option>> + 'static>>>, - _phantom: PhantomData, - } + impl fmt::Debug for StreamReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamReader") + .field("handle", &self.handle) + .finish() + } + } - impl StreamReader { - /// 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 StreamReader { + #[doc(hidden)] + pub fn from_handle(handle: *mut WitStream) -> 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, + // } + todo!() + } - impl fmt::Debug for StreamReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StreamReader") - .field("handle", &self.handle) - .finish() - } - } + #[doc(hidden)] + pub fn into_handle(self) -> *mut WitStream { + // 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 + todo!() + } + } - impl StreamReader { - #[doc(hidden)] - pub fn from_handle(handle: *mut WitStream) -> 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, - // } - todo!() - } + impl Stream for StreamReader { + type Item = Vec; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + // 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:: { + // handle: Some(handle), + // _phantom: PhantomData, + // }; + // Box::pin(async move { + // let mut buffer = iter::repeat_with(MaybeUninit::uninit) + // .take(ceiling(64 * 1024, mem::size_of::())) + // .collect::>(); + + // let result = if let Some(count) = T::read(handle, &mut buffer).await { + // buffer.truncate(count); + // Some(unsafe { + // mem::transmute::>, Vec>(buffer) + // }) + // } else { + // None + // }; + // cancel_on_drop.handle = None; + // drop(cancel_on_drop); + // result + // }) as Pin>> + // } + // Handle::LocalOpen => { + // let (tx, rx) = oneshot::channel(); + // entry.insert(Handle::LocalWaiting(tx)); + // let mut cancel_on_drop = CancelReadOnDrop:: { + // 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, + // } + todo!() + } + } - #[doc(hidden)] - pub fn into_handle(self) -> *mut WitStream { - // 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 - todo!() - } - } + impl Drop for StreamReader { + 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!(), + // }, + // }); + todo!() + } + } - impl Stream for StreamReader { - type Item = Vec; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - // 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:: { - // handle: Some(handle), - // _phantom: PhantomData, - // }; - // Box::pin(async move { - // let mut buffer = iter::repeat_with(MaybeUninit::uninit) - // .take(ceiling(64 * 1024, mem::size_of::())) - // .collect::>(); - - // let result = if let Some(count) = T::read(handle, &mut buffer).await { - // buffer.truncate(count); - // Some(unsafe { - // mem::transmute::>, Vec>(buffer) - // }) - // } else { - // None - // }; - // cancel_on_drop.handle = None; - // drop(cancel_on_drop); - // result - // }) as Pin>> - // } - // Handle::LocalOpen => { - // let (tx, rx) = oneshot::channel(); - // entry.insert(Handle::LocalWaiting(tx)); - // let mut cancel_on_drop = CancelReadOnDrop:: { - // 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, - // } - todo!() - } - } + /// Creates a new Component Model `future` with the specified payload type. + pub fn new_future() -> (FutureWriter, FutureReader) { + // 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, + // }, + // ) + todo!() + } - impl Drop for StreamReader { - 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!(), - // }, - // }); - todo!() - } - } + /// Creates a new Component Model `stream` with the specified payload type. + pub fn new_stream() -> (StreamWriter, StreamReader) { + let handle = Box::into_raw(Box::new(WitStream::new())); + ( + StreamWriter { + handle, + future: None, + _phantom: PhantomData, + }, + StreamReader { + handle, + future: None, + _phantom: PhantomData, + }, + ) + } - /// Creates a new Component Model `future` with the specified payload type. - pub fn new_future() -> (FutureWriter, FutureReader) { - // 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, - // }, - // ) - todo!() + fn ceiling(x: usize, y: usize) -> usize { + (x / y) + if x % y == 0 { 0 } else { 1 } + } } - - /// Creates a new Component Model `stream` with the specified payload type. - pub fn new_stream() -> (StreamWriter, StreamReader) { - // 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, - // }, - // ) - todo!() + pub use alloc_crate::alloc; + 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); } - fn ceiling(x: usize, y: usize) -> usize { - (x / y) + if x % y == 0 { 0 } else { 1 } + #[cfg(target_arch = "wasm32")] + pub fn run_ctors_once() { + wit_bindgen::rt::run_ctors_once(); } - }pub use alloc_crate::alloc; - 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); - } - - #[cfg(target_arch = "wasm32")] - pub fn run_ctors_once() { - wit_bindgen::rt::run_ctors_once(); - } - extern crate alloc as alloc_crate; + extern crate alloc as alloc_crate; } #[allow(unused_imports)] pub use _rt::stream_and_future_support; @@ -1051,6 +1080,5 @@ tream-world\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x07 #[inline(never)] #[doc(hidden)] pub fn __link_custom_section_describing_imports() { - wit_bindgen::rt::maybe_link_cabi_realloc(); + wit_bindgen::rt::maybe_link_cabi_realloc(); } - diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index e4bc5de61..ab9b0b793 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -7,10 +7,8 @@ use std::{ task::{Context, Poll, RawWaker, RawWakerVTable}, }; -use crate::module::symmetric::runtime::{ - symmetric_executor::{ - self, CallbackState, EventGenerator, EventSubscription, - }, +use crate::module::symmetric::runtime::symmetric_executor::{ + self, CallbackState, EventGenerator, EventSubscription, }; type BoxFuture = Pin + 'static>>; @@ -49,17 +47,29 @@ pub struct StreamVtable { #[repr(C)] pub struct Stream { - vtable: *const StreamVtable, - read_ready_event_send: *mut (), - write_ready_event_send: *mut (), - read_addr: *mut (), - read_size: usize, + pub vtable: *const StreamVtable, + pub read_ready_event_send: *mut (), + pub write_ready_event_send: *mut (), + pub read_addr: *mut (), + pub read_size: usize, +} + +impl Stream { + pub fn new() -> Self { + Self { + vtable: todo!(), + read_ready_event_send: todo!(), + write_ready_event_send: todo!(), + read_addr: core::ptr::null_mut(), + read_size: 0, + } + } } // pub enum Entry<'a, K, V> { // Vacant(), // Occupied(&'a mut Stream), -// } +// } // #[doc(hidden)] // pub fn with_entry(h: *mut (), f: impl FnOnce(Entry<'_, u32, Handle>) -> T) -> T { @@ -104,8 +114,9 @@ async fn wait_on(wait_for: &EventSubscription) { let data = cx.waker().data(); // dangerous duplication? let wait_for_copy = unsafe { EventSubscription::from_handle(wait_for.handle()) }; - let old_waiting_for = unsafe { &mut *(data.cast::>().cast_mut()) } - .replace(wait_for_copy); + let old_waiting_for = + unsafe { &mut *(data.cast::>().cast_mut()) } + .replace(wait_for_copy); // don't free the old subscription we found if let Some(subscription) = old_waiting_for { subscription.take_handle(); @@ -181,5 +192,5 @@ pub unsafe fn callback(_ctx: *mut u8, _event0: i32, _event1: i32, _event2: i32) } pub fn spawn(_future: impl Future + 'static) { -todo!() -} \ No newline at end of file + todo!() +} From d5d419aa92bdb42444260b3604eaecc971144897 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 29 Dec 2024 11:58:30 +0100 Subject: [PATCH 420/672] stream new impl --- .../rust-client/src/async_support.rs | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index ab9b0b793..3333f8127 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -54,12 +54,35 @@ pub struct Stream { pub read_size: usize, } +fn read_impl(_stream: *mut (), _buf: *mut (), _size: usize) -> isize { + todo!() +} + +fn write_impl(_stream: *mut (), _buf: *mut (), _size: usize) -> isize { + todo!() +} + +fn read_close_impl(stream: *mut ()) { + todo!() +} + +fn write_close_impl(stream: *mut ()) { + todo!() +} + +const STREAM_VTABLE: StreamVtable = StreamVtable{ + read: read_impl, + close_read: read_close_impl, + write: write_impl, + close_write: write_close_impl, +}; + impl Stream { pub fn new() -> Self { Self { - vtable: todo!(), - read_ready_event_send: todo!(), - write_ready_event_send: todo!(), + vtable: &STREAM_VTABLE as *const StreamVtable, + read_ready_event_send: EventGenerator::new().take_handle() as *mut (), + write_ready_event_send: EventGenerator::new().take_handle() as *mut (), read_addr: core::ptr::null_mut(), read_size: 0, } From 7b8b5fe7f4f3730ca5c903a7113eb53c24c950a6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 30 Dec 2024 22:44:39 +0100 Subject: [PATCH 421/672] should the futures be Send? --- crates/cpp/tests/symmetric_stream/Cargo.lock | 4 +++ .../tests/symmetric_stream/source/Cargo.toml | 2 ++ .../tests/symmetric_stream/source/build.rs | 9 ++++++ .../tests/symmetric_stream/source/src/lib.rs | 8 ++++-- .../stream/src/stream_world.rs | 28 ++++++++++--------- .../rust-client/src/async_support.rs | 14 ++++------ .../symmetric_executor/rust-client/src/lib.rs | 10 +++++++ 7 files changed, 52 insertions(+), 23 deletions(-) create mode 100644 crates/cpp/tests/symmetric_stream/source/build.rs diff --git a/crates/cpp/tests/symmetric_stream/Cargo.lock b/crates/cpp/tests/symmetric_stream/Cargo.lock index 5ad8d108b..b0374cce8 100644 --- a/crates/cpp/tests/symmetric_stream/Cargo.lock +++ b/crates/cpp/tests/symmetric_stream/Cargo.lock @@ -368,6 +368,10 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "source" version = "0.1.0" +dependencies = [ + "symmetric_executor", + "wit-bindgen-symmetric-rt", +] [[package]] name = "spdx" diff --git a/crates/cpp/tests/symmetric_stream/source/Cargo.toml b/crates/cpp/tests/symmetric_stream/source/Cargo.toml index a53b889b2..03f1e5961 100644 --- a/crates/cpp/tests/symmetric_stream/source/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/source/Cargo.toml @@ -4,6 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] +symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor" } +wit-bindgen-symmetric-rt = { version = "0.36.0", 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 index 063dc70f4..d854c296d 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -1,6 +1,10 @@ +use wit_bindgen_symmetric_rt::async_support::Stream; + #[allow(non_snake_case)] #[no_mangle] -pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(_args:*mut u8, _results:*mut u8) -> *mut u8 { - todo!() +pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(_args:*mut u8, results:*mut u8) -> *mut u8 { + let obj = Box::new(Stream::new()); + *unsafe{&mut *results.cast::<*mut Stream>()} = Box::into_raw(obj); + std::ptr::null_mut() } diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 5f22b1e98..579317d54 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -630,9 +630,12 @@ mod _rt { } } + pub struct StreamHandle2(*mut WitStream); + unsafe impl Send for StreamHandle2 {} + /// Represents the writable end of a Component Model `stream`. pub struct StreamWriter { - handle: *mut WitStream, + handle: StreamHandle2, future: Option + 'static>>>, _phantom: PhantomData, } @@ -650,7 +653,7 @@ mod _rt { impl fmt::Debug for StreamWriter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("StreamWriter") - .field("handle", &self.handle) + .field("handle", &self.handle.0) .finish() } } @@ -805,8 +808,8 @@ mod _rt { /// Represents the readable end of a Component Model `stream`. pub struct StreamReader { - handle: *mut WitStream, - future: Option>> + 'static>>>, + handle: StreamHandle2, + future: Option>> + 'static + Send>>>, _phantom: PhantomData, } @@ -823,7 +826,7 @@ mod _rt { impl fmt::Debug for StreamReader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("StreamReader") - .field("handle", &self.handle) + .field("handle", &self.handle.0) .finish() } } @@ -849,12 +852,11 @@ mod _rt { // }, // }); - // Self { - // handle, - // future: None, - // _phantom: PhantomData, - // } - todo!() + Self { + handle: StreamHandle2(handle), + future: None, + _phantom: PhantomData, + } } #[doc(hidden)] @@ -1003,12 +1005,12 @@ mod _rt { let handle = Box::into_raw(Box::new(WitStream::new())); ( StreamWriter { - handle, + handle: StreamHandle2(handle), future: None, _phantom: PhantomData, }, StreamReader { - handle, + handle: StreamHandle2(handle), future: None, _phantom: PhantomData, }, diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 3333f8127..21794afff 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -1,10 +1,6 @@ use futures::{channel::oneshot, task::Waker, FutureExt}; use std::{ - any::Any, - collections::hash_map::{self, OccupiedEntry}, - future::Future, - pin::Pin, - task::{Context, Poll, RawWaker, RawWakerVTable}, + any::Any, collections::hash_map::{self, OccupiedEntry}, future::Future, pin::Pin, sync::Mutex, task::{Context, Poll, RawWaker, RawWakerVTable} }; use crate::module::symmetric::runtime::symmetric_executor::{ @@ -70,7 +66,7 @@ fn write_close_impl(stream: *mut ()) { todo!() } -const STREAM_VTABLE: StreamVtable = StreamVtable{ +const STREAM_VTABLE: StreamVtable = StreamVtable { read: read_impl, close_read: read_close_impl, write: write_impl, @@ -214,6 +210,8 @@ pub unsafe fn callback(_ctx: *mut u8, _event0: i32, _event1: i32, _event2: i32) todo!() } -pub fn spawn(_future: impl Future + 'static) { - todo!() +static TASKS: Mutex + 'static + Send>>> = Mutex::new(Vec::new()); + +pub fn spawn(future: impl Future + 'static + Send) { + TASKS.lock().unwrap().push(Box::new(future)); } diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index 644a404f1..a80bdb2ab 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -13,3 +13,13 @@ pub fn register( 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_36_0( + _old_ptr: *mut u8, + _old_len: usize, + _align: usize, + _new_len: usize, +) -> *mut u8 { + todo!() +} From caa2e504e80f76472d1c643e035de317f038fc64 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 2 Jan 2025 11:50:27 +0100 Subject: [PATCH 422/672] read is now called --- crates/cpp/tests/symmetric_stream/Cargo.lock | 1 + .../tests/symmetric_stream/main/Cargo.toml | 1 + .../tests/symmetric_stream/main/src/main.rs | 24 +++++++++++++ .../tests/symmetric_stream/source/src/lib.rs | 8 +++-- .../stream/src/stream_world.rs | 34 ++++++++----------- .../symmetric_executor/rust-client/Cargo.lock | 11 ++++++ .../rust-client/src/async_support.rs | 25 +++++++++----- .../symmetric_executor/rust-client/src/lib.rs | 12 ++++++- 8 files changed, 83 insertions(+), 33 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/Cargo.lock b/crates/cpp/tests/symmetric_stream/Cargo.lock index b0374cce8..c1bd0eecc 100644 --- a/crates/cpp/tests/symmetric_stream/Cargo.lock +++ b/crates/cpp/tests/symmetric_stream/Cargo.lock @@ -228,6 +228,7 @@ version = "0.1.0" dependencies = [ "stream", "symmetric_executor", + "wit-bindgen-symmetric-rt", ] [[package]] diff --git a/crates/cpp/tests/symmetric_stream/main/Cargo.toml b/crates/cpp/tests/symmetric_stream/main/Cargo.toml index 030412058..6a30869cb 100644 --- a/crates/cpp/tests/symmetric_stream/main/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/main/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" [dependencies] stream = { version = "0.1.0", path = "../stream" } symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor" } +wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../../../../symmetric_executor/rust-client" } diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index b6c0aad62..399d8dfd1 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -1,5 +1,12 @@ // use wit_bindgen_symmetric_rt::{CallbackState, EventSubscription}; +use std::pin::pin; + +use wit_bindgen_symmetric_rt::{ + async_support::{self, Stream}, + CallbackState, +}; + #[link(name = "stream")] extern "C" { pub fn testX3AtestX2Fstream_testX00X5BasyncX5Dcreate( @@ -8,6 +15,10 @@ extern "C" { ) -> *mut (); } +extern "C" fn ready(arg: *mut ()) -> CallbackState { + todo!() +} + fn main() { let mut result_stream: *mut () = core::ptr::null_mut(); let handle = unsafe { @@ -17,4 +28,17 @@ fn main() { ) }; assert!(handle.is_null()); + let handle = result_stream.cast::(); + let mut target = pin!(0_u32); + unsafe { + ((&*(&*handle).vtable).read)( + handle, + ((&mut *target) as *mut u32).cast(), + size_of::(), + ); + }; + let read_ready = unsafe { (&*handle).read_ready_event_send }; + let subscription = unsafe { wit_bindgen_symmetric_rt::subscribe_event_send_ptr(read_ready) }; + wit_bindgen_symmetric_rt::register(subscription, ready, ((&mut *target) as *mut u32).cast()); + wit_bindgen_symmetric_rt::run(); } diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index d854c296d..7e252f2ed 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -1,10 +1,12 @@ use wit_bindgen_symmetric_rt::async_support::Stream; #[allow(non_snake_case)] - #[no_mangle] -pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(_args:*mut u8, results:*mut u8) -> *mut u8 { +pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate( + _args: *mut u8, + results: *mut u8, +) -> *mut u8 { let obj = Box::new(Stream::new()); - *unsafe{&mut *results.cast::<*mut Stream>()} = Box::into_raw(obj); + *unsafe { &mut *results.cast::<*mut Stream>() } = Box::into_raw(obj); std::ptr::null_mut() } diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 579317d54..59ab1d3d4 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -176,25 +176,18 @@ pub mod exports { #[allow(non_snake_case)] pub unsafe fn _export_create_cabi( _args: *mut u8, - _results: *mut u8, + results: *mut u8, ) -> *mut u8 { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); let result0 = T::create(); - let result = - ::wit_bindgen_symmetric_rt::async_support::first_poll(result0, |result1| { - #[link(wasm_import_module = "[export]test:test/stream-test")] - extern "C" { - #[cfg_attr( - target_arch = "wasm32", - link_name = "[task-return]create" - )] - fn X5BexportX5DtestX3AtestX2Fstream_testX00X5Btask_returnX5Dcreate( - _: i32, - ); - } - // X5BexportX5DtestX3AtestX2Fstream_testX00X5Btask_returnX5Dcreate((result1).into_handle() as i32); - }); + let result = ::wit_bindgen_symmetric_rt::async_support::first_poll( + result0, + move |result1| { + let outptr = results.cast::<*mut ()>(); + *(unsafe { &mut *outptr }) = result1.into_handle().cast(); + }, + ); result.cast() } @@ -636,7 +629,7 @@ mod _rt { /// Represents the writable end of a Component Model `stream`. pub struct StreamWriter { handle: StreamHandle2, - future: Option + 'static>>>, + future: Option + 'static + Send>>>, _phantom: PhantomData, } @@ -853,9 +846,9 @@ mod _rt { // }); Self { - handle: StreamHandle2(handle), - future: None, - _phantom: PhantomData, + handle: StreamHandle2(handle), + future: None, + _phantom: PhantomData, } } @@ -875,7 +868,8 @@ mod _rt { // }); // ManuallyDrop::new(self).handle - todo!() + // todo!() + ManuallyDrop::new(self).handle.0 } } diff --git a/crates/symmetric_executor/rust-client/Cargo.lock b/crates/symmetric_executor/rust-client/Cargo.lock index 4bd09ef9d..662047506 100644 --- a/crates/symmetric_executor/rust-client/Cargo.lock +++ b/crates/symmetric_executor/rust-client/Cargo.lock @@ -366,6 +366,16 @@ dependencies = [ "smallvec", ] +[[package]] +name = "symmetric_executor" +version = "0.1.0" +dependencies = [ + "futures", + "libc", + "wit-bindgen", + "wit-bindgen-rt", +] + [[package]] name = "syn" version = "2.0.90" @@ -546,6 +556,7 @@ name = "wit-bindgen-symmetric-rt" version = "0.36.0" dependencies = [ "futures", + "symmetric_executor", "wit-bindgen", ] diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 21794afff..be14bd5e0 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -1,12 +1,19 @@ use futures::{channel::oneshot, task::Waker, FutureExt}; use std::{ - any::Any, collections::hash_map::{self, OccupiedEntry}, future::Future, pin::Pin, sync::Mutex, task::{Context, Poll, RawWaker, RawWakerVTable} + any::Any, future::Future, pin::Pin, sync::Mutex, task::{Context, Poll, RawWaker, RawWakerVTable} }; use crate::module::symmetric::runtime::symmetric_executor::{ self, CallbackState, EventGenerator, EventSubscription, }; +// 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 MaybeSend for T where T: Send {} + +// pub trait FutureMaybeSend : Future + MaybeSend {} + type BoxFuture = Pin + 'static>>; struct FutureState { @@ -31,11 +38,11 @@ pub enum Handle { pub struct StreamVtable { // magic value for EOF(-1) and block(-MAX) // asynchronous function, if this blocks wait for read ready event - pub read: fn(stream: *mut (), buf: *mut (), size: usize) -> isize, - pub close_read: fn(stream: *mut ()), + pub read: fn(stream: *mut Stream, buf: *mut (), size: usize) -> isize, + pub close_read: fn(stream: *mut Stream), - pub write: fn(stream: *mut (), buf: *mut (), size: usize) -> isize, - pub close_write: fn(stream: *mut ()), + pub write: fn(stream: *mut Stream, buf: *mut (), size: usize) -> isize, + pub close_write: fn(stream: *mut Stream), // post WASI 0.3, CPB // pub allocate: fn(stream: *mut ()) -> (*mut (), isize), // pub publish: fn(stream: *mut (), size: usize), @@ -50,19 +57,19 @@ pub struct Stream { pub read_size: usize, } -fn read_impl(_stream: *mut (), _buf: *mut (), _size: usize) -> isize { +fn read_impl(_stream: *mut Stream, _buf: *mut (), _size: usize) -> isize { todo!() } -fn write_impl(_stream: *mut (), _buf: *mut (), _size: usize) -> isize { +fn write_impl(_stream: *mut Stream, _buf: *mut (), _size: usize) -> isize { todo!() } -fn read_close_impl(stream: *mut ()) { +fn read_close_impl(stream: *mut Stream) { todo!() } -fn write_close_impl(stream: *mut ()) { +fn write_close_impl(stream: *mut Stream) { todo!() } diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index a80bdb2ab..f95a74b31 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -1,5 +1,6 @@ use module::symmetric::runtime::symmetric_executor::{self, CallbackData, CallbackFunction}; -pub use module::symmetric::runtime::symmetric_executor::{run, CallbackState, EventSubscription}; +pub use module::symmetric::runtime::symmetric_executor::{run, CallbackState, EventSubscription, EventGenerator}; +use std::sync::Arc; pub mod async_support; mod module; @@ -23,3 +24,12 @@ fn cabi_realloc_wit_bindgen_0_36_0( ) -> *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 +} From 16e73997ee989d4eb5d12e0da4e4754c6c9503ee Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 2 Jan 2025 15:09:17 +0100 Subject: [PATCH 423/672] read implemented --- .../rust-client/src/async_support.rs | 40 ++++++++++++++----- .../symmetric_executor/rust-client/src/lib.rs | 4 +- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index be14bd5e0..58dd8b6e7 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -1,6 +1,13 @@ use futures::{channel::oneshot, task::Waker, FutureExt}; use std::{ - any::Any, future::Future, pin::Pin, sync::Mutex, task::{Context, Poll, RawWaker, RawWakerVTable} + any::Any, + future::Future, + pin::Pin, + sync::{ + atomic::{AtomicPtr, AtomicUsize, Ordering}, + Mutex, + }, + task::{Context, Poll, RawWaker, RawWakerVTable}, }; use crate::module::symmetric::runtime::symmetric_executor::{ @@ -36,7 +43,7 @@ pub enum Handle { #[repr(C)] pub struct StreamVtable { - // magic value for EOF(-1) and block(-MAX) + // magic value for EOF(-1) and block(MIN) // asynchronous function, if this blocks wait for read ready event pub read: fn(stream: *mut Stream, buf: *mut (), size: usize) -> isize, pub close_read: fn(stream: *mut Stream), @@ -53,12 +60,25 @@ pub struct Stream { pub vtable: *const StreamVtable, pub read_ready_event_send: *mut (), pub write_ready_event_send: *mut (), - pub read_addr: *mut (), - pub read_size: usize, -} - -fn read_impl(_stream: *mut Stream, _buf: *mut (), _size: usize) -> isize { - todo!() + pub read_addr: AtomicPtr<()>, + pub read_size: AtomicUsize, +} + +fn read_impl(stream: *mut Stream, buf: *mut (), size: usize) -> isize { + let old_size = unsafe { &mut *stream } + .read_size + .swap(size, Ordering::Acquire); + assert_eq!(old_size, 0); + let old_ptr = unsafe { &mut *stream } + .read_addr + .swap(buf, Ordering::Release); + assert_eq!(old_ptr, std::ptr::null_mut()); + let write_evt = unsafe { &mut *stream }.write_ready_event_send; + let gen: EventGenerator = unsafe { EventGenerator::from_handle(write_evt as usize) }; + gen.activate(); + // don't consume + let _ = gen.take_handle(); + isize::MIN } fn write_impl(_stream: *mut Stream, _buf: *mut (), _size: usize) -> isize { @@ -86,8 +106,8 @@ impl Stream { vtable: &STREAM_VTABLE as *const StreamVtable, read_ready_event_send: EventGenerator::new().take_handle() as *mut (), write_ready_event_send: EventGenerator::new().take_handle() as *mut (), - read_addr: core::ptr::null_mut(), - read_size: 0, + read_addr: AtomicPtr::new(core::ptr::null_mut()), + read_size: AtomicUsize::new(0), } } } diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index f95a74b31..dfd9d6645 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -1,5 +1,7 @@ use module::symmetric::runtime::symmetric_executor::{self, CallbackData, CallbackFunction}; -pub use module::symmetric::runtime::symmetric_executor::{run, CallbackState, EventSubscription, EventGenerator}; +pub use module::symmetric::runtime::symmetric_executor::{ + run, CallbackState, EventGenerator, EventSubscription, +}; use std::sync::Arc; pub mod async_support; From c7ab16396517caba8b6cdfdaf876a9138082795d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 2 Jan 2025 23:12:41 +0100 Subject: [PATCH 424/672] read implementation --- .../tests/symmetric_stream/main/src/main.rs | 7 +- .../stream/src/stream_world.rs | 140 ++++++++++++------ .../symmetric_executor/rust-client/Cargo.lock | 11 -- .../rust-client/src/async_support.rs | 86 +++++++++-- .../symmetric_executor/rust-client/src/lib.rs | 1 - .../rust-client/src/module.rs | 22 +-- 6 files changed, 183 insertions(+), 84 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index 399d8dfd1..c784be406 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -2,10 +2,7 @@ use std::pin::pin; -use wit_bindgen_symmetric_rt::{ - async_support::{self, Stream}, - CallbackState, -}; +use wit_bindgen_symmetric_rt::{async_support::Stream, CallbackState}; #[link(name = "stream")] extern "C" { @@ -15,7 +12,7 @@ extern "C" { ) -> *mut (); } -extern "C" fn ready(arg: *mut ()) -> CallbackState { +extern "C" fn ready(_arg: *mut ()) -> CallbackState { todo!() } diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 59ab1d3d4..ee1a54cd3 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -11,6 +11,12 @@ pub mod test { static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + // use _rt::stream_and_future_support::StreamHandle2; + use wit_bindgen_symmetric_rt::{ + async_support::{AddressSend, StreamHandle2, StreamHandleRust}, + // subscribe_event_send_ptr, + }; + use super::super::super::_rt; use _rt::stream_and_future_support::WitStream; @@ -27,7 +33,7 @@ pub mod test { } } - async fn write(stream: *mut WitStream, values: &[Self]) -> Option { + async fn write(_stream: &StreamHandleRust, _values: &[Self]) -> Option { { // let address = values.as_ptr() as _; @@ -45,31 +51,51 @@ pub mod test { } async fn read( - stream: *mut WitStream, + stream: &StreamHandleRust, values: &mut [::core::mem::MaybeUninit], ) -> Option { { - // let address = values.as_mut_ptr() as _; // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] // extern "C" { // #[link_name = "[async][stream-read-0]create"] // fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; // } - - // let count = unsafe { - // ::wit_bindgen_symmetric_rt::async_support::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await - // }; - // #[allow(unused)] - // if let Some(count) = count { - // let value = (); - + let poll_fn = unsafe { &*((&*(stream.handle.0)).vtable) }.read; + // match &stream.event { + // SubscriptionType::None => { + // let subscr = unsafe { + // subscribe_event_send_ptr( + // (&mut *stream.handle.0).read_ready_event_send, + // ) + // }; + // stream.event = SubscriptionType::Read(subscr) + // } + // SubscriptionType::Read(_event_subscription) => (), + // SubscriptionType::Write(_event_subscription) => unreachable!(), // } - // count - todo!() + // let SubscriptionType::Read(subscr) = &stream.event else { + // unreachable!() + // }; + let address = AddressSend(values.as_mut_ptr() as _); + let count = unsafe { + ::wit_bindgen_symmetric_rt::async_support::await_stream_result( + poll_fn, + StreamHandle2(stream.handle.0), + address, + values.len(), + &stream.event, + ) + .await + }; + #[allow(unused)] + if let Some(count) = count { + let value = (); + } + count } } - fn cancel_write(writer: *mut WitStream) { + fn cancel_write(_writer: *mut WitStream) { { // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] // extern "C" { @@ -81,7 +107,7 @@ pub mod test { } } - fn cancel_read(reader: *mut WitStream) { + fn cancel_read(_reader: *mut WitStream) { { // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] // extern "C" { @@ -93,7 +119,7 @@ pub mod test { } } - fn close_writable(writer: *mut WitStream) { + fn close_writable(_writer: *mut WitStream) { { // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] // extern "C" { @@ -105,7 +131,7 @@ pub mod test { } } - fn close_readable(reader: *mut WitStream) { + fn close_readable(_reader: *mut WitStream) { { // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] // extern "C" { @@ -232,15 +258,18 @@ mod _rt { #![allow(dead_code, clippy::all)] pub mod stream_and_future_support { pub use wit_bindgen_symmetric_rt::async_support::Stream as WitStream; + pub use wit_bindgen_symmetric_rt::async_support::StreamHandle2; + use wit_bindgen_symmetric_rt::async_support::StreamHandleRust; + use wit_bindgen_symmetric_rt::EventSubscription; use { futures::{ - channel::oneshot, - future::{self, FutureExt}, + // channel::oneshot, + future::FutureExt, sink::Sink, stream::Stream, }, std::{ - collections::hash_map::Entry, + // collections::hash_map::Entry, convert::Infallible, fmt, future::{Future, IntoFuture}, @@ -250,7 +279,7 @@ mod _rt { pin::Pin, task::{Context, Poll}, }, - wit_bindgen_symmetric_rt::async_support::{self, Handle}, + // wit_bindgen_symmetric_rt::async_support::{self, Handle}, }; #[doc(hidden)] @@ -337,7 +366,7 @@ mod _rt { impl FutureWriter { /// Write the specified value to this `future`. - pub fn write(self, v: T) -> CancelableWrite { + pub fn write(self, _v: T) -> CancelableWrite { // let handle = self.handle; // CancelableWrite { // writer: Some(self), @@ -475,7 +504,7 @@ mod _rt { impl FutureReader { #[doc(hidden)] - pub fn from_handle(handle: u32) -> Self { + pub fn from_handle(_handle: u32) -> Self { // async_support::with_entry(handle, |entry| match entry { // Entry::Vacant(entry) => { // entry.insert(Handle::Read); @@ -586,11 +615,14 @@ mod _rt { #[doc(hidden)] pub trait StreamPayload: Unpin + Sized + 'static { fn new() -> *mut WitStream; - async fn write(stream: *mut WitStream, values: &[Self]) -> Option; - async fn read( - stream: *mut WitStream, + fn write( + stream: &StreamHandleRust, + values: &[Self], + ) -> impl std::future::Future> + Send; + fn read( + stream: &StreamHandleRust, values: &mut [MaybeUninit], - ) -> Option; + ) -> impl std::future::Future> + Send; fn cancel_write(stream: *mut WitStream); fn cancel_read(stream: *mut WitStream); fn close_writable(stream: *mut WitStream); @@ -623,9 +655,6 @@ mod _rt { } } - pub struct StreamHandle2(*mut WitStream); - unsafe impl Send for StreamHandle2 {} - /// Represents the writable end of a Component Model `stream`. pub struct StreamWriter { handle: StreamHandle2, @@ -670,7 +699,7 @@ mod _rt { } } - fn start_send(self: Pin<&mut Self>, item: Vec) -> Result<(), Self::Error> { + fn start_send(self: Pin<&mut Self>, _item: Vec) -> Result<(), Self::Error> { // assert!(self.future.is_none()); // async_support::with_entry(self.handle, |entry| match entry { // Entry::Vacant(_) => unreachable!(), @@ -803,6 +832,7 @@ mod _rt { pub struct StreamReader { handle: StreamHandle2, future: Option>> + 'static + Send>>>, + event: Option, _phantom: PhantomData, } @@ -848,6 +878,7 @@ mod _rt { Self { handle: StreamHandle2(handle), future: None, + event: None, _phantom: PhantomData, } } @@ -873,13 +904,38 @@ mod _rt { } } - impl Stream for StreamReader { + impl Stream for StreamReader { type Item = Vec; fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - // let me = self.get_mut(); + let me = self.get_mut(); - // if me.future.is_none() { + if me.future.is_none() { + // assumption: future doesn't outlive stream + let handle = StreamHandle2(me.handle.0); + let event = unsafe { + EventSubscription::from_handle(me.event.as_ref().map_or(0, |e| e.handle())) + }; + me.future = Some(Box::pin(async move { + let mut buffer = iter::repeat_with(MaybeUninit::uninit) + .take(ceiling(4 * 1024, mem::size_of::())) + .collect::>(); + let stream_handle = StreamHandleRust { handle, event }; + let result = if let Some(count) = T::read(&stream_handle, &mut buffer).await + { + buffer.truncate(count); + Some(unsafe { mem::transmute::>, Vec>(buffer) }) + } else { + None + }; + let _ = stream_handle.event.take_handle(); + // cancel_on_drop.handle = None; + // drop(cancel_on_drop); + result + // T::read(me.handle.0, &mut buffer) + }) + as Pin + Send>>); + } // me.future = Some(async_support::with_entry(me.handle, |entry| match entry { // Entry::Vacant(_) => unreachable!(), // Entry::Occupied(mut entry) => match entry.get() { @@ -934,14 +990,13 @@ mod _rt { // })); // } - // match me.future.as_mut().unwrap().as_mut().poll(cx) { - // Poll::Ready(v) => { - // me.future = None; - // Poll::Ready(v) - // } - // Poll::Pending => Poll::Pending, - // } - todo!() + match me.future.as_mut().unwrap().as_mut().poll(cx) { + Poll::Ready(v) => { + me.future = None; + Poll::Ready(v) + } + Poll::Pending => Poll::Pending, + } } } @@ -1006,6 +1061,7 @@ mod _rt { StreamReader { handle: StreamHandle2(handle), future: None, + event: None, _phantom: PhantomData, }, ) diff --git a/crates/symmetric_executor/rust-client/Cargo.lock b/crates/symmetric_executor/rust-client/Cargo.lock index 662047506..4bd09ef9d 100644 --- a/crates/symmetric_executor/rust-client/Cargo.lock +++ b/crates/symmetric_executor/rust-client/Cargo.lock @@ -366,16 +366,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "symmetric_executor" -version = "0.1.0" -dependencies = [ - "futures", - "libc", - "wit-bindgen", - "wit-bindgen-rt", -] - [[package]] name = "syn" version = "2.0.90" @@ -556,7 +546,6 @@ name = "wit-bindgen-symmetric-rt" version = "0.36.0" dependencies = [ "futures", - "symmetric_executor", "wit-bindgen", ] diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 58dd8b6e7..23eaafc11 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -3,10 +3,7 @@ use std::{ any::Any, future::Future, pin::Pin, - sync::{ - atomic::{AtomicPtr, AtomicUsize, Ordering}, - Mutex, - }, + sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}, task::{Context, Poll, RawWaker, RawWakerVTable}, }; @@ -45,11 +42,11 @@ pub enum Handle { pub struct StreamVtable { // magic value for EOF(-1) and block(MIN) // asynchronous function, if this blocks wait for read ready event - pub read: fn(stream: *mut Stream, buf: *mut (), size: usize) -> isize, - pub close_read: fn(stream: *mut Stream), + pub read: extern "C" fn(stream: *mut Stream, buf: *mut (), size: usize) -> isize, + pub close_read: extern "C" fn(stream: *mut Stream), - pub write: fn(stream: *mut Stream, buf: *mut (), size: usize) -> isize, - pub close_write: fn(stream: *mut Stream), + pub write: extern "C" fn(stream: *mut Stream, buf: *mut (), size: usize) -> isize, + pub close_write: extern "C" fn(stream: *mut Stream), // post WASI 0.3, CPB // pub allocate: fn(stream: *mut ()) -> (*mut (), isize), // pub publish: fn(stream: *mut (), size: usize), @@ -62,9 +59,10 @@ pub struct Stream { pub write_ready_event_send: *mut (), pub read_addr: AtomicPtr<()>, pub read_size: AtomicUsize, + pub ready_size: AtomicIsize, } -fn read_impl(stream: *mut Stream, buf: *mut (), size: usize) -> isize { +extern "C" fn read_impl(stream: *mut Stream, buf: *mut (), size: usize) -> isize { let old_size = unsafe { &mut *stream } .read_size .swap(size, Ordering::Acquire); @@ -81,15 +79,15 @@ fn read_impl(stream: *mut Stream, buf: *mut (), size: usize) -> isize { isize::MIN } -fn write_impl(_stream: *mut Stream, _buf: *mut (), _size: usize) -> isize { +extern "C" fn write_impl(_stream: *mut Stream, _buf: *mut (), _size: usize) -> isize { todo!() } -fn read_close_impl(stream: *mut Stream) { +extern "C" fn read_close_impl(_stream: *mut Stream) { todo!() } -fn write_close_impl(stream: *mut Stream) { +extern "C" fn write_close_impl(_stream: *mut Stream) { todo!() } @@ -108,6 +106,7 @@ impl Stream { write_ready_event_send: EventGenerator::new().take_handle() as *mut (), read_addr: AtomicPtr::new(core::ptr::null_mut()), read_size: AtomicUsize::new(0), + ready_size: AtomicIsize::new(0), } } } @@ -237,8 +236,67 @@ pub unsafe fn callback(_ctx: *mut u8, _event0: i32, _event1: i32, _event2: i32) todo!() } -static TASKS: Mutex + 'static + Send>>> = Mutex::new(Vec::new()); +// static TASKS: Mutex + 'static + Send>>> = Mutex::new(Vec::new()); pub fn spawn(future: impl Future + 'static + Send) { - TASKS.lock().unwrap().push(Box::new(future)); + let _ = first_poll(future, |()| ()); + // TASKS.lock().unwrap().push(Box::new(future)); +} + +mod results { + pub const BLOCKED: isize = -1; + pub const CLOSED: isize = isize::MIN; + pub const CANCELED: isize = 0; +} + +// Stream handles are Send, so wrap them +#[repr(transparent)] +pub struct StreamHandle2(pub *mut Stream); +unsafe impl Send for StreamHandle2 {} +unsafe impl Sync for StreamHandle2 {} + +#[repr(transparent)] +pub struct AddressSend(pub *mut ()); +unsafe impl Send for AddressSend {} +// unsafe impl Sync for StreamHandle2 {} + +// pub enum SubscriptionType { +// None, +// Read(EventSubscription), +// Write(EventSubscription), +// } + +pub struct StreamHandleRust { + pub handle: StreamHandle2, + pub event: EventSubscription, +} + +pub async unsafe fn await_stream_result( + import: unsafe extern "C" fn(*mut Stream, *mut (), usize) -> isize, + stream: StreamHandle2, + address: AddressSend, + count: usize, + event: &EventSubscription, +) -> Option { + let stream_copy = stream.0; + let result = import(stream_copy, address.0, count); + match result { + results::BLOCKED => { + // assert!(!CURRENT.is_null()); + // (*CURRENT).todo += 1; + // let (tx, rx) = oneshot::channel(); + // CALLS.insert(stream as _, tx); + wait_on(event).await; + let v = unsafe { &mut *stream.0 } + .ready_size + .swap(0, Ordering::SeqCst); + if let results::CLOSED | results::CANCELED = v { + None + } else { + Some(usize::try_from(v).unwrap()) + } + } + results::CLOSED | results::CANCELED => None, + v => Some(usize::try_from(v).unwrap()), + } } diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index dfd9d6645..9a93dd8c0 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -2,7 +2,6 @@ use module::symmetric::runtime::symmetric_executor::{self, CallbackData, Callbac pub use module::symmetric::runtime::symmetric_executor::{ run, CallbackState, EventGenerator, EventSubscription, }; -use std::sync::Arc; pub mod async_support; mod module; diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index f51256d37..bdfd3d31d 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -550,26 +550,26 @@ mod _rt { } } pub mod stream_and_future_support { - use crate as wit_bindgen_symmetric_rt; + // use crate as wit_bindgen_symmetric_rt; use { futures::{ - channel::oneshot, - future::{self, FutureExt}, + // channel::oneshot, + // future::{self, FutureExt}, sink::Sink, stream::Stream, }, std::{ - collections::hash_map::Entry, + // collections::hash_map::Entry, convert::Infallible, fmt, - future::{Future, IntoFuture}, - iter, + future::Future, + // iter, marker::PhantomData, - mem::{self, ManuallyDrop, MaybeUninit}, + mem::MaybeUninit, pin::Pin, task::{Context, Poll}, }, - wit_bindgen_symmetric_rt::async_support::{self, Handle}, + // wit_bindgen_symmetric_rt::async_support::{self, Handle}, }; // #[doc(hidden)] @@ -986,7 +986,7 @@ mod _rt { } } - fn start_send(self: Pin<&mut Self>, item: Vec) -> Result<(), Self::Error> { + fn start_send(self: Pin<&mut Self>, _item: Vec) -> Result<(), Self::Error> { // assert!(self.future.is_none()); // async_support::with_entry(self.handle, |entry| match entry { // Entry::Vacant(_) => unreachable!(), @@ -1143,7 +1143,7 @@ mod _rt { impl StreamReader { #[doc(hidden)] - pub fn from_handle(handle: u32) -> Self { + pub fn from_handle(_handle: u32) -> Self { todo!() // async_support::with_entry(handle, |entry| match entry { // Entry::Vacant(entry) => { @@ -1195,7 +1195,7 @@ mod _rt { impl Stream for StreamReader { type Item = Vec; - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + fn poll_next(self: Pin<&mut Self>, _cx: &mut Context) -> Poll> { // let me = self.get_mut(); // if me.future.is_none() { From 431b90ff4cb7badac9db9d34c129e68e22020f42 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 2 Jan 2025 23:24:24 +0100 Subject: [PATCH 425/672] revise event design --- .../stream/src/stream_world.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index ee1a54cd3..bf4c38ae3 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -260,6 +260,7 @@ mod _rt { pub use wit_bindgen_symmetric_rt::async_support::Stream as WitStream; pub use wit_bindgen_symmetric_rt::async_support::StreamHandle2; use wit_bindgen_symmetric_rt::async_support::StreamHandleRust; + use wit_bindgen_symmetric_rt::subscribe_event_send_ptr; use wit_bindgen_symmetric_rt::EventSubscription; use { futures::{ @@ -832,7 +833,7 @@ mod _rt { pub struct StreamReader { handle: StreamHandle2, future: Option>> + 'static + Send>>>, - event: Option, + event: EventSubscription, _phantom: PhantomData, } @@ -875,10 +876,12 @@ mod _rt { // }, // }); + let subscr = + unsafe { subscribe_event_send_ptr((&mut *handle).read_ready_event_send) }; Self { handle: StreamHandle2(handle), future: None, - event: None, + event: subscr, _phantom: PhantomData, } } @@ -913,9 +916,8 @@ mod _rt { if me.future.is_none() { // assumption: future doesn't outlive stream let handle = StreamHandle2(me.handle.0); - let event = unsafe { - EventSubscription::from_handle(me.event.as_ref().map_or(0, |e| e.handle())) - }; + // duplicate handle (same assumption) + let event = unsafe { EventSubscription::from_handle(me.event.handle()) }; me.future = Some(Box::pin(async move { let mut buffer = iter::repeat_with(MaybeUninit::uninit) .take(ceiling(4 * 1024, mem::size_of::())) @@ -1058,12 +1060,7 @@ mod _rt { future: None, _phantom: PhantomData, }, - StreamReader { - handle: StreamHandle2(handle), - future: None, - event: None, - _phantom: PhantomData, - }, + StreamReader::from_handle(handle), ) } From 265f4ede6572fc7336fadf5a8f1f84c5429c7a78 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 2 Jan 2025 23:38:19 +0100 Subject: [PATCH 426/672] proper blocking --- crates/symmetric_executor/rust-client/src/async_support.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 23eaafc11..31bf44f8d 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -76,7 +76,7 @@ extern "C" fn read_impl(stream: *mut Stream, buf: *mut (), size: usize) -> isize gen.activate(); // don't consume let _ = gen.take_handle(); - isize::MIN + results::BLOCKED } extern "C" fn write_impl(_stream: *mut Stream, _buf: *mut (), _size: usize) -> isize { From 72af9d1b7e9abd30b8904f3c91ec90defef6994d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 3 Jan 2025 13:14:23 +0100 Subject: [PATCH 427/672] add poor man's tracing --- crates/symmetric_executor/src/lib.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index fc457e5b8..590936666 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -9,6 +9,8 @@ use executor::exports::symmetric::runtime::symmetric_executor::{ self, CallbackState, GuestEventSubscription, }; +const DEBUGGING: bool = true; + mod executor; struct Guest; @@ -122,6 +124,9 @@ impl symmetric_executor::Guest for Guest { let mut ex = EXECUTOR.lock().unwrap(); ex.active_tasks.iter_mut().for_each(|task| { if task.ready() { + if DEBUGGING { + println!("task ready {} {}", 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) }); @@ -166,7 +171,10 @@ impl symmetric_executor::Guest for Guest { } // with no work left the break should have occured assert!(!tvptr.is_null() || maxfd > 0); - let selectresult = unsafe { + if DEBUGGING { + println!("select({maxfd}, {:x}, {}.{})", unsafe {*rfd_ptr.cast::()}, wait.tv_sec, wait.tv_usec); + } + let selectresult = unsafe { libc::select( maxfd, rfd_ptr, @@ -206,6 +214,9 @@ impl symmetric_executor::Guest for Guest { ) -> () { // TODO: Tidy this mess up // Note: Trigger is consumed, callback and data are managed elsewhere + if DEBUGGING { + println!("register({:x}, {:x},{:x})", trigger.handle(), callback.handle(), data.handle()); + } let mut subscr = EventSubscription { inner: EventType::SystemTime(std::time::UNIX_EPOCH), callback: None, From f0c3968ba354d9574cfcf590d92dc28d9aaf7375 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 3 Jan 2025 15:04:10 +0100 Subject: [PATCH 428/672] more meaningful tracing --- .../async_module/src/async_module.rs | 3 +- .../tests/symmetric_stream/source/src/lib.rs | 13 ++++- crates/symmetric_executor/src/lib.rs | 57 +++++++++++++++++-- 3 files changed, 64 insertions(+), 9 deletions(-) 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 index c331bab94..5d0cd4cc3 100644 --- a/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs +++ b/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs @@ -185,6 +185,7 @@ mod _rt { } pub use alloc_crate::string::String; // pub use alloc_crate::vec::Vec; + #[cfg(target_arch = "never")] pub mod stream_and_future_support { use { futures::{ @@ -974,7 +975,7 @@ mod _rt { extern crate alloc as alloc_crate; } #[allow(unused_imports)] -pub use _rt::stream_and_future_support; +// pub use _rt::stream_and_future_support; /// Generates `#[no_mangle]` functions to export the specified type as the /// root implementation of all generated traits. diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index 7e252f2ed..c577292c9 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -1,4 +1,10 @@ -use wit_bindgen_symmetric_rt::async_support::Stream; +use wit_bindgen_symmetric_rt::{ + async_support::Stream, register, subscribe_event_send_ptr, CallbackState, +}; + +extern "C" fn read_ready(data: *mut ()) -> CallbackState { + CallbackState::Pending +} #[allow(non_snake_case)] #[no_mangle] @@ -7,6 +13,9 @@ pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate( results: *mut u8, ) -> *mut u8 { let obj = Box::new(Stream::new()); - *unsafe { &mut *results.cast::<*mut Stream>() } = Box::into_raw(obj); + let event = unsafe { subscribe_event_send_ptr(obj.read_ready_event_send) }; + let addr = Box::into_raw(obj); + register(event, read_ready, addr.cast()); + *unsafe { &mut *results.cast::<*mut Stream>() } = addr; std::ptr::null_mut() } diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 590936666..46e59ec99 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -60,6 +60,12 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { fn subscribe(&self) -> symmetric_executor::EventSubscription { let event_fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }; + if DEBUGGING { + println!( + "subscribe({:x}) fd={event_fd}", + Arc::as_ptr(&self.0) as usize + ); + } self.0.lock().unwrap().waiting.push(event_fd); symmetric_executor::EventSubscription::new(EventSubscription { inner: EventType::Triggered { @@ -72,6 +78,9 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { } fn activate(&self) { + if DEBUGGING { + println!("activate({:x})", Arc::as_ptr(&self.0) as usize); + } if let Ok(mut event) = self.0.lock() { event.counter += 1; let file_signal: u64 = 1; @@ -125,7 +134,11 @@ impl symmetric_executor::Guest for Guest { ex.active_tasks.iter_mut().for_each(|task| { if task.ready() { if DEBUGGING { - println!("task ready {} {}", task.callback.as_ref().unwrap().0 as usize, task.callback.as_ref().unwrap().1 as usize); + 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) @@ -172,9 +185,20 @@ impl symmetric_executor::Guest for Guest { // with no work left the break should have occured assert!(!tvptr.is_null() || maxfd > 0); if DEBUGGING { - println!("select({maxfd}, {:x}, {}.{})", unsafe {*rfd_ptr.cast::()}, wait.tv_sec, wait.tv_usec); + if tvptr.is_null() { + println!("select({maxfd}, {:x}, null)", unsafe { + *rfd_ptr.cast::() + },); + } else { + println!( + "select({maxfd}, {:x}, {}.{})", + unsafe { *rfd_ptr.cast::() }, + wait.tv_sec, + wait.tv_usec + ); + } } - let selectresult = unsafe { + let selectresult = unsafe { libc::select( maxfd, rfd_ptr, @@ -214,9 +238,6 @@ impl symmetric_executor::Guest for Guest { ) -> () { // TODO: Tidy this mess up // Note: Trigger is consumed, callback and data are managed elsewhere - if DEBUGGING { - println!("register({:x}, {:x},{:x})", trigger.handle(), callback.handle(), data.handle()); - } let mut subscr = EventSubscription { inner: EventType::SystemTime(std::time::UNIX_EPOCH), callback: None, @@ -225,6 +246,30 @@ impl symmetric_executor::Guest for Guest { // TODO: This should be handle to free the resource, but it is used later? &mut *(trigger.take_handle() as *mut EventSubscription) }); + if DEBUGGING { + match &subscr.inner { + EventType::Triggered { + last_counter: _, + event_fd, + event, + } => println!( + "register(Triggered {:x} fd {event_fd}, {:x},{:x})", + Arc::as_ptr(event) as usize, + callback.handle(), + data.handle() + ), + EventType::SystemTime(system_time) => { + let diff = system_time.duration_since(SystemTime::now()).unwrap(); + println!( + "register(Time {}.{}, {:x},{:x})", + diff.as_secs(), + diff.subsec_nanos(), + callback.handle(), + data.handle() + ); + } + } + } let cb: fn(*mut ()) -> CallbackState = unsafe { transmute(callback.take_handle()) }; let data = data.take_handle() as *mut (); subscr.callback.replace(CallbackEntry(cb, data)); From d5ca598b897ed69f20d0ff1ad417bcd495a6b565 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 3 Jan 2025 16:16:59 +0100 Subject: [PATCH 429/672] sending numbers work --- .../tests/symmetric_stream/source/src/lib.rs | 64 +++++++++++++++++-- .../symmetric_executor/rust-client/src/lib.rs | 7 ++ crates/symmetric_executor/src/lib.rs | 32 +++++++++- 3 files changed, 96 insertions(+), 7 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index c577292c9..0fb60fde8 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -1,9 +1,63 @@ +use std::sync::atomic::{AtomicU32, Ordering}; + use wit_bindgen_symmetric_rt::{ - async_support::Stream, register, subscribe_event_send_ptr, CallbackState, + activate_event_send_ptr, async_support::Stream, register, subscribe_event_send_ptr, + CallbackState, EventSubscription, }; -extern "C" fn read_ready(data: *mut ()) -> CallbackState { - CallbackState::Pending +#[link(name = "symmetric_executor")] +extern "C" { + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout( + nanoseconds: u64, + ) -> *mut (); +} + +static COUNT: AtomicU32 = AtomicU32::new(1); + +extern "C" fn timer_call(data: *mut ()) -> CallbackState { + let count = COUNT.fetch_add(1, Ordering::SeqCst); + let stream: *const Stream = data.cast(); + if count <= 5 { + let size = unsafe { &*stream }.read_size.swap(0, Ordering::Acquire); + let addr = unsafe { &*stream } + .read_addr + .swap(core::ptr::null_mut(), Ordering::Relaxed); + assert!(size >= size_of::()); + *unsafe { &mut *addr.cast::() } = count; + let old_ready = unsafe { &*stream } + .ready_size + .swap(size_of::() as isize, Ordering::Release); + assert_eq!(old_ready, 0); + let read_ready_evt = unsafe { &*stream }.read_ready_event_send; + unsafe { activate_event_send_ptr(read_ready_evt) }; + let ms_30 = unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(30*1_000_000) + } as usize; + assert_ne!(ms_30, 0); + let event = unsafe { EventSubscription::from_handle(ms_30) }; + register(event, timer_call, data); + } else { + // EOF + let old_ready = unsafe { &*stream } + .ready_size + .swap(isize::MIN, Ordering::Release); + assert_eq!(old_ready, 0); + let read_ready_evt = unsafe { &*stream }.read_ready_event_send; + unsafe { activate_event_send_ptr(read_ready_evt) }; + } + CallbackState::Ready +} + +extern "C" fn write_ready(data: *mut ()) -> CallbackState { + println!("we can write now, starting timer"); + let ms_30 = unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(30*1_000_000) + } as usize; + assert_ne!(ms_30, 0); + let event = unsafe { EventSubscription::from_handle(ms_30) }; + register(event, timer_call, data); + // this callback is done + CallbackState::Ready } #[allow(non_snake_case)] @@ -13,9 +67,9 @@ pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate( results: *mut u8, ) -> *mut u8 { let obj = Box::new(Stream::new()); - let event = unsafe { subscribe_event_send_ptr(obj.read_ready_event_send) }; + let event = unsafe { subscribe_event_send_ptr(obj.write_ready_event_send) }; let addr = Box::into_raw(obj); - register(event, read_ready, addr.cast()); + register(event, write_ready, addr.cast()); *unsafe { &mut *results.cast::<*mut Stream>() } = addr; std::ptr::null_mut() } diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index 9a93dd8c0..134b4da51 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -34,3 +34,10 @@ pub unsafe fn subscribe_event_send_ptr(event_send: *mut ()) -> EventSubscription 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/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 46e59ec99..9d2bf6786 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -1,7 +1,10 @@ use std::{ ffi::c_int, mem::transmute, - sync::{atomic::AtomicU32, Arc, Mutex}, + sync::{ + atomic::{AtomicBool, AtomicU32}, + Arc, Mutex, + }, time::{Duration, SystemTime}, }; @@ -110,6 +113,9 @@ struct Executor { static EXECUTOR: Mutex = Mutex::new(Executor { active_tasks: Vec::new(), }); +// while executing tasks from the loop we can't directly queue new ones +static EXECUTOR_BUSY: AtomicBool = AtomicBool::new(false); +static NEW_TASKS: Mutex> = Mutex::new(Vec::new()); impl symmetric_executor::Guest for Guest { type CallbackFunction = Ignore; @@ -131,6 +137,8 @@ impl symmetric_executor::Guest for Guest { unsafe { libc::FD_ZERO(rfd_ptr) }; { let mut ex = EXECUTOR.lock().unwrap(); + let old_busy = EXECUTOR_BUSY.swap(true, std::sync::atomic::Ordering::SeqCst); + assert!(!old_busy); ex.active_tasks.iter_mut().for_each(|task| { if task.ready() { if DEBUGGING { @@ -177,7 +185,17 @@ impl symmetric_executor::Guest for Guest { } } }); + let old_busy = EXECUTOR_BUSY.swap(false, std::sync::atomic::Ordering::SeqCst); + 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; } @@ -273,7 +291,17 @@ impl symmetric_executor::Guest for Guest { let cb: fn(*mut ()) -> CallbackState = unsafe { transmute(callback.take_handle()) }; let data = data.take_handle() as *mut (); subscr.callback.replace(CallbackEntry(cb, data)); - EXECUTOR.lock().unwrap().active_tasks.push(subscr); + match EXECUTOR.try_lock() { + Ok(mut lock) => lock.active_tasks.push(subscr), + Err(_err) => { + if EXECUTOR_BUSY.load(std::sync::atomic::Ordering::Relaxed) { + NEW_TASKS.lock().unwrap().push(subscr); + } else { + // actually this is unlikely, but give it a try + EXECUTOR.lock().unwrap().active_tasks.push(subscr); + } + } + } } } From 406495d84e4bb4672d448ebc856397560fa75e1d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 3 Jan 2025 23:49:20 +0100 Subject: [PATCH 430/672] event subscriptions can only be used once for registration ... --- .../tests/symmetric_stream/main/src/main.rs | 9 +-- .../tests/symmetric_stream/source/src/lib.rs | 6 +- .../stream/src/stream_world.rs | 74 ++++++++++++++++--- .../rust-client/src/async_support.rs | 2 +- 4 files changed, 69 insertions(+), 22 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index c784be406..34ad86b2c 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -26,16 +26,13 @@ fn main() { }; assert!(handle.is_null()); let handle = result_stream.cast::(); - let mut target = pin!(0_u32); + let mut target = Box::pin([0_u32, 0]); unsafe { - ((&*(&*handle).vtable).read)( - handle, - ((&mut *target) as *mut u32).cast(), - size_of::(), - ); + ((&*(&*handle).vtable).read)(handle, target.as_mut_ptr().cast(), 2); }; let read_ready = unsafe { (&*handle).read_ready_event_send }; let subscription = unsafe { wit_bindgen_symmetric_rt::subscribe_event_send_ptr(read_ready) }; + println!("Register read in main"); wit_bindgen_symmetric_rt::register(subscription, ready, ((&mut *target) as *mut u32).cast()); wit_bindgen_symmetric_rt::run(); } diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index 0fb60fde8..7d23d4290 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -22,11 +22,9 @@ extern "C" fn timer_call(data: *mut ()) -> CallbackState { let addr = unsafe { &*stream } .read_addr .swap(core::ptr::null_mut(), Ordering::Relaxed); - assert!(size >= size_of::()); + assert!(size >= 1); *unsafe { &mut *addr.cast::() } = count; - let old_ready = unsafe { &*stream } - .ready_size - .swap(size_of::() as isize, Ordering::Release); + let old_ready = unsafe { &*stream }.ready_size.swap(1, Ordering::Release); assert_eq!(old_ready, 0); let read_ready_evt = unsafe { &*stream }.read_ready_event_send; unsafe { activate_event_send_ptr(read_ready_evt) }; diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index bf4c38ae3..84161af87 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -147,8 +147,8 @@ pub mod test { #[allow(unused_unsafe, clippy::all)] pub async fn create() -> _rt::stream_and_future_support::StreamReader { unsafe { - let layout0 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); - let ptr0 = _rt::alloc::alloc(layout0); + // let layout0 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); + let ptr0 = core::ptr::null_mut(); //_rt::alloc::alloc(layout0); let layout1 = _rt::alloc::Layout::from_size_align_unchecked( core::mem::size_of::<*const u8>(), core::mem::size_of::<*const u8>(), @@ -257,6 +257,10 @@ pub mod exports { mod _rt { #![allow(dead_code, clippy::all)] pub mod stream_and_future_support { + use std::sync::atomic::Ordering; + + use wit_bindgen_symmetric_rt::activate_event_send_ptr; + use wit_bindgen_symmetric_rt::async_support::wait_on; pub use wit_bindgen_symmetric_rt::async_support::Stream as WitStream; pub use wit_bindgen_symmetric_rt::async_support::StreamHandle2; use wit_bindgen_symmetric_rt::async_support::StreamHandleRust; @@ -660,6 +664,7 @@ mod _rt { pub struct StreamWriter { handle: StreamHandle2, future: Option + 'static + Send>>>, + event: EventSubscription, _phantom: PhantomData, } @@ -671,6 +676,23 @@ mod _rt { assert!(self.future.is_some()); self.future = None; } + + #[doc(hidden)] + pub fn from_handle(handle: *mut WitStream) -> Self { + let subscr = + unsafe { subscribe_event_send_ptr((&mut *handle).write_ready_event_send) }; + let subscr_copy = unsafe { EventSubscription::from_handle(subscr.handle()) }; + let ready = Box::pin(async move { + wait_on(&subscr_copy).await; + let _ = subscr_copy.take_handle(); + }); + Self { + handle: StreamHandle2(handle), + future: Some(ready), + event: subscr, + _phantom: PhantomData, + } + } } impl fmt::Debug for StreamWriter { @@ -700,7 +722,28 @@ mod _rt { } } - fn start_send(self: Pin<&mut Self>, _item: Vec) -> Result<(), Self::Error> { + fn start_send(self: Pin<&mut Self>, mut item: Vec) -> Result<(), Self::Error> { + let item_len = item.len(); + let me = self.get_mut(); + let stream = me.handle.0; + let size = unsafe { &*stream }.read_size.swap(0, Ordering::Acquire); + let addr = unsafe { &*stream } + .read_addr + .swap(core::ptr::null_mut(), Ordering::Relaxed); + assert!(size >= item_len); + // let outptr = addr.cast::>(); + let slice = unsafe { + std::slice::from_raw_parts_mut(addr.cast::>(), item_len) + }; + for (a, b) in slice.iter_mut().zip(item.drain(..)) { + a.write(b); + } + let old_ready = unsafe { &*stream } + .ready_size + .swap(item_len as isize, Ordering::Release); + assert_eq!(old_ready, 0); + let read_ready_evt = unsafe { &*stream }.read_ready_event_send; + unsafe { activate_event_send_ptr(read_ready_evt) }; // assert!(self.future.is_none()); // async_support::with_entry(self.handle, |entry| match entry { // Entry::Vacant(_) => unreachable!(), @@ -770,7 +813,14 @@ mod _rt { // }, // }); // Ok(()) - todo!() + + // wait before next element is written + let subscr_copy = unsafe { EventSubscription::from_handle(me.event.handle()) }; + me.future.replace(Box::pin(async move { + wait_on(&subscr_copy).await; + let _ = subscr_copy.take_handle(); + })); + Ok(()) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { @@ -799,7 +849,10 @@ mod _rt { // } // }, // }); - todo!() + let stream = self.handle.0; + let read_ready_evt = unsafe { &*stream }.read_ready_event_send; + unsafe { activate_event_send_ptr(read_ready_evt) }; +// todo!() } } @@ -1025,7 +1078,10 @@ mod _rt { // Handle::Write => unreachable!(), // }, // }); - todo!() + //todo!() + let stream = self.handle.0; + let write_ready_evt = unsafe { &*stream }.write_ready_event_send; + unsafe { activate_event_send_ptr(write_ready_evt) }; } } @@ -1055,11 +1111,7 @@ mod _rt { pub fn new_stream() -> (StreamWriter, StreamReader) { let handle = Box::into_raw(Box::new(WitStream::new())); ( - StreamWriter { - handle: StreamHandle2(handle), - future: None, - _phantom: PhantomData, - }, + StreamWriter::from_handle(handle), StreamReader::from_handle(handle), ) } diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 31bf44f8d..12a188990 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -151,7 +151,7 @@ unsafe fn poll(state: *mut FutureState) -> Poll<()> { }) } -async fn wait_on(wait_for: &EventSubscription) { +pub async fn wait_on(wait_for: &EventSubscription) { std::future::poll_fn(move |cx| { if wait_for.ready() { Poll::Ready(()) From 54746f184b6c636ce4b1546d9d6b72dad0663337 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 Jan 2025 12:10:01 +0100 Subject: [PATCH 431/672] the problem is with wait_on --- crates/cpp/tests/symmetric_stream/main/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index 34ad86b2c..095dec7e3 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -1,6 +1,6 @@ // use wit_bindgen_symmetric_rt::{CallbackState, EventSubscription}; -use std::pin::pin; +// use std::pin::pin; use wit_bindgen_symmetric_rt::{async_support::Stream, CallbackState}; From 60bcdeaff4b2085d3026c131420c1bf40246777c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 Jan 2025 13:03:34 +0100 Subject: [PATCH 432/672] wait on should consume the object --- crates/symmetric_executor/src/lib.rs | 58 +++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 9d2bf6786..ec10a4b69 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -2,7 +2,7 @@ use std::{ ffi::c_int, mem::transmute, sync::{ - atomic::{AtomicBool, AtomicU32}, + atomic::{AtomicBool, AtomicU32, Ordering}, Arc, Mutex, }, time::{Duration, SystemTime}, @@ -34,9 +34,9 @@ impl symmetric_executor::GuestEventSubscription for EventSubscription { } => { let current_counter = event.lock().unwrap().counter; let active = - current_counter != last_counter.load(std::sync::atomic::Ordering::Acquire); + current_counter != last_counter.load(Ordering::Acquire); if active { - last_counter.store(current_counter, std::sync::atomic::Ordering::Release); + last_counter.store(current_counter, Ordering::Release); } active } @@ -137,7 +137,7 @@ impl symmetric_executor::Guest for Guest { unsafe { libc::FD_ZERO(rfd_ptr) }; { let mut ex = EXECUTOR.lock().unwrap(); - let old_busy = EXECUTOR_BUSY.swap(true, std::sync::atomic::Ordering::SeqCst); + let old_busy = EXECUTOR_BUSY.swap(true, Ordering::SeqCst); assert!(!old_busy); ex.active_tasks.iter_mut().for_each(|task| { if task.ready() { @@ -185,7 +185,7 @@ impl symmetric_executor::Guest for Guest { } } }); - let old_busy = EXECUTOR_BUSY.swap(false, std::sync::atomic::Ordering::SeqCst); + let old_busy = EXECUTOR_BUSY.swap(false, Ordering::SeqCst); assert!(old_busy); ex.active_tasks.retain(|task| task.callback.is_some()); { @@ -294,7 +294,7 @@ impl symmetric_executor::Guest for Guest { match EXECUTOR.try_lock() { Ok(mut lock) => lock.active_tasks.push(subscr), Err(_err) => { - if EXECUTOR_BUSY.load(std::sync::atomic::Ordering::Relaxed) { + if EXECUTOR_BUSY.load(Ordering::Relaxed) { NEW_TASKS.lock().unwrap().push(subscr); } else { // actually this is unlikely, but give it a try @@ -324,6 +324,52 @@ struct EventSubscription { callback: Option, } +impl EventSubscription { + fn dup(&self) -> Self { + let inner = match &self.inner { + EventType::Triggered { last_counter: last_counter_old, event_fd, event } => { + let new_event_fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }; + let new_event = Arc::clone(event); + let last_counter = last_counter_old.load(Ordering::Relaxed); + if DEBUGGING { + println!( + "dup(subscr {last_counter} {event_fd} {:x}) fd={new_event_fd}", + Arc::as_ptr(&event) as usize + ); + } + new_event.lock().unwrap().waiting.push(new_event_fd); + EventType::Triggered { + last_counter: AtomicU32::new(last_counter), + event_fd: new_event_fd, + event: new_event, + } + } + EventType::SystemTime(system_time) => EventType::SystemTime(*system_time), + }; + EventSubscription { + inner, + callback: None, + } + } +} + +impl Drop for EventSubscription { + 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_fd, event } => { + if DEBUGGING { println!("drop(subscription fd {event_fd}"); } + event.lock().unwrap().waiting.retain(|e| e != event_fd); + unsafe {libc::close(*event_fd)}; } + EventType::SystemTime(_system_time) => (), + } + } +} + enum EventType { Triggered { last_counter: AtomicU32, From 2ef5e13dc834af847fdeceacb358e6033158a4c0 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 Jan 2025 16:18:48 +0100 Subject: [PATCH 433/672] preparation for more clean design --- .../stream/src/stream_world.rs | 2 +- crates/symmetric_executor/generate.sh | 0 .../rust-client/src/async_support.rs | 21 +++++++-------- crates/symmetric_executor/src/lib.rs | 27 ++++++++++++++----- crates/symmetric_executor/wit/executor.wit | 2 ++ 5 files changed, 33 insertions(+), 19 deletions(-) mode change 100644 => 100755 crates/symmetric_executor/generate.sh diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 84161af87..9e32def99 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -852,7 +852,7 @@ mod _rt { let stream = self.handle.0; let read_ready_evt = unsafe { &*stream }.read_ready_event_send; unsafe { activate_event_send_ptr(read_ready_evt) }; -// todo!() + // todo!() } } diff --git a/crates/symmetric_executor/generate.sh b/crates/symmetric_executor/generate.sh old mode 100644 new mode 100755 diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 12a188990..32705746f 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -151,21 +151,21 @@ unsafe fn poll(state: *mut FutureState) -> Poll<()> { }) } -pub async fn wait_on(wait_for: &EventSubscription) { +pub async fn wait_on(wait_for: EventSubscription) { std::future::poll_fn(move |cx| { if wait_for.ready() { Poll::Ready(()) } else { let data = cx.waker().data(); // dangerous duplication? - let wait_for_copy = unsafe { EventSubscription::from_handle(wait_for.handle()) }; - let old_waiting_for = - unsafe { &mut *(data.cast::>().cast_mut()) } - .replace(wait_for_copy); - // don't free the old subscription we found - if let Some(subscription) = old_waiting_for { - subscription.take_handle(); - } + // let wait_for_copy = unsafe { EventSubscription::from_handle(wait_for.handle()) }; + //let _old_waiting_for = + unsafe { &mut *(data.cast::>().cast_mut()) } + .replace(wait_for); + // ~~don't free the old subscription we found~~ + // if let Some(subscription) = old_waiting_for { + // subscription.take_handle(); + // } Poll::Pending } }) @@ -226,8 +226,7 @@ pub async unsafe fn await_result( let wait_for = function(params, results); if !wait_for.is_null() { let wait_for = unsafe { EventSubscription::from_handle(wait_for as usize) }; - wait_on(&wait_for).await; - let _ = wait_for.take_handle(); + wait_on(wait_for).await; } } diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index ec10a4b69..22740c101 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -33,8 +33,7 @@ impl symmetric_executor::GuestEventSubscription for EventSubscription { event, } => { let current_counter = event.lock().unwrap().counter; - let active = - current_counter != last_counter.load(Ordering::Acquire); + let active = current_counter != last_counter.load(Ordering::Acquire); if active { last_counter.store(current_counter, Ordering::Release); } @@ -327,7 +326,11 @@ struct EventSubscription { impl EventSubscription { fn dup(&self) -> Self { let inner = match &self.inner { - EventType::Triggered { last_counter: last_counter_old, event_fd, event } => { + EventType::Triggered { + last_counter: last_counter_old, + event_fd, + event, + } => { let new_event_fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }; let new_event = Arc::clone(event); let last_counter = last_counter_old.load(Ordering::Relaxed); @@ -357,14 +360,24 @@ impl Drop for EventSubscription { 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); + println!( + "drop() with active callback {:x},{:x}", + cb.0 as usize, cb.1 as usize + ); } } match &self.inner { - EventType::Triggered { last_counter:_, event_fd, event } => { - if DEBUGGING { println!("drop(subscription fd {event_fd}"); } + EventType::Triggered { + last_counter: _, + event_fd, + event, + } => { + if DEBUGGING { + println!("drop(subscription fd {event_fd}"); + } event.lock().unwrap().waiting.retain(|e| e != event_fd); - unsafe {libc::close(*event_fd)}; } + unsafe { libc::close(*event_fd) }; + } EventType::SystemTime(_system_time) => (), } } diff --git a/crates/symmetric_executor/wit/executor.wit b/crates/symmetric_executor/wit/executor.wit index 5eade9a20..db7665dc5 100644 --- a/crates/symmetric_executor/wit/executor.wit +++ b/crates/symmetric_executor/wit/executor.wit @@ -19,6 +19,8 @@ interface symmetric-executor { ready: func() -> bool; /// Create a timeout event from-timeout: static func(nanoseconds: u64) -> event-subscription; + /// Duplicate the subscription (e.g. for repeated callback registering) + dup: func() -> event-subscription; } /// A user controlled event resource event-generator { From 543ad8c36014484aaada653f6caf262b5d881c22 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 Jan 2025 19:28:14 +0100 Subject: [PATCH 434/672] memory leak fixed, cleaner code --- crates/symmetric_executor/generate.sh | 2 +- .../rust-client/src/async_support.rs | 10 +- .../rust-client/src/module.rs | 44 ++++-- crates/symmetric_executor/src/executor.rs | 44 ++++-- crates/symmetric_executor/src/lib.rs | 145 ++++++++++-------- 5 files changed, 155 insertions(+), 90 deletions(-) diff --git a/crates/symmetric_executor/generate.sh b/crates/symmetric_executor/generate.sh index 337f07115..5ca2a9855 100755 --- a/crates/symmetric_executor/generate.sh +++ b/crates/symmetric_executor/generate.sh @@ -1,3 +1,3 @@ #!/bin/sh (cd rust-client/src;../../../../target/debug/wit-bindgen rust ../../wit -w module --symmetric --async none) -(cd rsrc;../../../target/debug/wit-bindgen rust ../wit -w executor --symmetric --async none) +(cd src;../../../target/debug/wit-bindgen rust ../wit -w executor --symmetric --async none) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 32705746f..d41cfba0f 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -157,11 +157,15 @@ pub async fn wait_on(wait_for: EventSubscription) { Poll::Ready(()) } else { let data = cx.waker().data(); + let mut copy = Some(wait_for.dup()); // dangerous duplication? // let wait_for_copy = unsafe { EventSubscription::from_handle(wait_for.handle()) }; //let _old_waiting_for = - unsafe { &mut *(data.cast::>().cast_mut()) } - .replace(wait_for); + std::mem::swap( + unsafe { &mut *(data.cast::>().cast_mut()) }, + &mut copy, + ); + // .replace(wait_for); // ~~don't free the old subscription we found~~ // if let Some(subscription) = old_waiting_for { // subscription.take_handle(); @@ -285,7 +289,7 @@ pub async unsafe fn await_stream_result( // (*CURRENT).todo += 1; // let (tx, rx) = oneshot::channel(); // CALLS.insert(stream as _, tx); - wait_on(event).await; + wait_on(event.dup()).await; let v = unsafe { &mut *stream.0 } .ready_size .swap(0, Ordering::SeqCst); diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index bdfd3d31d..0164db22b 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -316,6 +316,26 @@ pub mod symmetric { } } } + impl EventSubscription { + #[allow(unused_unsafe, clippy::all)] + /// Duplicate the subscription (e.g. for repeated callback registering) + 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 EventGenerator { #[allow(unused_unsafe, clippy::all)] pub fn new() -> Self { @@ -1354,22 +1374,22 @@ pub use _rt::stream_and_future_support; #[link_section = "component-type:wit-bindgen:0.36.0:symmetric:runtime@0.1.0:module:encoded world"] #[doc(hidden)] #[allow(clippy::octal_escapes)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 695] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xba\x04\x01A\x02\x01\ -A\x02\x01B\x1c\x04\0\x11callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\ +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 741] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xe8\x04\x01A\x02\x01\ +A\x02\x01B\x1e\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\x01i\x03\x01@\0\0\x0c\x04\ -\0\x1c[constructor]event-generator\x01\x0d\x01h\x03\x01@\x01\x04self\x0e\0\x0a\x04\ -\0![method]event-generator.subscribe\x01\x0f\x01@\x01\x04self\x0e\x01\0\x04\0\x20\ -[method]event-generator.activate\x01\x10\x01@\0\x01\0\x04\0\x03run\x01\x11\x01i\0\ -\x01i\x01\x01@\x03\x07trigger\x0a\x08callback\x12\x04data\x13\x01\0\x04\0\x08reg\ -ister\x01\x14\x03\0*symmetric:runtime/symmetric-executor@0.1.0\x05\0\x04\0\x1esy\ -mmetric:runtime/module@0.1.0\x04\0\x0b\x0c\x01\0\x06module\x03\0\0\0G\x09produce\ -rs\x01\x0cprocessed-by\x02\x0dwit-component\x070.221.2\x10wit-bindgen-rust\x060.\ -36.0"; +\x04\0'[static]event-subscription.from-timeout\x01\x0b\x01@\x01\x04self\x08\0\x0a\ +\x04\0\x1e[method]event-subscription.dup\x01\x0c\x01i\x03\x01@\0\0\x0d\x04\0\x1c\ +[constructor]event-generator\x01\x0e\x01h\x03\x01@\x01\x04self\x0f\0\x0a\x04\0![\ +method]event-generator.subscribe\x01\x10\x01@\x01\x04self\x0f\x01\0\x04\0\x20[me\ +thod]event-generator.activate\x01\x11\x01@\0\x01\0\x04\0\x03run\x01\x12\x01i\0\x01\ +i\x01\x01@\x03\x07trigger\x0a\x08callback\x13\x04data\x14\x01\0\x04\0\x08registe\ +r\x01\x15\x03\0*symmetric:runtime/symmetric-executor@0.1.0\x05\0\x04\0\x1esymmet\ +ric:runtime/module@0.1.0\x04\0\x0b\x0c\x01\0\x06module\x03\0\0\0G\x09producers\x01\ +\x0cprocessed-by\x02\x0dwit-component\x070.221.2\x10wit-bindgen-rust\x060.36.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/symmetric_executor/src/executor.rs b/crates/symmetric_executor/src/executor.rs index 479375f31..5d73481b3 100644 --- a/crates/symmetric_executor/src/executor.rs +++ b/crates/symmetric_executor/src/executor.rs @@ -705,6 +705,18 @@ pub mod exports { } #[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_constructor_event_generator_cabi( ) -> *mut u8 { #[cfg(target_arch = "wasm32")] @@ -851,6 +863,8 @@ pub mod exports { fn ready(&self) -> bool; /// Create a timeout event fn from_timeout(nanoseconds: u64) -> EventSubscription; + /// Duplicate the subscription (e.g. for repeated callback registering) + fn dup(&self) -> EventSubscription; } #[doc(hidden)] #[allow(non_snake_case)] @@ -899,6 +913,11 @@ pub mod exports { 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 = "[constructor]event-generator")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BconstructorX5Devent_generator() -> *mut u8 { @@ -1878,22 +1897,23 @@ pub(crate) use __export_executor_impl as export; #[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; 699] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xbc\x04\x01A\x02\x01\ -A\x02\x01B\x1c\x04\0\x11callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\ +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 745] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xea\x04\x01A\x02\x01\ +A\x02\x01B\x1e\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\x01i\x03\x01@\0\0\x0c\x04\ -\0\x1c[constructor]event-generator\x01\x0d\x01h\x03\x01@\x01\x04self\x0e\0\x0a\x04\ -\0![method]event-generator.subscribe\x01\x0f\x01@\x01\x04self\x0e\x01\0\x04\0\x20\ -[method]event-generator.activate\x01\x10\x01@\0\x01\0\x04\0\x03run\x01\x11\x01i\0\ -\x01i\x01\x01@\x03\x07trigger\x0a\x08callback\x12\x04data\x13\x01\0\x04\0\x08reg\ -ister\x01\x14\x04\0*symmetric:runtime/symmetric-executor@0.1.0\x05\0\x04\0\x20sy\ -mmetric:runtime/executor@0.1.0\x04\0\x0b\x0e\x01\0\x08executor\x03\0\0\0G\x09pro\ -ducers\x01\x0cprocessed-by\x02\x0dwit-component\x070.221.2\x10wit-bindgen-rust\x06\ -0.36.0"; +\x04\0'[static]event-subscription.from-timeout\x01\x0b\x01@\x01\x04self\x08\0\x0a\ +\x04\0\x1e[method]event-subscription.dup\x01\x0c\x01i\x03\x01@\0\0\x0d\x04\0\x1c\ +[constructor]event-generator\x01\x0e\x01h\x03\x01@\x01\x04self\x0f\0\x0a\x04\0![\ +method]event-generator.subscribe\x01\x10\x01@\x01\x04self\x0f\x01\0\x04\0\x20[me\ +thod]event-generator.activate\x01\x11\x01@\0\x01\0\x04\0\x03run\x01\x12\x01i\0\x01\ +i\x01\x01@\x03\x07trigger\x0a\x08callback\x13\x04data\x14\x01\0\x04\0\x08registe\ +r\x01\x15\x04\0*symmetric:runtime/symmetric-executor@0.1.0\x05\0\x04\0\x20symmet\ +ric:runtime/executor@0.1.0\x04\0\x0b\x0e\x01\0\x08executor\x03\0\0\0G\x09produce\ +rs\x01\x0cprocessed-by\x02\x0dwit-component\x070.221.2\x10wit-bindgen-rust\x060.\ +36.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 22740c101..3a8cff9cf 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -9,10 +9,13 @@ use std::{ }; use executor::exports::symmetric::runtime::symmetric_executor::{ - self, CallbackState, GuestEventSubscription, + self, + CallbackState, + // GuestEventSubscription, }; const DEBUGGING: bool = true; +const INVALID_FD: EventFd = -1; mod executor; @@ -26,30 +29,20 @@ impl symmetric_executor::GuestCallbackData for Ignore {} impl symmetric_executor::GuestEventSubscription for EventSubscription { fn ready(&self) -> bool { - match &self.inner { - EventType::Triggered { - last_counter, - event_fd: _, - 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(), - } + 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), - callback: None, + // callback: None, }) } + + fn dup(&self) -> symmetric_executor::EventSubscription { + symmetric_executor::EventSubscription::new(self.dup()) + } } impl symmetric_executor::GuestEventGenerator for EventGenerator { @@ -61,21 +54,18 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { } fn subscribe(&self) -> symmetric_executor::EventSubscription { - let event_fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }; + // let event_fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }; if DEBUGGING { - println!( - "subscribe({:x}) fd={event_fd}", - Arc::as_ptr(&self.0) as usize - ); + println!("subscribe({:x})", Arc::as_ptr(&self.0) as usize); } - self.0.lock().unwrap().waiting.push(event_fd); + // self.0.lock().unwrap().waiting.push(event_fd); symmetric_executor::EventSubscription::new(EventSubscription { inner: EventType::Triggered { last_counter: AtomicU32::new(0), - event_fd, + // event_fd, event: Arc::clone(&self.0), }, - callback: None, + // callback: None, }) } @@ -106,7 +96,7 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { } struct Executor { - active_tasks: Vec, + active_tasks: Vec, } static EXECUTOR: Mutex = Mutex::new(Executor { @@ -114,7 +104,7 @@ static EXECUTOR: Mutex = Mutex::new(Executor { }); // while executing tasks from the loop we can't directly queue new ones static EXECUTOR_BUSY: AtomicBool = AtomicBool::new(false); -static NEW_TASKS: Mutex> = Mutex::new(Vec::new()); +static NEW_TASKS: Mutex> = Mutex::new(Vec::new()); impl symmetric_executor::Guest for Guest { type CallbackFunction = Ignore; @@ -139,7 +129,7 @@ impl symmetric_executor::Guest for Guest { let old_busy = EXECUTOR_BUSY.swap(true, Ordering::SeqCst); assert!(!old_busy); ex.active_tasks.iter_mut().for_each(|task| { - if task.ready() { + if task.inner.ready() { if DEBUGGING { println!( "task ready {:x} {:x}", @@ -154,12 +144,12 @@ impl symmetric_executor::Guest for Guest { match &task.inner { EventType::Triggered { last_counter: _, - event_fd, + // event_fd, event: _, } => { - unsafe { libc::FD_SET(*event_fd, rfd_ptr) }; - if *event_fd > maxfd { - maxfd = *event_fd + 1; + unsafe { libc::FD_SET(task.event_fd, rfd_ptr) }; + if task.event_fd > maxfd { + maxfd = task.event_fd + 1; } } EventType::SystemTime(system_time) => { @@ -253,27 +243,31 @@ impl symmetric_executor::Guest for Guest { callback: symmetric_executor::CallbackFunction, data: symmetric_executor::CallbackData, ) -> () { - // TODO: Tidy this mess up - // Note: Trigger is consumed, callback and data are managed elsewhere - let mut subscr = EventSubscription { - inner: EventType::SystemTime(std::time::UNIX_EPOCH), - callback: None, + 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: _, + } => unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }, + EventType::SystemTime(_system_time) => INVALID_FD, + }; + let subscr = QueuedEvent { + inner: trigger.inner, + callback: Some(CallbackEntry(cb, data)), + event_fd, }; - std::mem::swap(&mut subscr, unsafe { - // TODO: This should be handle to free the resource, but it is used later? - &mut *(trigger.take_handle() as *mut EventSubscription) - }); if DEBUGGING { match &subscr.inner { EventType::Triggered { last_counter: _, - event_fd, event, } => println!( - "register(Triggered {:x} fd {event_fd}, {:x},{:x})", + "register(Trigger {:x} fd {event_fd}, {:x},{:x})", Arc::as_ptr(event) as usize, - callback.handle(), - data.handle() + cb as usize, + data as usize ), EventType::SystemTime(system_time) => { let diff = system_time.duration_since(SystemTime::now()).unwrap(); @@ -281,15 +275,13 @@ impl symmetric_executor::Guest for Guest { "register(Time {}.{}, {:x},{:x})", diff.as_secs(), diff.subsec_nanos(), - callback.handle(), - data.handle() + cb as usize, + data as usize ); } } } - let cb: fn(*mut ()) -> CallbackState = unsafe { transmute(callback.take_handle()) }; - let data = data.take_handle() as *mut (); - subscr.callback.replace(CallbackEntry(cb, data)); + // subscr.callback.replace(CallbackEntry(cb, data)); match EXECUTOR.try_lock() { Ok(mut lock) => lock.active_tasks.push(subscr), Err(_err) => { @@ -320,6 +312,11 @@ unsafe impl Send for CallbackEntry {} struct EventSubscription { inner: EventType, +} + +struct QueuedEvent { + inner: EventType, + event_fd: EventFd, callback: Option, } @@ -328,22 +325,22 @@ impl EventSubscription { let inner = match &self.inner { EventType::Triggered { last_counter: last_counter_old, - event_fd, + // event_fd, event, } => { - let new_event_fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }; + // let new_event_fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }; let new_event = Arc::clone(event); let last_counter = last_counter_old.load(Ordering::Relaxed); if DEBUGGING { println!( - "dup(subscr {last_counter} {event_fd} {:x}) fd={new_event_fd}", + "dup(subscr {last_counter} {:x})", Arc::as_ptr(&event) as usize ); } - new_event.lock().unwrap().waiting.push(new_event_fd); + // new_event.lock().unwrap().waiting.push(new_event_fd); EventType::Triggered { last_counter: AtomicU32::new(last_counter), - event_fd: new_event_fd, + // event_fd: new_event_fd, event: new_event, } } @@ -351,12 +348,12 @@ impl EventSubscription { }; EventSubscription { inner, - callback: None, + // callback: None, } } } -impl Drop for EventSubscription { +impl Drop for QueuedEvent { fn drop(&mut self) { if let Some(cb) = &self.callback { if DEBUGGING { @@ -369,14 +366,18 @@ impl Drop for EventSubscription { match &self.inner { EventType::Triggered { last_counter: _, - event_fd, + // event_fd, event, } => { if DEBUGGING { - println!("drop(subscription fd {event_fd}"); + println!("drop(queued fd {}", self.event_fd); } - event.lock().unwrap().waiting.retain(|e| e != event_fd); - unsafe { libc::close(*event_fd) }; + event + .lock() + .unwrap() + .waiting + .retain(|&e| e != self.event_fd); + unsafe { libc::close(self.event_fd) }; } EventType::SystemTime(_system_time) => (), } @@ -386,8 +387,28 @@ impl Drop for EventSubscription { enum EventType { Triggered { last_counter: AtomicU32, - event_fd: EventFd, + // event_fd: EventFd, event: Arc>, }, SystemTime(SystemTime), } + +impl EventType { + pub fn ready(&self) -> bool { + match self { + EventType::Triggered { + last_counter, + // event_fd: _, + 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(), + } + } +} From c89ced5a877c53a2b8832bedf325c0d662f9ef76 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 Jan 2025 19:53:00 +0100 Subject: [PATCH 435/672] fully working again (main read in progress) --- .../tests/symmetric_stream/stream/src/stream_world.rs | 10 ++++------ crates/symmetric_executor/src/lib.rs | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 9e32def99..8bdd14054 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -681,10 +681,9 @@ mod _rt { pub fn from_handle(handle: *mut WitStream) -> Self { let subscr = unsafe { subscribe_event_send_ptr((&mut *handle).write_ready_event_send) }; - let subscr_copy = unsafe { EventSubscription::from_handle(subscr.handle()) }; + let subscr_copy = subscr.dup(); let ready = Box::pin(async move { - wait_on(&subscr_copy).await; - let _ = subscr_copy.take_handle(); + wait_on(subscr_copy).await; }); Self { handle: StreamHandle2(handle), @@ -815,10 +814,9 @@ mod _rt { // Ok(()) // wait before next element is written - let subscr_copy = unsafe { EventSubscription::from_handle(me.event.handle()) }; + let subscr_copy = me.event.dup(); me.future.replace(Box::pin(async move { - wait_on(&subscr_copy).await; - let _ = subscr_copy.take_handle(); + wait_on(subscr_copy).await; })); Ok(()) } diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 3a8cff9cf..0e34202bb 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -370,7 +370,7 @@ impl Drop for QueuedEvent { event, } => { if DEBUGGING { - println!("drop(queued fd {}", self.event_fd); + println!("drop(queued fd {})", self.event_fd); } event .lock() From 19bca45b7a80ea505c149b53d737e74c54db9935 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 Jan 2025 20:14:48 +0100 Subject: [PATCH 436/672] first two stream values arrive in main --- .../tests/symmetric_stream/main/src/main.rs | 51 ++++++++++++++++--- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index 095dec7e3..ad9e2fe47 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -2,6 +2,8 @@ // use std::pin::pin; +use std::sync::atomic::Ordering; + use wit_bindgen_symmetric_rt::{async_support::Stream, CallbackState}; #[link(name = "stream")] @@ -12,27 +14,62 @@ extern "C" { ) -> *mut (); } -extern "C" fn ready(_arg: *mut ()) -> CallbackState { - todo!() +const DATALEN: usize = 2; + +struct CallbackInfo { + stream: *mut Stream, + data: [u32; DATALEN], +} + +extern "C" fn ready(arg: *mut ()) -> CallbackState { + let info = unsafe { &*arg.cast::() }; + let len = unsafe { &*info.stream } + .ready_size + .swap(0, Ordering::Acquire); + if len > 0 { + for i in 0..len as usize { + println!("data {}", info.data[i]); + } + unsafe { + ((&*(&*info.stream).vtable).read)( + info.stream, + info.data.as_ptr().cast_mut().cast(), + DATALEN, + ); + }; + // call again + CallbackState::Pending + } else { + // finished + CallbackState::Ready + } } fn main() { let mut result_stream: *mut () = core::ptr::null_mut(); - let handle = unsafe { + let continuation = unsafe { testX3AtestX2Fstream_testX00X5BasyncX5Dcreate( core::ptr::null_mut(), (&mut result_stream as *mut *mut ()).cast(), ) }; - assert!(handle.is_null()); + // function should have completed (not async) + assert!(continuation.is_null()); let handle = result_stream.cast::(); - let mut target = Box::pin([0_u32, 0]); + let mut info = Box::pin(CallbackInfo { + stream: handle, + data: [0, 0], + }); unsafe { - ((&*(&*handle).vtable).read)(handle, target.as_mut_ptr().cast(), 2); + ((&*(&*handle).vtable).read)(handle, info.data.as_mut_ptr().cast(), DATALEN); }; let read_ready = unsafe { (&*handle).read_ready_event_send }; let subscription = unsafe { wit_bindgen_symmetric_rt::subscribe_event_send_ptr(read_ready) }; println!("Register read in main"); - wit_bindgen_symmetric_rt::register(subscription, ready, ((&mut *target) as *mut u32).cast()); + wit_bindgen_symmetric_rt::register( + subscription, + ready, + (&*info as *const CallbackInfo).cast_mut().cast(), + ); wit_bindgen_symmetric_rt::run(); } From 95febdfcd8b675c75301cb12a3afda2a36fe5b1a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 Jan 2025 21:43:38 +0100 Subject: [PATCH 437/672] prepare for reset --- .../symmetric_stream/stream/src/stream_world.rs | 4 +++- .../rust-client/src/async_support.rs | 13 +++++++------ crates/symmetric_executor/rust-client/src/module.rs | 3 ++- crates/symmetric_executor/src/executor.rs | 3 ++- crates/symmetric_executor/wit/executor.wit | 4 +++- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 8bdd14054..f4a497702 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -968,11 +968,13 @@ mod _rt { // assumption: future doesn't outlive stream let handle = StreamHandle2(me.handle.0); // duplicate handle (same assumption) - let event = unsafe { EventSubscription::from_handle(me.event.handle()) }; + let event = me.event.dup(); + // unsafe { EventSubscription::from_handle(me.event.handle()) }; me.future = Some(Box::pin(async move { let mut buffer = iter::repeat_with(MaybeUninit::uninit) .take(ceiling(4 * 1024, mem::size_of::())) .collect::>(); + assert!(!event.ready()); let stream_handle = StreamHandleRust { handle, event }; let result = if let Some(count) = T::read(&stream_handle, &mut buffer).await { diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index d41cfba0f..f9533ce5f 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -7,9 +7,9 @@ use std::{ task::{Context, Poll, RawWaker, RawWakerVTable}, }; -use crate::module::symmetric::runtime::symmetric_executor::{ +use crate::{activate_event_send_ptr, module::symmetric::runtime::symmetric_executor::{ self, CallbackState, EventGenerator, EventSubscription, -}; +}}; // See https://github.com/rust-lang/rust/issues/13231 for the limitation // / Send constraint on futures for spawn, loosen later @@ -72,10 +72,11 @@ extern "C" fn read_impl(stream: *mut Stream, buf: *mut (), size: usize) -> isize .swap(buf, Ordering::Release); assert_eq!(old_ptr, std::ptr::null_mut()); let write_evt = unsafe { &mut *stream }.write_ready_event_send; - let gen: EventGenerator = unsafe { EventGenerator::from_handle(write_evt as usize) }; - gen.activate(); - // don't consume - let _ = gen.take_handle(); + unsafe { activate_event_send_ptr(write_evt) }; + // let gen: EventGenerator = unsafe { EventGenerator::from_handle(write_evt as usize) }; + // gen.activate(); + // // don't consume + // let _ = gen.take_handle(); results::BLOCKED } diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index 0164db22b..061df6432 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -569,6 +569,7 @@ mod _rt { self as i64 } } + #[cfg(never)] pub mod stream_and_future_support { // use crate as wit_bindgen_symmetric_rt; use { @@ -1368,7 +1369,7 @@ mod _rt { } } #[allow(unused_imports)] -pub use _rt::stream_and_future_support; +//pub use _rt::stream_and_future_support; #[cfg(target_arch = "wasm32")] #[link_section = "component-type:wit-bindgen:0.36.0:symmetric:runtime@0.1.0:module:encoded world"] diff --git a/crates/symmetric_executor/src/executor.rs b/crates/symmetric_executor/src/executor.rs index 5d73481b3..2e7dd0443 100644 --- a/crates/symmetric_executor/src/executor.rs +++ b/crates/symmetric_executor/src/executor.rs @@ -1074,6 +1074,7 @@ mod _rt { pub fn run_ctors_once() { wit_bindgen::rt::run_ctors_once(); } + #[cfg(never)] pub mod stream_and_future_support { use { futures::{ @@ -1863,7 +1864,7 @@ mod _rt { extern crate alloc as alloc_crate; } #[allow(unused_imports)] -pub use _rt::stream_and_future_support; +// pub use _rt::stream_and_future_support; /// Generates `#[no_mangle]` functions to export the specified type as the /// root implementation of all generated traits. diff --git a/crates/symmetric_executor/wit/executor.wit b/crates/symmetric_executor/wit/executor.wit index db7665dc5..1d45600af 100644 --- a/crates/symmetric_executor/wit/executor.wit +++ b/crates/symmetric_executor/wit/executor.wit @@ -19,8 +19,10 @@ interface symmetric-executor { ready: func() -> bool; /// Create a timeout event from-timeout: static func(nanoseconds: u64) -> event-subscription; - /// Duplicate the subscription (e.g. for repeated callback registering) + /// 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 { From 13b99ee08a7f353f3f1481745411e1fb9afd3d79 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 Jan 2025 22:02:19 +0100 Subject: [PATCH 438/672] reset implementation --- .../tests/symmetric_stream/main/Cargo.toml | 2 +- crates/symmetric_executor/Cargo.toml | 6 + .../symmetric_executor/rust-client/Cargo.toml | 4 + .../rust-client/src/async_support.rs | 9 +- .../rust-client/src/module.rs | 1299 +++++++++-------- crates/symmetric_executor/src/executor.rs | 46 +- crates/symmetric_executor/src/lib.rs | 11 +- 7 files changed, 713 insertions(+), 664 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/Cargo.toml b/crates/cpp/tests/symmetric_stream/main/Cargo.toml index 6a30869cb..9b9a650b2 100644 --- a/crates/cpp/tests/symmetric_stream/main/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/main/Cargo.toml @@ -5,5 +5,5 @@ edition = "2021" [dependencies] stream = { version = "0.1.0", path = "../stream" } -symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor" } +symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor", features = ["trace"]} wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../../../../symmetric_executor/rust-client" } diff --git a/crates/symmetric_executor/Cargo.toml b/crates/symmetric_executor/Cargo.toml index 4f23b7857..e3fd9e1d8 100644 --- a/crates/symmetric_executor/Cargo.toml +++ b/crates/symmetric_executor/Cargo.toml @@ -15,3 +15,9 @@ wit-bindgen-rt = { version = "0.36.0", path = "../guest-rust/rt" } [lib] crate-type = ["cdylib"] + +[features] +# always off feature +never = [] +# output debugging information +trace = [] diff --git a/crates/symmetric_executor/rust-client/Cargo.toml b/crates/symmetric_executor/rust-client/Cargo.toml index c4ca546c7..0fc08d41d 100644 --- a/crates/symmetric_executor/rust-client/Cargo.toml +++ b/crates/symmetric_executor/rust-client/Cargo.toml @@ -8,3 +8,7 @@ edition = "2021" [dependencies] futures = "0.3.31" wit-bindgen = { version = "0.36.0", path = "../../guest-rust" } + +[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 index f9533ce5f..393a39cca 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -7,9 +7,12 @@ use std::{ task::{Context, Poll, RawWaker, RawWakerVTable}, }; -use crate::{activate_event_send_ptr, module::symmetric::runtime::symmetric_executor::{ - self, CallbackState, EventGenerator, EventSubscription, -}}; +use crate::{ + activate_event_send_ptr, + module::symmetric::runtime::symmetric_executor::{ + self, CallbackState, EventGenerator, EventSubscription, + }, +}; // See https://github.com/rust-lang/rust/issues/13231 for the limitation // / Send constraint on futures for spawn, loosen later diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index 061df6432..589370535 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -318,7 +318,7 @@ pub mod symmetric { } impl EventSubscription { #[allow(unused_unsafe, clippy::all)] - /// Duplicate the subscription (e.g. for repeated callback registering) + /// 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")] @@ -336,6 +336,25 @@ pub mod symmetric { } } } + 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 { @@ -569,362 +588,361 @@ mod _rt { self as i64 } } - #[cfg(never)] + #[cfg(feature = "never")] pub mod stream_and_future_support { - // use crate as wit_bindgen_symmetric_rt; use { futures::{ - // channel::oneshot, - // future::{self, FutureExt}, + channel::oneshot, + future::{self, FutureExt}, sink::Sink, stream::Stream, }, std::{ - // collections::hash_map::Entry, + collections::hash_map::Entry, convert::Infallible, fmt, - future::Future, - // iter, + future::{Future, IntoFuture}, + iter, marker::PhantomData, - mem::MaybeUninit, + mem::{self, ManuallyDrop, MaybeUninit}, pin::Pin, task::{Context, Poll}, }, - // wit_bindgen_symmetric_rt::async_support::{self, Handle}, + 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; - // 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 { - // handle: u32, - // _phantom: PhantomData, - // } - - // impl fmt::Debug for FutureWriter { - // fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // f.debug_struct("FutureWriter") - // .field("handle", &self.handle) - // .finish() - // } - // } + #[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; + 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 { + handle: u32, + _phantom: PhantomData, + } + + impl fmt::Debug for FutureWriter { + 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 { - // writer: Option>, - // future: Pin>>, - // } - - // impl Future for CancelableWrite { - // 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 CancelableWrite { - // /// 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 { - // self.cancel_mut() - // } - - // fn cancel_mut(&mut self) -> FutureWriter { - // 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 Drop for CancelableWrite { - // fn drop(&mut self) { - // if self.writer.is_some() { - // self.cancel_mut(); - // } - // } - // } - - // impl FutureWriter { - // /// Write the specified value to this `future`. - // pub fn write(self, v: T) -> CancelableWrite { - // 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>> - // } - // 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 Drop for FutureWriter { - // 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 { - // reader: Option>, - // future: Pin>>>, - // } - - // impl Future for CancelableRead { - // type Output = Option; - - // fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - // 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 CancelableRead { - // /// 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 { - // self.cancel_mut() - // } - - // fn cancel_mut(&mut self) -> FutureReader { - // 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 Drop for CancelableRead { - // fn drop(&mut self) { - // if self.reader.is_some() { - // self.cancel_mut(); - // } - // } - // } - - // /// Represents the readable end of a Component Model `future`. - // pub struct FutureReader { - // handle: u32, - // _phantom: PhantomData, - // } - - // impl fmt::Debug for FutureReader { - // fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // f.debug_struct("FutureReader") - // .field("handle", &self.handle) - // .finish() - // } - // } - - // impl FutureReader { - // #[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 IntoFuture for FutureReader { - // type Output = Option; - // type IntoFuture = CancelableRead; - - // /// 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>>, - // 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 Drop for FutureReader { - // 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!(), - // }, - // }); - // } - // } + pub struct CancelableWrite { + writer: Option>, + future: Pin>>, + } + + impl Future for CancelableWrite { + 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 CancelableWrite { + /// 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 { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureWriter { + 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 Drop for CancelableWrite { + fn drop(&mut self) { + if self.writer.is_some() { + self.cancel_mut(); + } + } + } + + impl FutureWriter { + /// Write the specified value to this `future`. + pub fn write(self, v: T) -> CancelableWrite { + 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>> + } + 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 Drop for FutureWriter { + 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 { + reader: Option>, + future: Pin>>>, + } + + impl Future for CancelableRead { + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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 CancelableRead { + /// 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 { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureReader { + 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 Drop for CancelableRead { + fn drop(&mut self) { + if self.reader.is_some() { + self.cancel_mut(); + } + } + } + + /// Represents the readable end of a Component Model `future`. + pub struct FutureReader { + handle: u32, + _phantom: PhantomData, + } + + impl fmt::Debug for FutureReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("FutureReader") + .field("handle", &self.handle) + .finish() + } + } + + impl FutureReader { + #[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 IntoFuture for FutureReader { + type Output = Option; + type IntoFuture = CancelableRead; + + /// 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>>, + 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 Drop for FutureReader { + 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 { @@ -944,22 +962,21 @@ mod _rt { impl Drop for CancelWriteOnDrop { fn drop(&mut self) { - todo!() - // 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), - // }, - // }); - // } + 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), + }, + }); + } } } @@ -1007,77 +1024,76 @@ mod _rt { } } - fn start_send(self: Pin<&mut Self>, _item: Vec) -> 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:: { - // 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:: { - // 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(()) - todo!() + fn start_send(self: Pin<&mut Self>, item: Vec) -> 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:: { + 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:: { + 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> { @@ -1092,21 +1108,20 @@ mod _rt { impl Drop for StreamWriter { fn drop(&mut self) { self.future = None; - todo!() - - // 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); - // } - // }, - // }); + + 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); + } + }, + }); } } @@ -1117,23 +1132,21 @@ mod _rt { impl Drop for CancelReadOnDrop { fn drop(&mut self) { - todo!() - - // 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), - // }, - // }); - // } + 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), + }, + }); + } } } @@ -1164,203 +1177,198 @@ mod _rt { impl StreamReader { #[doc(hidden)] - pub fn from_handle(_handle: u32) -> Self { - todo!() - // 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, - // } + 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 - todo!() + 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 Stream for StreamReader { type Item = Vec; - fn poll_next(self: Pin<&mut Self>, _cx: &mut Context) -> Poll> { - // 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:: { - // handle: Some(handle), - // _phantom: PhantomData, - // }; - // Box::pin(async move { - // let mut buffer = iter::repeat_with(MaybeUninit::uninit) - // .take(ceiling(64 * 1024, mem::size_of::())) - // .collect::>(); - - // let result = if let Some(count) = - // T::read(handle, &mut buffer).await - // { - // buffer.truncate(count); - // Some(unsafe { - // mem::transmute::>, Vec>(buffer) - // }) - // } else { - // None - // }; - // cancel_on_drop.handle = None; - // drop(cancel_on_drop); - // result - // }) - // as Pin>> - // } - // Handle::LocalOpen => { - // let (tx, rx) = oneshot::channel(); - // entry.insert(Handle::LocalWaiting(tx)); - // let mut cancel_on_drop = CancelReadOnDrop:: { - // 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, - // } - todo!() + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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:: { + handle: Some(handle), + _phantom: PhantomData, + }; + Box::pin(async move { + let mut buffer = iter::repeat_with(MaybeUninit::uninit) + .take(ceiling(64 * 1024, mem::size_of::())) + .collect::>(); + + let result = if let Some(count) = + T::read(handle, &mut buffer).await + { + buffer.truncate(count); + Some(unsafe { + mem::transmute::>, Vec>(buffer) + }) + } else { + None + }; + cancel_on_drop.handle = None; + drop(cancel_on_drop); + result + }) + as Pin>> + } + Handle::LocalOpen => { + let (tx, rx) = oneshot::channel(); + entry.insert(Handle::LocalWaiting(tx)); + let mut cancel_on_drop = CancelReadOnDrop:: { + 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 Drop for StreamReader { 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!(), - // }, - // }); - todo!() + 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() -> (FutureWriter, FutureReader) { - // 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, - // }, - // ) - // } + pub fn new_future() -> (FutureWriter, FutureReader) { + 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() -> (StreamWriter, StreamReader) { - // 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, - // }, - // ) - todo!() + 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 { @@ -1375,22 +1383,23 @@ mod _rt { #[link_section = "component-type:wit-bindgen:0.36.0:symmetric:runtime@0.1.0:module:encoded world"] #[doc(hidden)] #[allow(clippy::octal_escapes)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 741] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xe8\x04\x01A\x02\x01\ -A\x02\x01B\x1e\x04\0\x11callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\ +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 789] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x98\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\x01i\x03\x01@\0\0\x0d\x04\0\x1c\ -[constructor]event-generator\x01\x0e\x01h\x03\x01@\x01\x04self\x0f\0\x0a\x04\0![\ -method]event-generator.subscribe\x01\x10\x01@\x01\x04self\x0f\x01\0\x04\0\x20[me\ -thod]event-generator.activate\x01\x11\x01@\0\x01\0\x04\0\x03run\x01\x12\x01i\0\x01\ -i\x01\x01@\x03\x07trigger\x0a\x08callback\x13\x04data\x14\x01\0\x04\0\x08registe\ -r\x01\x15\x03\0*symmetric:runtime/symmetric-executor@0.1.0\x05\0\x04\0\x1esymmet\ -ric:runtime/module@0.1.0\x04\0\x0b\x0c\x01\0\x06module\x03\0\0\0G\x09producers\x01\ -\x0cprocessed-by\x02\x0dwit-component\x070.221.2\x10wit-bindgen-rust\x060.36.0"; +\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\x04\0\x1esymmetric:r\ +untime/module@0.1.0\x04\0\x0b\x0c\x01\0\x06module\x03\0\0\0G\x09producers\x01\x0c\ +processed-by\x02\x0dwit-component\x070.221.2\x10wit-bindgen-rust\x060.36.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/symmetric_executor/src/executor.rs b/crates/symmetric_executor/src/executor.rs index 2e7dd0443..5a827f5aa 100644 --- a/crates/symmetric_executor/src/executor.rs +++ b/crates/symmetric_executor/src/executor.rs @@ -717,6 +717,17 @@ pub mod exports { } #[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( ) -> *mut u8 { #[cfg(target_arch = "wasm32")] @@ -863,8 +874,10 @@ pub mod exports { fn ready(&self) -> bool; /// Create a timeout event fn from_timeout(nanoseconds: u64) -> EventSubscription; - /// Duplicate the subscription (e.g. for repeated callback registering) + /// 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)] @@ -918,6 +931,11 @@ pub mod exports { 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 { @@ -1074,7 +1092,7 @@ mod _rt { pub fn run_ctors_once() { wit_bindgen::rt::run_ctors_once(); } - #[cfg(never)] + #[cfg(feature = "never")] pub mod stream_and_future_support { use { futures::{ @@ -1898,23 +1916,23 @@ pub(crate) use __export_executor_impl as export; #[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; 745] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xea\x04\x01A\x02\x01\ -A\x02\x01B\x1e\x04\0\x11callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\ +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\x01i\x03\x01@\0\0\x0d\x04\0\x1c\ -[constructor]event-generator\x01\x0e\x01h\x03\x01@\x01\x04self\x0f\0\x0a\x04\0![\ -method]event-generator.subscribe\x01\x10\x01@\x01\x04self\x0f\x01\0\x04\0\x20[me\ -thod]event-generator.activate\x01\x11\x01@\0\x01\0\x04\0\x03run\x01\x12\x01i\0\x01\ -i\x01\x01@\x03\x07trigger\x0a\x08callback\x13\x04data\x14\x01\0\x04\0\x08registe\ -r\x01\x15\x04\0*symmetric:runtime/symmetric-executor@0.1.0\x05\0\x04\0\x20symmet\ -ric:runtime/executor@0.1.0\x04\0\x0b\x0e\x01\0\x08executor\x03\0\0\0G\x09produce\ -rs\x01\x0cprocessed-by\x02\x0dwit-component\x070.221.2\x10wit-bindgen-rust\x060.\ -36.0"; +\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)] diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 0e34202bb..74ec6da36 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -14,7 +14,7 @@ use executor::exports::symmetric::runtime::symmetric_executor::{ // GuestEventSubscription, }; -const DEBUGGING: bool = true; +const DEBUGGING: bool = cfg!(feature = "trace"); const INVALID_FD: EventFd = -1; mod executor; @@ -43,6 +43,15 @@ impl symmetric_executor::GuestEventSubscription for EventSubscription { 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 { From 9e5eef9b826c0d5bd6b220927f3871074cab58d7 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 4 Jan 2025 23:51:22 +0100 Subject: [PATCH 439/672] clean up subscription logic --- crates/cpp/tests/symmetric_stream/README.md | 66 +++++++++++++++++++ .../tests/symmetric_stream/source/src/lib.rs | 54 ++++++++------- .../stream/src/stream_world.rs | 55 +++++++++------- .../rust-client/src/async_support.rs | 26 +++++--- .../rust-client/src/module.rs | 1 - crates/symmetric_executor/src/lib.rs | 7 +- 6 files changed, 153 insertions(+), 56 deletions(-) create mode 100644 crates/cpp/tests/symmetric_stream/README.md diff --git a/crates/cpp/tests/symmetric_stream/README.md b/crates/cpp/tests/symmetric_stream/README.md new file mode 100644 index 000000000..92e318388 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream/README.md @@ -0,0 +1,66 @@ +# 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) + +## Special values of ready + + - CLOSED: MIN (0x8000…) also EOF + - BLOCKED: -1 (normal) + - CANCELLED: 0 (TBD) + +## Seqence + +"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 + - read (waits) + - start_write (wait and returns buffer) + - finish (can also set eof independently of start_write) + +Perhaps: + + - close_read (read with NULL?) + - close_write (=finish(EOF)?) + +### 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? diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index 7d23d4290..d6f809a4f 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -1,8 +1,9 @@ use std::sync::atomic::{AtomicU32, Ordering}; use wit_bindgen_symmetric_rt::{ - activate_event_send_ptr, async_support::Stream, register, subscribe_event_send_ptr, - CallbackState, EventSubscription, + activate_event_send_ptr, + async_support::{results, Stream}, + register, subscribe_event_send_ptr, CallbackState, EventSubscription, }; #[link(name = "symmetric_executor")] @@ -25,16 +26,23 @@ extern "C" fn timer_call(data: *mut ()) -> CallbackState { assert!(size >= 1); *unsafe { &mut *addr.cast::() } = count; let old_ready = unsafe { &*stream }.ready_size.swap(1, Ordering::Release); - assert_eq!(old_ready, 0); + assert_eq!(old_ready, results::BLOCKED); let read_ready_evt = unsafe { &*stream }.read_ready_event_send; unsafe { activate_event_send_ptr(read_ready_evt) }; - let ms_30 = unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(30*1_000_000) - } as usize; - assert_ne!(ms_30, 0); - let event = unsafe { EventSubscription::from_handle(ms_30) }; - register(event, timer_call, data); - } else { + // let ms_30 = unsafe { + // symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(30*1_000_000) + // } as usize; + // assert_ne!(ms_30, 0); + // let event = unsafe { EventSubscription::from_handle(ms_30) }; + // register(event, timer_call, data); + } + CallbackState::Ready +} + +extern "C" fn write_ready(data: *mut ()) -> CallbackState { + let count = COUNT.load(Ordering::SeqCst); + if count > 5 { + let stream: *const Stream = data.cast(); // EOF let old_ready = unsafe { &*stream } .ready_size @@ -42,20 +50,20 @@ extern "C" fn timer_call(data: *mut ()) -> CallbackState { assert_eq!(old_ready, 0); let read_ready_evt = unsafe { &*stream }.read_ready_event_send; unsafe { activate_event_send_ptr(read_ready_evt) }; + CallbackState::Ready + } else { + if count == 1 { + println!("we can write now, starting timer"); + } + let ms_30 = unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(30*1_000_000) + } as usize; + assert_ne!(ms_30, 0); + let event = unsafe { EventSubscription::from_handle(ms_30) }; + register(event, timer_call, data); + // this callback is done + CallbackState::Pending } - CallbackState::Ready -} - -extern "C" fn write_ready(data: *mut ()) -> CallbackState { - println!("we can write now, starting timer"); - let ms_30 = unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(30*1_000_000) - } as usize; - assert_ne!(ms_30, 0); - let event = unsafe { EventSubscription::from_handle(ms_30) }; - register(event, timer_call, data); - // this callback is done - CallbackState::Ready } #[allow(non_snake_case)] diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index f4a497702..18fb9cd43 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -83,7 +83,7 @@ pub mod test { StreamHandle2(stream.handle.0), address, values.len(), - &stream.event, + // &stream.event, ) .await }; @@ -664,7 +664,7 @@ mod _rt { pub struct StreamWriter { handle: StreamHandle2, future: Option + 'static + Send>>>, - event: EventSubscription, + // event: EventSubscription, _phantom: PhantomData, } @@ -679,16 +679,16 @@ mod _rt { #[doc(hidden)] pub fn from_handle(handle: *mut WitStream) -> Self { - let subscr = - unsafe { subscribe_event_send_ptr((&mut *handle).write_ready_event_send) }; - let subscr_copy = subscr.dup(); - let ready = Box::pin(async move { - wait_on(subscr_copy).await; - }); + // let subscr = + // unsafe { subscribe_event_send_ptr((&mut *handle).write_ready_event_send) }; + // let subscr_copy = subscr.dup(); + // let ready = Box::pin(async move { + // wait_on(subscr).await; + // }); Self { handle: StreamHandle2(handle), - future: Some(ready), - event: subscr, + future: None, //Some(ready), + // event: subscr, _phantom: PhantomData, } } @@ -708,6 +708,20 @@ mod _rt { fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let me = self.get_mut(); + // see also StreamReader::poll_next + if me.future.is_none() { + let handle = StreamHandle2(me.handle.0); + me.future = Some(Box::pin(async move { + let handle_local = handle; + let subscr = unsafe { + subscribe_event_send_ptr((&*(handle_local.0)).write_ready_event_send) + }; + subscr.reset(); + wait_on(subscr).await; + }) + as Pin + Send>>); + } + if let Some(future) = &mut me.future { match future.as_mut().poll(cx) { Poll::Ready(_) => { @@ -814,10 +828,7 @@ mod _rt { // Ok(()) // wait before next element is written - let subscr_copy = me.event.dup(); - me.future.replace(Box::pin(async move { - wait_on(subscr_copy).await; - })); + // let subscr_copy = me.event.dup(); Ok(()) } @@ -884,7 +895,7 @@ mod _rt { pub struct StreamReader { handle: StreamHandle2, future: Option>> + 'static + Send>>>, - event: EventSubscription, + // event: EventSubscription, _phantom: PhantomData, } @@ -927,12 +938,12 @@ mod _rt { // }, // }); - let subscr = - unsafe { subscribe_event_send_ptr((&mut *handle).read_ready_event_send) }; + // let subscr = + // unsafe { subscribe_event_send_ptr((&mut *handle).read_ready_event_send) }; Self { handle: StreamHandle2(handle), future: None, - event: subscr, + // event: subscr, _phantom: PhantomData, } } @@ -968,14 +979,14 @@ mod _rt { // assumption: future doesn't outlive stream let handle = StreamHandle2(me.handle.0); // duplicate handle (same assumption) - let event = me.event.dup(); + // let event = me.event.dup(); // unsafe { EventSubscription::from_handle(me.event.handle()) }; me.future = Some(Box::pin(async move { let mut buffer = iter::repeat_with(MaybeUninit::uninit) .take(ceiling(4 * 1024, mem::size_of::())) .collect::>(); - assert!(!event.ready()); - let stream_handle = StreamHandleRust { handle, event }; + // assert!(!event.ready()); + let stream_handle = StreamHandleRust { handle }; let result = if let Some(count) = T::read(&stream_handle, &mut buffer).await { buffer.truncate(count); @@ -983,7 +994,7 @@ mod _rt { } else { None }; - let _ = stream_handle.event.take_handle(); + // let _ = stream_handle.event.take_handle(); // cancel_on_drop.handle = None; // drop(cancel_on_drop); result diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 393a39cca..beaf650aa 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -12,6 +12,7 @@ use crate::{ module::symmetric::runtime::symmetric_executor::{ self, CallbackState, EventGenerator, EventSubscription, }, + subscribe_event_send_ptr, }; // See https://github.com/rust-lang/rust/issues/13231 for the limitation @@ -66,6 +67,11 @@ pub struct Stream { } extern "C" fn read_impl(stream: *mut Stream, buf: *mut (), size: usize) -> isize { + let old_ready = unsafe { &*stream }.ready_size.load(Ordering::SeqCst); + if old_ready == results::CLOSED { + return old_ready; + } + assert!(old_ready == results::BLOCKED); let old_size = unsafe { &mut *stream } .read_size .swap(size, Ordering::Acquire); @@ -87,8 +93,9 @@ extern "C" fn write_impl(_stream: *mut Stream, _buf: *mut (), _size: usize) -> i todo!() } -extern "C" fn read_close_impl(_stream: *mut Stream) { - todo!() +extern "C" fn read_close_impl(stream: *mut Stream) { + read_impl(stream, core::ptr::null_mut(), 0); + // deallocate? } extern "C" fn write_close_impl(_stream: *mut Stream) { @@ -110,7 +117,7 @@ impl Stream { write_ready_event_send: EventGenerator::new().take_handle() as *mut (), read_addr: AtomicPtr::new(core::ptr::null_mut()), read_size: AtomicUsize::new(0), - ready_size: AtomicIsize::new(0), + ready_size: AtomicIsize::new(results::BLOCKED), } } } @@ -250,7 +257,7 @@ pub fn spawn(future: impl Future + 'static + Send) { // TASKS.lock().unwrap().push(Box::new(future)); } -mod results { +pub mod results { pub const BLOCKED: isize = -1; pub const CLOSED: isize = isize::MIN; pub const CANCELED: isize = 0; @@ -275,15 +282,16 @@ unsafe impl Send for AddressSend {} pub struct StreamHandleRust { pub handle: StreamHandle2, - pub event: EventSubscription, + // pub event: EventSubscription, } +// this is used for reading? pub async unsafe fn await_stream_result( import: unsafe extern "C" fn(*mut Stream, *mut (), usize) -> isize, stream: StreamHandle2, address: AddressSend, count: usize, - event: &EventSubscription, + // event: &EventSubscription, ) -> Option { let stream_copy = stream.0; let result = import(stream_copy, address.0, count); @@ -293,10 +301,12 @@ pub async unsafe fn await_stream_result( // (*CURRENT).todo += 1; // let (tx, rx) = oneshot::channel(); // CALLS.insert(stream as _, tx); - wait_on(event.dup()).await; + let event = unsafe { subscribe_event_send_ptr((&*stream.0).read_ready_event_send) }; + event.reset(); + wait_on(event).await; let v = unsafe { &mut *stream.0 } .ready_size - .swap(0, Ordering::SeqCst); + .swap(results::BLOCKED, Ordering::SeqCst); if let results::CLOSED | results::CANCELED = v { None } else { diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index 589370535..d6be6cae9 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -1378,7 +1378,6 @@ mod _rt { } #[allow(unused_imports)] //pub use _rt::stream_and_future_support; - #[cfg(target_arch = "wasm32")] #[link_section = "component-type:wit-bindgen:0.36.0:symmetric:runtime@0.1.0:module:encoded world"] #[doc(hidden)] diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 74ec6da36..986313c76 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -43,10 +43,13 @@ impl symmetric_executor::GuestEventSubscription for EventSubscription { fn dup(&self) -> symmetric_executor::EventSubscription { symmetric_executor::EventSubscription::new(self.dup()) } - + fn reset(&self) { match &self.inner { - EventType::Triggered { last_counter, event } => { + EventType::Triggered { + last_counter, + event, + } => { last_counter.store(event.lock().unwrap().counter, Ordering::Relaxed); } EventType::SystemTime(_system_time) => (), From 8aac26aa7972ed17f04fc1ca5a270d8b21fc59e3 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 Jan 2025 00:19:11 +0100 Subject: [PATCH 440/672] still blocked somewhere --- .../tests/symmetric_stream/stream/src/lib.rs | 1 + .../stream/src/stream_world.rs | 6 ++++- crates/symmetric_executor/src/lib.rs | 23 +++++++++++++++---- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs index 56e66f6d1..8f5010a3e 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs @@ -15,6 +15,7 @@ impl stream_world::exports::test::test::stream_test::Guest for MyStruct { 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(); } diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 18fb9cd43..097e8558d 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -260,6 +260,7 @@ mod _rt { use std::sync::atomic::Ordering; use wit_bindgen_symmetric_rt::activate_event_send_ptr; + use wit_bindgen_symmetric_rt::async_support::results; use wit_bindgen_symmetric_rt::async_support::wait_on; pub use wit_bindgen_symmetric_rt::async_support::Stream as WitStream; pub use wit_bindgen_symmetric_rt::async_support::StreamHandle2; @@ -708,8 +709,11 @@ mod _rt { fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let me = self.get_mut(); + let ready = + unsafe { &*me.handle.0 }.ready_size.load(Ordering::SeqCst) != results::BLOCKED; + // see also StreamReader::poll_next - if me.future.is_none() { + if !ready && me.future.is_none() { let handle = StreamHandle2(me.handle.0); me.future = Some(Box::pin(async move { let handle_local = handle; diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 986313c76..b80568fce 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -82,13 +82,20 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { } fn activate(&self) { - if DEBUGGING { - println!("activate({:x})", Arc::as_ptr(&self.0) as usize); - } 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, @@ -103,6 +110,8 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { ); } }); + } else if DEBUGGING { + println!("activate failure"); } } } @@ -261,8 +270,12 @@ impl symmetric_executor::Guest for Guest { let event_fd = match &trigger.inner { EventType::Triggered { last_counter: _, - event: _, - } => unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }, + 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 { From 6943ab7dfe293f8569628e069247eb35b0018157 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 Jan 2025 00:40:05 +0100 Subject: [PATCH 441/672] not properly cleaning up, but the data arrives --- crates/cpp/tests/symmetric_stream/main/src/main.rs | 7 +++++-- .../tests/symmetric_stream/stream/src/stream_world.rs | 11 ++++++++--- crates/symmetric_executor/src/lib.rs | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index ad9e2fe47..1da711924 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -4,7 +4,10 @@ use std::sync::atomic::Ordering; -use wit_bindgen_symmetric_rt::{async_support::Stream, CallbackState}; +use wit_bindgen_symmetric_rt::{ + async_support::{results, Stream}, + CallbackState, +}; #[link(name = "stream")] extern "C" { @@ -25,7 +28,7 @@ extern "C" fn ready(arg: *mut ()) -> CallbackState { let info = unsafe { &*arg.cast::() }; let len = unsafe { &*info.stream } .ready_size - .swap(0, Ordering::Acquire); + .swap(results::BLOCKED, Ordering::Acquire); if len > 0 { for i in 0..len as usize { println!("data {}", info.data[i]); diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 097e8558d..0d97679c1 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -709,8 +709,13 @@ mod _rt { fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let me = self.get_mut(); - let ready = - unsafe { &*me.handle.0 }.ready_size.load(Ordering::SeqCst) != results::BLOCKED; + // This is for reading + // let ready = + // unsafe { &*me.handle.0 }.ready_size.load(Ordering::SeqCst) != results::BLOCKED; + let ready = !unsafe { &*me.handle.0 } + .read_addr + .load(Ordering::SeqCst) + .is_null(); // see also StreamReader::poll_next if !ready && me.future.is_none() { @@ -758,7 +763,7 @@ mod _rt { let old_ready = unsafe { &*stream } .ready_size .swap(item_len as isize, Ordering::Release); - assert_eq!(old_ready, 0); + assert_eq!(old_ready, results::BLOCKED); let read_ready_evt = unsafe { &*stream }.read_ready_event_send; unsafe { activate_event_send_ptr(read_ready_evt) }; // assert!(self.future.is_none()); diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index b80568fce..cd4b3b216 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -169,7 +169,7 @@ impl symmetric_executor::Guest for Guest { event: _, } => { unsafe { libc::FD_SET(task.event_fd, rfd_ptr) }; - if task.event_fd > maxfd { + if task.event_fd >= maxfd { maxfd = task.event_fd + 1; } } From d8af6cb30c535464478126886ce929a5c894faa8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 Jan 2025 14:02:35 +0100 Subject: [PATCH 442/672] avoid overly registering callbacks --- crates/cpp/tests/symmetric_async/main/Cargo.toml | 2 +- crates/cpp/tests/symmetric_stream/source/src/lib.rs | 2 +- .../rust-client/src/async_support.rs | 13 ++++--------- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/crates/cpp/tests/symmetric_async/main/Cargo.toml b/crates/cpp/tests/symmetric_async/main/Cargo.toml index 455e9ed0f..d769962ef 100644 --- a/crates/cpp/tests/symmetric_async/main/Cargo.toml +++ b/crates/cpp/tests/symmetric_async/main/Cargo.toml @@ -5,5 +5,5 @@ edition = "2021" [dependencies] async_module = { version = "0.1.0", path = "../async_module" } -symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor" } +symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor", features = ["trace"] } wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../../../../symmetric_executor/rust-client" } diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index d6f809a4f..68d5a761c 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -47,7 +47,7 @@ extern "C" fn write_ready(data: *mut ()) -> CallbackState { let old_ready = unsafe { &*stream } .ready_size .swap(isize::MIN, Ordering::Release); - assert_eq!(old_ready, 0); + assert_eq!(old_ready, results::BLOCKED); let read_ready_evt = unsafe { &*stream }.read_ready_event_send; unsafe { activate_event_send_ptr(read_ready_evt) }; CallbackState::Ready diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index beaf650aa..194a3f64f 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -167,20 +167,13 @@ pub async fn wait_on(wait_for: EventSubscription) { 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()); - // dangerous duplication? - // let wait_for_copy = unsafe { EventSubscription::from_handle(wait_for.handle()) }; - //let _old_waiting_for = std::mem::swap( unsafe { &mut *(data.cast::>().cast_mut()) }, &mut copy, ); - // .replace(wait_for); - // ~~don't free the old subscription we found~~ - // if let Some(subscription) = old_waiting_for { - // subscription.take_handle(); - // } Poll::Pending } }) @@ -194,7 +187,9 @@ extern "C" fn symmetric_callback(obj: *mut ()) -> symmetric_executor::CallbackSt let state = obj.cast::(); let waiting_for = unsafe { &mut *state }.waiting_for.take(); super::register(waiting_for.unwrap(), symmetric_callback, obj); - CallbackState::Pending + // as we registered this callback on a new event stop calling + // from the old event + CallbackState::Ready } } } From e6dbfdbb2ad8a181a0a3879e6dd6e67d395689d9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 Jan 2025 14:20:07 +0100 Subject: [PATCH 443/672] wow, the streaming example works --- crates/symmetric_executor/src/lib.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index cd4b3b216..ee34a7a0c 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -210,8 +210,18 @@ impl symmetric_executor::Guest for Guest { break; } } + if tvptr.is_null() && maxfd == 0 { + // 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); + // assert!(!tvptr.is_null() || maxfd > 0); if DEBUGGING { if tvptr.is_null() { println!("select({maxfd}, {:x}, null)", unsafe { From 120e594d88d2fe82d2737d6fcd4f74b90a7595df Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 Jan 2025 14:40:55 +0100 Subject: [PATCH 444/672] plug one memory leak and clean up --- .../rust-client/src/async_support.rs | 37 ++----------------- 1 file changed, 3 insertions(+), 34 deletions(-) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 194a3f64f..e3f714d7d 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -20,8 +20,6 @@ use crate::{ // pub unsafe auto trait MaybeSend : Send {} // unsafe impl MaybeSend for T where T: Send {} -// pub trait FutureMaybeSend : Future + MaybeSend {} - type BoxFuture = Pin + 'static>>; struct FutureState { @@ -82,10 +80,6 @@ extern "C" fn read_impl(stream: *mut Stream, buf: *mut (), size: usize) -> isize assert_eq!(old_ptr, std::ptr::null_mut()); let write_evt = unsafe { &mut *stream }.write_ready_event_send; unsafe { activate_event_send_ptr(write_evt) }; - // let gen: EventGenerator = unsafe { EventGenerator::from_handle(write_evt as usize) }; - // gen.activate(); - // // don't consume - // let _ = gen.take_handle(); results::BLOCKED } @@ -122,17 +116,6 @@ impl Stream { } } -// pub enum Entry<'a, K, V> { -// Vacant(), -// Occupied(&'a mut Stream), -// } - -// #[doc(hidden)] -// pub fn with_entry(h: *mut (), f: impl FnOnce(Entry<'_, u32, Handle>) -> T) -> T { -// let entry = unsafe { &mut *(h.cast::()) }; -// f(hash_map::Entry::Occupied(entry)) -// } - static VTABLE: RawWakerVTable = RawWakerVTable::new( |_| RawWaker::new(core::ptr::null(), &VTABLE), // `wake` does nothing @@ -229,7 +212,6 @@ pub fn first_poll( #[doc(hidden)] pub async unsafe fn await_result( function: unsafe extern "C" fn(*mut u8, *mut u8) -> *mut u8, - // _params_layout: Layout, params: *mut u8, results: *mut u8, ) { @@ -245,11 +227,10 @@ pub unsafe fn callback(_ctx: *mut u8, _event0: i32, _event1: i32, _event2: i32) todo!() } -// static TASKS: Mutex + 'static + Send>>> = Mutex::new(Vec::new()); - pub fn spawn(future: impl Future + 'static + Send) { - let _ = first_poll(future, |()| ()); - // TASKS.lock().unwrap().push(Box::new(future)); + let wait_for = first_poll(future, |()| ()); + let wait_for = unsafe { EventSubscription::from_handle(wait_for as usize) }; + drop(wait_for); } pub mod results { @@ -269,15 +250,8 @@ pub struct AddressSend(pub *mut ()); unsafe impl Send for AddressSend {} // unsafe impl Sync for StreamHandle2 {} -// pub enum SubscriptionType { -// None, -// Read(EventSubscription), -// Write(EventSubscription), -// } - pub struct StreamHandleRust { pub handle: StreamHandle2, - // pub event: EventSubscription, } // this is used for reading? @@ -286,16 +260,11 @@ pub async unsafe fn await_stream_result( stream: StreamHandle2, address: AddressSend, count: usize, - // event: &EventSubscription, ) -> Option { let stream_copy = stream.0; let result = import(stream_copy, address.0, count); match result { results::BLOCKED => { - // assert!(!CURRENT.is_null()); - // (*CURRENT).todo += 1; - // let (tx, rx) = oneshot::channel(); - // CALLS.insert(stream as _, tx); let event = unsafe { subscribe_event_send_ptr((&*stream.0).read_ready_event_send) }; event.reset(); wait_on(event).await; From 73884fb97a092b3a0624cfd9faee4889403997b0 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 Jan 2025 14:50:14 +0100 Subject: [PATCH 445/672] remove vtable --- .../tests/symmetric_stream/main/src/main.rs | 10 +-- .../stream/src/stream_world.rs | 8 +- .../rust-client/src/async_support.rs | 75 ++++++++++--------- 3 files changed, 48 insertions(+), 45 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index 1da711924..1b05d478a 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -5,7 +5,7 @@ use std::sync::atomic::Ordering; use wit_bindgen_symmetric_rt::{ - async_support::{results, Stream}, + async_support::{self, results, Stream}, CallbackState, }; @@ -34,11 +34,7 @@ extern "C" fn ready(arg: *mut ()) -> CallbackState { println!("data {}", info.data[i]); } unsafe { - ((&*(&*info.stream).vtable).read)( - info.stream, - info.data.as_ptr().cast_mut().cast(), - DATALEN, - ); + async_support::stream::read(info.stream, info.data.as_ptr().cast_mut().cast(), DATALEN); }; // call again CallbackState::Pending @@ -64,7 +60,7 @@ fn main() { data: [0, 0], }); unsafe { - ((&*(&*handle).vtable).read)(handle, info.data.as_mut_ptr().cast(), DATALEN); + async_support::stream::read(handle, info.data.as_mut_ptr().cast(), DATALEN); }; let read_ready = unsafe { (&*handle).read_ready_event_send }; let subscription = unsafe { wit_bindgen_symmetric_rt::subscribe_event_send_ptr(read_ready) }; diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 0d97679c1..076e16a73 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -12,9 +12,8 @@ pub mod test { super::super::super::__link_custom_section_describing_imports; // use _rt::stream_and_future_support::StreamHandle2; - use wit_bindgen_symmetric_rt::{ - async_support::{AddressSend, StreamHandle2, StreamHandleRust}, - // subscribe_event_send_ptr, + use wit_bindgen_symmetric_rt::async_support::{ + self, AddressSend, StreamHandle2, StreamHandleRust, }; use super::super::super::_rt; @@ -60,7 +59,8 @@ pub mod test { // #[link_name = "[async][stream-read-0]create"] // fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; // } - let poll_fn = unsafe { &*((&*(stream.handle.0)).vtable) }.read; + let poll_fn = async_support::stream::read; + // &*((&*(stream.handle.0)).vtable) }.read; // match &stream.event { // SubscriptionType::None => { // let subscr = unsafe { diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index e3f714d7d..77a6cef16 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -56,7 +56,7 @@ pub struct StreamVtable { #[repr(C)] pub struct Stream { - pub vtable: *const StreamVtable, + // pub vtable: *const StreamVtable, pub read_ready_event_send: *mut (), pub write_ready_event_send: *mut (), pub read_addr: AtomicPtr<()>, @@ -64,49 +64,56 @@ pub struct Stream { pub ready_size: AtomicIsize, } -extern "C" fn read_impl(stream: *mut Stream, buf: *mut (), size: usize) -> isize { - let old_ready = unsafe { &*stream }.ready_size.load(Ordering::SeqCst); - if old_ready == results::CLOSED { - return old_ready; +pub mod stream { + use std::sync::atomic::Ordering; + + use super::{results, Stream}; + use crate::activate_event_send_ptr; + + pub unsafe extern "C" fn read(stream: *mut Stream, buf: *mut (), size: usize) -> isize { + let old_ready = unsafe { &*stream }.ready_size.load(Ordering::SeqCst); + if old_ready == results::CLOSED { + return old_ready; + } + assert!(old_ready == results::BLOCKED); + let old_size = unsafe { &mut *stream } + .read_size + .swap(size, Ordering::Acquire); + assert_eq!(old_size, 0); + let old_ptr = unsafe { &mut *stream } + .read_addr + .swap(buf, Ordering::Release); + assert_eq!(old_ptr, std::ptr::null_mut()); + let write_evt = unsafe { &mut *stream }.write_ready_event_send; + unsafe { activate_event_send_ptr(write_evt) }; + results::BLOCKED } - assert!(old_ready == results::BLOCKED); - let old_size = unsafe { &mut *stream } - .read_size - .swap(size, Ordering::Acquire); - assert_eq!(old_size, 0); - let old_ptr = unsafe { &mut *stream } - .read_addr - .swap(buf, Ordering::Release); - assert_eq!(old_ptr, std::ptr::null_mut()); - let write_evt = unsafe { &mut *stream }.write_ready_event_send; - unsafe { activate_event_send_ptr(write_evt) }; - results::BLOCKED } -extern "C" fn write_impl(_stream: *mut Stream, _buf: *mut (), _size: usize) -> isize { - todo!() -} +// extern "C" fn write_impl(_stream: *mut Stream, _buf: *mut (), _size: usize) -> isize { +// todo!() +// } -extern "C" fn read_close_impl(stream: *mut Stream) { - read_impl(stream, core::ptr::null_mut(), 0); - // deallocate? -} +// extern "C" fn read_close_impl(stream: *mut Stream) { +// read_impl(stream, core::ptr::null_mut(), 0); +// // deallocate? +// } -extern "C" fn write_close_impl(_stream: *mut Stream) { - todo!() -} +// extern "C" fn write_close_impl(_stream: *mut Stream) { +// todo!() +// } -const STREAM_VTABLE: StreamVtable = StreamVtable { - read: read_impl, - close_read: read_close_impl, - write: write_impl, - close_write: write_close_impl, -}; +// const STREAM_VTABLE: StreamVtable = StreamVtable { +// read: read_impl, +// close_read: read_close_impl, +// write: write_impl, +// close_write: write_close_impl, +// }; impl Stream { pub fn new() -> Self { Self { - vtable: &STREAM_VTABLE as *const StreamVtable, + // vtable: &STREAM_VTABLE as *const StreamVtable, read_ready_event_send: EventGenerator::new().take_handle() as *mut (), write_ready_event_send: EventGenerator::new().take_handle() as *mut (), read_addr: AtomicPtr::new(core::ptr::null_mut()), From 17ffb99255a95932b6bd0b8ec18ce733c285244d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 Jan 2025 15:07:03 +0100 Subject: [PATCH 446/672] stream is half abstracted --- .../tests/symmetric_stream/main/src/main.rs | 7 +++- .../tests/symmetric_stream/source/src/lib.rs | 10 ++--- .../stream/src/stream_world.rs | 22 ++++++----- .../rust-client/src/async_support.rs | 39 +++++++++++-------- 4 files changed, 44 insertions(+), 34 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index 1b05d478a..1675b2e62 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -62,8 +62,11 @@ fn main() { unsafe { async_support::stream::read(handle, info.data.as_mut_ptr().cast(), DATALEN); }; - let read_ready = unsafe { (&*handle).read_ready_event_send }; - let subscription = unsafe { wit_bindgen_symmetric_rt::subscribe_event_send_ptr(read_ready) }; + let subscription = unsafe { + wit_bindgen_symmetric_rt::subscribe_event_send_ptr(async_support::stream::read_ready_event( + handle, + )) + }; println!("Register read in main"); wit_bindgen_symmetric_rt::register( subscription, diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index 68d5a761c..ac7e96b08 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -2,7 +2,7 @@ use std::sync::atomic::{AtomicU32, Ordering}; use wit_bindgen_symmetric_rt::{ activate_event_send_ptr, - async_support::{results, Stream}, + async_support::{self, results, Stream}, register, subscribe_event_send_ptr, CallbackState, EventSubscription, }; @@ -27,8 +27,7 @@ extern "C" fn timer_call(data: *mut ()) -> CallbackState { *unsafe { &mut *addr.cast::() } = count; let old_ready = unsafe { &*stream }.ready_size.swap(1, Ordering::Release); assert_eq!(old_ready, results::BLOCKED); - let read_ready_evt = unsafe { &*stream }.read_ready_event_send; - unsafe { activate_event_send_ptr(read_ready_evt) }; + unsafe { activate_event_send_ptr(async_support::stream::read_ready_event(stream)) }; // let ms_30 = unsafe { // symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(30*1_000_000) // } as usize; @@ -48,8 +47,7 @@ extern "C" fn write_ready(data: *mut ()) -> CallbackState { .ready_size .swap(isize::MIN, Ordering::Release); assert_eq!(old_ready, results::BLOCKED); - let read_ready_evt = unsafe { &*stream }.read_ready_event_send; - unsafe { activate_event_send_ptr(read_ready_evt) }; + unsafe { activate_event_send_ptr(async_support::stream::read_ready_event(stream)) }; CallbackState::Ready } else { if count == 1 { @@ -73,8 +71,8 @@ pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate( results: *mut u8, ) -> *mut u8 { let obj = Box::new(Stream::new()); - let event = unsafe { subscribe_event_send_ptr(obj.write_ready_event_send) }; let addr = Box::into_raw(obj); + let event = unsafe { subscribe_event_send_ptr(async_support::stream::write_ready_event(addr)) }; register(event, write_ready, addr.cast()); *unsafe { &mut *results.cast::<*mut Stream>() } = addr; std::ptr::null_mut() diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 076e16a73..b7f1aa60d 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -260,13 +260,14 @@ mod _rt { use std::sync::atomic::Ordering; use wit_bindgen_symmetric_rt::activate_event_send_ptr; + use wit_bindgen_symmetric_rt::async_support; use wit_bindgen_symmetric_rt::async_support::results; use wit_bindgen_symmetric_rt::async_support::wait_on; pub use wit_bindgen_symmetric_rt::async_support::Stream as WitStream; pub use wit_bindgen_symmetric_rt::async_support::StreamHandle2; use wit_bindgen_symmetric_rt::async_support::StreamHandleRust; use wit_bindgen_symmetric_rt::subscribe_event_send_ptr; - use wit_bindgen_symmetric_rt::EventSubscription; + // use wit_bindgen_symmetric_rt::EventSubscription; use { futures::{ // channel::oneshot, @@ -723,7 +724,9 @@ mod _rt { me.future = Some(Box::pin(async move { let handle_local = handle; let subscr = unsafe { - subscribe_event_send_ptr((&*(handle_local.0)).write_ready_event_send) + subscribe_event_send_ptr(async_support::stream::write_ready_event( + handle_local.0, + )) }; subscr.reset(); wait_on(subscr).await; @@ -764,8 +767,7 @@ mod _rt { .ready_size .swap(item_len as isize, Ordering::Release); assert_eq!(old_ready, results::BLOCKED); - let read_ready_evt = unsafe { &*stream }.read_ready_event_send; - unsafe { activate_event_send_ptr(read_ready_evt) }; + unsafe { activate_event_send_ptr(async_support::stream::read_ready_event(stream)) }; // assert!(self.future.is_none()); // async_support::with_entry(self.handle, |entry| match entry { // Entry::Vacant(_) => unreachable!(), @@ -868,9 +870,7 @@ mod _rt { // }, // }); let stream = self.handle.0; - let read_ready_evt = unsafe { &*stream }.read_ready_event_send; - unsafe { activate_event_send_ptr(read_ready_evt) }; - // todo!() + unsafe { activate_event_send_ptr(async_support::stream::read_ready_event(stream)) }; } } @@ -1099,9 +1099,11 @@ mod _rt { // }, // }); //todo!() - let stream = self.handle.0; - let write_ready_evt = unsafe { &*stream }.write_ready_event_send; - unsafe { activate_event_send_ptr(write_ready_evt) }; + // let stream = self.handle.0; + // let write_ready_evt = unsafe { &*stream }.write_ready_event_send; + unsafe { + activate_event_send_ptr(async_support::stream::write_ready_event(self.handle.0)) + }; } } diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 77a6cef16..74cee0531 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -40,25 +40,24 @@ pub enum Handle { Write, } -#[repr(C)] -pub struct StreamVtable { - // magic value for EOF(-1) and block(MIN) - // asynchronous function, if this blocks wait for read ready event - pub read: extern "C" fn(stream: *mut Stream, buf: *mut (), size: usize) -> isize, - pub close_read: extern "C" fn(stream: *mut Stream), - - pub write: extern "C" fn(stream: *mut Stream, buf: *mut (), size: usize) -> isize, - pub close_write: extern "C" fn(stream: *mut Stream), - // post WASI 0.3, CPB - // pub allocate: fn(stream: *mut ()) -> (*mut (), isize), - // pub publish: fn(stream: *mut (), size: usize), -} +// #[repr(C)] +// pub struct StreamVtable { +// // magic value for EOF(-1) and block(MIN) +// // asynchronous function, if this blocks wait for read ready event +// pub read: extern "C" fn(stream: *mut Stream, buf: *mut (), size: usize) -> isize, +// pub close_read: extern "C" fn(stream: *mut Stream), + +// pub write: extern "C" fn(stream: *mut Stream, buf: *mut (), size: usize) -> isize, +// pub close_write: extern "C" fn(stream: *mut Stream), +// // post WASI 0.3, CPB +// // pub allocate: fn(stream: *mut ()) -> (*mut (), isize), +// // pub publish: fn(stream: *mut (), size: usize), +// } #[repr(C)] pub struct Stream { - // pub vtable: *const StreamVtable, - pub read_ready_event_send: *mut (), - pub write_ready_event_send: *mut (), + read_ready_event_send: *mut (), + write_ready_event_send: *mut (), pub read_addr: AtomicPtr<()>, pub read_size: AtomicUsize, pub ready_size: AtomicIsize, @@ -88,6 +87,14 @@ pub mod stream { unsafe { activate_event_send_ptr(write_evt) }; results::BLOCKED } + + pub unsafe extern "C" fn read_ready_event(stream: *const Stream) -> *mut () { + unsafe { (&*stream).read_ready_event_send } + } + + pub unsafe extern "C" fn write_ready_event(stream: *const Stream) -> *mut () { + unsafe { (&*stream).write_ready_event_send } + } } // extern "C" fn write_impl(_stream: *mut Stream, _buf: *mut (), _size: usize) -> isize { From 7d5cfd3f45683e171959156b8571cf93fdc41531 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 Jan 2025 15:35:07 +0100 Subject: [PATCH 447/672] fully abstracted but sill unsafe stream operations --- .../tests/symmetric_stream/main/src/main.rs | 8 +-- .../tests/symmetric_stream/source/src/lib.rs | 34 +++++----- .../stream/src/stream_world.rs | 21 ++---- .../rust-client/src/async_support.rs | 65 ++++++++++++++----- 4 files changed, 76 insertions(+), 52 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index 1675b2e62..308b3c1ef 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -2,10 +2,10 @@ // use std::pin::pin; -use std::sync::atomic::Ordering; +// use std::sync::atomic::Ordering; use wit_bindgen_symmetric_rt::{ - async_support::{self, results, Stream}, + async_support::{self, Stream}, CallbackState, }; @@ -26,9 +26,7 @@ struct CallbackInfo { extern "C" fn ready(arg: *mut ()) -> CallbackState { let info = unsafe { &*arg.cast::() }; - let len = unsafe { &*info.stream } - .ready_size - .swap(results::BLOCKED, Ordering::Acquire); + let len = unsafe { async_support::stream::read_amount(info.stream) }; if len > 0 { for i in 0..len as usize { println!("data {}", info.data[i]); diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index ac7e96b08..45f640d5f 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -1,8 +1,7 @@ use std::sync::atomic::{AtomicU32, Ordering}; use wit_bindgen_symmetric_rt::{ - activate_event_send_ptr, - async_support::{self, results, Stream}, + async_support::{self, results, stream::Slice, Stream}, register, subscribe_event_send_ptr, CallbackState, EventSubscription, }; @@ -17,17 +16,19 @@ static COUNT: AtomicU32 = AtomicU32::new(1); extern "C" fn timer_call(data: *mut ()) -> CallbackState { let count = COUNT.fetch_add(1, Ordering::SeqCst); - let stream: *const Stream = data.cast(); + let stream: *mut Stream = data.cast(); if count <= 5 { - let size = unsafe { &*stream }.read_size.swap(0, Ordering::Acquire); - let addr = unsafe { &*stream } - .read_addr - .swap(core::ptr::null_mut(), Ordering::Relaxed); + let Slice { addr, size } = unsafe { async_support::stream::start_writing(stream) }; + // let size = unsafe { &*stream }.read_size.swap(0, Ordering::Acquire); + // let addr = unsafe { &*stream } + // .read_addr + // .swap(core::ptr::null_mut(), Ordering::Relaxed); assert!(size >= 1); *unsafe { &mut *addr.cast::() } = count; - let old_ready = unsafe { &*stream }.ready_size.swap(1, Ordering::Release); - assert_eq!(old_ready, results::BLOCKED); - unsafe { activate_event_send_ptr(async_support::stream::read_ready_event(stream)) }; + unsafe { async_support::stream::finish_writing(stream, 1) }; + // let old_ready = unsafe { &*stream }.ready_size.swap(1, Ordering::Release); + // assert_eq!(old_ready, results::BLOCKED); + // unsafe { activate_event_send_ptr(async_support::stream::read_ready_event(stream)) }; // let ms_30 = unsafe { // symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(30*1_000_000) // } as usize; @@ -41,13 +42,14 @@ extern "C" fn timer_call(data: *mut ()) -> CallbackState { extern "C" fn write_ready(data: *mut ()) -> CallbackState { let count = COUNT.load(Ordering::SeqCst); if count > 5 { - let stream: *const Stream = data.cast(); + let stream: *mut Stream = data.cast(); // EOF - let old_ready = unsafe { &*stream } - .ready_size - .swap(isize::MIN, Ordering::Release); - assert_eq!(old_ready, results::BLOCKED); - unsafe { activate_event_send_ptr(async_support::stream::read_ready_event(stream)) }; + unsafe { async_support::stream::finish_writing(stream, results::CLOSED) }; + // let old_ready = unsafe { &*stream } + // .ready_size + // .swap(isize::MIN, Ordering::Release); + // assert_eq!(old_ready, results::BLOCKED); + // unsafe { activate_event_send_ptr(async_support::stream::read_ready_event(stream)) }; CallbackState::Ready } else { if count == 1 { diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index b7f1aa60d..5457c9b22 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -257,11 +257,12 @@ pub mod exports { mod _rt { #![allow(dead_code, clippy::all)] pub mod stream_and_future_support { - use std::sync::atomic::Ordering; + // use std::sync::atomic::Ordering; use wit_bindgen_symmetric_rt::activate_event_send_ptr; use wit_bindgen_symmetric_rt::async_support; - use wit_bindgen_symmetric_rt::async_support::results; + // use wit_bindgen_symmetric_rt::async_support::results; + use wit_bindgen_symmetric_rt::async_support::stream::Slice; use wit_bindgen_symmetric_rt::async_support::wait_on; pub use wit_bindgen_symmetric_rt::async_support::Stream as WitStream; pub use wit_bindgen_symmetric_rt::async_support::StreamHandle2; @@ -713,10 +714,7 @@ mod _rt { // This is for reading // let ready = // unsafe { &*me.handle.0 }.ready_size.load(Ordering::SeqCst) != results::BLOCKED; - let ready = !unsafe { &*me.handle.0 } - .read_addr - .load(Ordering::SeqCst) - .is_null(); + let ready = unsafe { async_support::stream::is_ready_to_write(me.handle.0) }; // see also StreamReader::poll_next if !ready && me.future.is_none() { @@ -751,10 +749,7 @@ mod _rt { let item_len = item.len(); let me = self.get_mut(); let stream = me.handle.0; - let size = unsafe { &*stream }.read_size.swap(0, Ordering::Acquire); - let addr = unsafe { &*stream } - .read_addr - .swap(core::ptr::null_mut(), Ordering::Relaxed); + let Slice { addr, size } = unsafe { async_support::stream::start_writing(stream) }; assert!(size >= item_len); // let outptr = addr.cast::>(); let slice = unsafe { @@ -763,11 +758,7 @@ mod _rt { for (a, b) in slice.iter_mut().zip(item.drain(..)) { a.write(b); } - let old_ready = unsafe { &*stream } - .ready_size - .swap(item_len as isize, Ordering::Release); - assert_eq!(old_ready, results::BLOCKED); - unsafe { activate_event_send_ptr(async_support::stream::read_ready_event(stream)) }; + unsafe { async_support::stream::finish_writing(stream, item_len as isize) }; // assert!(self.future.is_none()); // async_support::with_entry(self.handle, |entry| match entry { // Entry::Vacant(_) => unreachable!(), diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 74cee0531..e21c9a9b8 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -1,6 +1,5 @@ -use futures::{channel::oneshot, task::Waker, FutureExt}; +use futures::{task::Waker, FutureExt}; use std::{ - any::Any, future::Future, pin::Pin, sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}, @@ -8,7 +7,6 @@ use std::{ }; use crate::{ - activate_event_send_ptr, module::symmetric::runtime::symmetric_executor::{ self, CallbackState, EventGenerator, EventSubscription, }, @@ -30,15 +28,15 @@ struct FutureState { waiting_for: Option, } -#[doc(hidden)] -pub enum Handle { - LocalOpen, - LocalReady(Box, Waker), - LocalWaiting(oneshot::Sender>), - LocalClosed, - Read, - Write, -} +// #[doc(hidden)] +// pub enum Handle { +// LocalOpen, +// LocalReady(Box, Waker), +// LocalWaiting(oneshot::Sender>), +// LocalClosed, +// Read, +// Write, +// } // #[repr(C)] // pub struct StreamVtable { @@ -54,13 +52,13 @@ pub enum Handle { // // pub publish: fn(stream: *mut (), size: usize), // } -#[repr(C)] +// #[repr(C)] pub struct Stream { read_ready_event_send: *mut (), write_ready_event_send: *mut (), - pub read_addr: AtomicPtr<()>, - pub read_size: AtomicUsize, - pub ready_size: AtomicIsize, + read_addr: AtomicPtr<()>, + read_size: AtomicUsize, + ready_size: AtomicIsize, } pub mod stream { @@ -95,6 +93,41 @@ pub mod stream { pub unsafe extern "C" fn write_ready_event(stream: *const Stream) -> *mut () { unsafe { (&*stream).write_ready_event_send } } + + pub unsafe extern "C" fn is_ready_to_write(stream: *const Stream) -> bool { + !unsafe { &*stream } + .read_addr + .load(Ordering::SeqCst) + .is_null() + } + + #[repr(C)] + pub struct Slice { + pub addr: *mut (), + pub size: usize, + } + + pub unsafe extern "C" fn start_writing(stream: *mut Stream) -> Slice { + let size = unsafe { &*stream }.read_size.swap(0, Ordering::Acquire); + let addr = unsafe { &*stream } + .read_addr + .swap(core::ptr::null_mut(), Ordering::Release); + Slice { addr, size } + } + + pub unsafe extern "C" fn read_amount(stream: *const Stream) -> isize { + unsafe { &*stream } + .ready_size + .swap(results::BLOCKED, Ordering::Acquire) + } + + pub unsafe extern "C" fn finish_writing(stream: *mut Stream, elements: isize) { + let old_ready = unsafe { &*stream } + .ready_size + .swap(elements as isize, Ordering::Release); + assert_eq!(old_ready, results::BLOCKED); + unsafe { activate_event_send_ptr(read_ready_event(stream)) }; + } } // extern "C" fn write_impl(_stream: *mut Stream, _buf: *mut (), _size: usize) -> isize { From d3873c86f60250af5a0f90a7b5b4295d7af24fab Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 Jan 2025 15:42:44 +0100 Subject: [PATCH 448/672] more nicely encapsulated --- .../tests/symmetric_stream/main/src/main.rs | 14 ++- .../stream/src/stream_world.rs | 2 +- .../rust-client/src/async_support.rs | 87 +++++++++---------- 3 files changed, 48 insertions(+), 55 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index 308b3c1ef..5bd6716bf 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -1,9 +1,3 @@ -// use wit_bindgen_symmetric_rt::{CallbackState, EventSubscription}; - -// use std::pin::pin; - -// use std::sync::atomic::Ordering; - use wit_bindgen_symmetric_rt::{ async_support::{self, Stream}, CallbackState, @@ -32,7 +26,11 @@ extern "C" fn ready(arg: *mut ()) -> CallbackState { println!("data {}", info.data[i]); } unsafe { - async_support::stream::read(info.stream, info.data.as_ptr().cast_mut().cast(), DATALEN); + async_support::stream::start_reading( + info.stream, + info.data.as_ptr().cast_mut().cast(), + DATALEN, + ); }; // call again CallbackState::Pending @@ -58,7 +56,7 @@ fn main() { data: [0, 0], }); unsafe { - async_support::stream::read(handle, info.data.as_mut_ptr().cast(), DATALEN); + async_support::stream::start_reading(handle, info.data.as_mut_ptr().cast(), DATALEN); }; let subscription = unsafe { wit_bindgen_symmetric_rt::subscribe_event_send_ptr(async_support::stream::read_ready_event( diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 5457c9b22..0eca13296 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -59,7 +59,7 @@ pub mod test { // #[link_name = "[async][stream-read-0]create"] // fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; // } - let poll_fn = async_support::stream::read; + let poll_fn = async_support::stream::start_reading; // &*((&*(stream.handle.0)).vtable) }.read; // match &stream.event { // SubscriptionType::None => { diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index e21c9a9b8..e05aa5d1b 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -2,7 +2,6 @@ use futures::{task::Waker, FutureExt}; use std::{ future::Future, pin::Pin, - sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}, task::{Context, Poll, RawWaker, RawWakerVTable}, }; @@ -28,16 +27,6 @@ struct FutureState { waiting_for: Option, } -// #[doc(hidden)] -// pub enum Handle { -// LocalOpen, -// LocalReady(Box, Waker), -// LocalWaiting(oneshot::Sender>), -// LocalClosed, -// Read, -// Write, -// } - // #[repr(C)] // pub struct StreamVtable { // // magic value for EOF(-1) and block(MIN) @@ -52,22 +41,32 @@ struct FutureState { // // pub publish: fn(stream: *mut (), size: usize), // } -// #[repr(C)] -pub struct Stream { - read_ready_event_send: *mut (), - write_ready_event_send: *mut (), - read_addr: AtomicPtr<()>, - read_size: AtomicUsize, - ready_size: AtomicIsize, -} +pub use stream::{results, Stream}; pub mod stream { - use std::sync::atomic::Ordering; + use std::sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}; - use super::{results, Stream}; - use crate::activate_event_send_ptr; + use crate::{activate_event_send_ptr, EventGenerator}; - pub unsafe extern "C" fn read(stream: *mut Stream, buf: *mut (), size: usize) -> isize { + pub mod results { + pub const BLOCKED: isize = -1; + pub const CLOSED: isize = isize::MIN; + pub const CANCELED: isize = 0; + } + + pub struct Stream { + read_ready_event_send: *mut (), + write_ready_event_send: *mut (), + read_addr: AtomicPtr<()>, + read_size: AtomicUsize, + ready_size: AtomicIsize, + } + + pub unsafe extern "C" fn start_reading( + stream: *mut Stream, + buf: *mut (), + size: usize, + ) -> isize { let old_ready = unsafe { &*stream }.ready_size.load(Ordering::SeqCst); if old_ready == results::CLOSED { return old_ready; @@ -128,6 +127,19 @@ pub mod stream { assert_eq!(old_ready, results::BLOCKED); unsafe { activate_event_send_ptr(read_ready_event(stream)) }; } + + impl Stream { + pub fn new() -> Self { + Self { + // vtable: &STREAM_VTABLE as *const StreamVtable, + read_ready_event_send: EventGenerator::new().take_handle() as *mut (), + write_ready_event_send: EventGenerator::new().take_handle() as *mut (), + read_addr: AtomicPtr::new(core::ptr::null_mut()), + read_size: AtomicUsize::new(0), + ready_size: AtomicIsize::new(results::BLOCKED), + } + } + } } // extern "C" fn write_impl(_stream: *mut Stream, _buf: *mut (), _size: usize) -> isize { @@ -150,19 +162,6 @@ pub mod stream { // close_write: write_close_impl, // }; -impl Stream { - pub fn new() -> Self { - Self { - // vtable: &STREAM_VTABLE as *const StreamVtable, - read_ready_event_send: EventGenerator::new().take_handle() as *mut (), - write_ready_event_send: EventGenerator::new().take_handle() as *mut (), - read_addr: AtomicPtr::new(core::ptr::null_mut()), - read_size: AtomicUsize::new(0), - ready_size: AtomicIsize::new(results::BLOCKED), - } - } -} - static VTABLE: RawWakerVTable = RawWakerVTable::new( |_| RawWaker::new(core::ptr::null(), &VTABLE), // `wake` does nothing @@ -280,12 +279,6 @@ pub fn spawn(future: impl Future + 'static + Send) { drop(wait_for); } -pub mod results { - pub const BLOCKED: isize = -1; - pub const CLOSED: isize = isize::MIN; - pub const CANCELED: isize = 0; -} - // Stream handles are Send, so wrap them #[repr(transparent)] pub struct StreamHandle2(pub *mut Stream); @@ -312,12 +305,14 @@ pub async unsafe fn await_stream_result( let result = import(stream_copy, address.0, count); match result { results::BLOCKED => { - let event = unsafe { subscribe_event_send_ptr((&*stream.0).read_ready_event_send) }; + let event = unsafe { subscribe_event_send_ptr(stream::read_ready_event(stream.0)) }; + // (&*stream.0).read_ready_event_send) }; event.reset(); wait_on(event).await; - let v = unsafe { &mut *stream.0 } - .ready_size - .swap(results::BLOCKED, Ordering::SeqCst); + let v = stream::read_amount(stream.0); + // unsafe { &mut *stream.0 } + // .ready_size + // .swap(results::BLOCKED, Ordering::SeqCst); if let results::CLOSED | results::CANCELED = v { None } else { From 7729012d51c250c650e188be943153fa230516ba Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 Jan 2025 23:24:27 +0100 Subject: [PATCH 449/672] code simplification --- .../tests/symmetric_stream/source/src/lib.rs | 60 ++++++------------- .../stream/src/stream_world.rs | 2 +- .../rust-client/src/async_support.rs | 22 ++++++- 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index 45f640d5f..dd3133315 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -1,67 +1,42 @@ use std::sync::atomic::{AtomicU32, Ordering}; use wit_bindgen_symmetric_rt::{ - async_support::{self, results, stream::Slice, Stream}, + async_support::{ + results, + stream::{self, Slice}, + Stream, + }, register, subscribe_event_send_ptr, CallbackState, EventSubscription, }; -#[link(name = "symmetric_executor")] -extern "C" { - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout( - nanoseconds: u64, - ) -> *mut (); -} - static COUNT: AtomicU32 = AtomicU32::new(1); extern "C" fn timer_call(data: *mut ()) -> CallbackState { - let count = COUNT.fetch_add(1, Ordering::SeqCst); + let count = COUNT.fetch_add(1, Ordering::AcqRel); let stream: *mut Stream = data.cast(); if count <= 5 { - let Slice { addr, size } = unsafe { async_support::stream::start_writing(stream) }; - // let size = unsafe { &*stream }.read_size.swap(0, Ordering::Acquire); - // let addr = unsafe { &*stream } - // .read_addr - // .swap(core::ptr::null_mut(), Ordering::Relaxed); + let Slice { addr, size } = unsafe { stream::start_writing(stream) }; assert!(size >= 1); *unsafe { &mut *addr.cast::() } = count; - unsafe { async_support::stream::finish_writing(stream, 1) }; - // let old_ready = unsafe { &*stream }.ready_size.swap(1, Ordering::Release); - // assert_eq!(old_ready, results::BLOCKED); - // unsafe { activate_event_send_ptr(async_support::stream::read_ready_event(stream)) }; - // let ms_30 = unsafe { - // symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(30*1_000_000) - // } as usize; - // assert_ne!(ms_30, 0); - // let event = unsafe { EventSubscription::from_handle(ms_30) }; - // register(event, timer_call, data); + unsafe { stream::finish_writing(stream, 1) }; } CallbackState::Ready } extern "C" fn write_ready(data: *mut ()) -> CallbackState { - let count = COUNT.load(Ordering::SeqCst); + let count = COUNT.load(Ordering::Acquire); if count > 5 { let stream: *mut Stream = data.cast(); // EOF - unsafe { async_support::stream::finish_writing(stream, results::CLOSED) }; - // let old_ready = unsafe { &*stream } - // .ready_size - // .swap(isize::MIN, Ordering::Release); - // assert_eq!(old_ready, results::BLOCKED); - // unsafe { activate_event_send_ptr(async_support::stream::read_ready_event(stream)) }; + unsafe { stream::finish_writing(stream, results::CLOSED) }; + unsafe { stream::close_write(stream) }; CallbackState::Ready } else { if count == 1 { println!("we can write now, starting timer"); } - let ms_30 = unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(30*1_000_000) - } as usize; - assert_ne!(ms_30, 0); - let event = unsafe { EventSubscription::from_handle(ms_30) }; - register(event, timer_call, data); - // this callback is done + let ms_30 = EventSubscription::from_timeout(30 * 1_000_000); + register(ms_30, timer_call, data); CallbackState::Pending } } @@ -72,10 +47,9 @@ pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate( _args: *mut u8, results: *mut u8, ) -> *mut u8 { - let obj = Box::new(Stream::new()); - let addr = Box::into_raw(obj); - let event = unsafe { subscribe_event_send_ptr(async_support::stream::write_ready_event(addr)) }; - register(event, write_ready, addr.cast()); - *unsafe { &mut *results.cast::<*mut Stream>() } = addr; + let stream = stream::create_stream(); + let event = unsafe { subscribe_event_send_ptr(stream::write_ready_event(stream)) }; + register(event, write_ready, stream.cast()); + *unsafe { &mut *results.cast::<*mut Stream>() } = stream; std::ptr::null_mut() } diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 0eca13296..186fb3b48 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -1122,7 +1122,7 @@ mod _rt { /// Creates a new Component Model `stream` with the specified payload type. pub fn new_stream() -> (StreamWriter, StreamReader) { - let handle = Box::into_raw(Box::new(WitStream::new())); + let handle = async_support::stream::create_stream(); ( StreamWriter::from_handle(handle), StreamReader::from_handle(handle), diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index e05aa5d1b..b76890891 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -60,6 +60,7 @@ pub mod stream { read_addr: AtomicPtr<()>, read_size: AtomicUsize, ready_size: AtomicIsize, + active_instances: AtomicUsize, } pub unsafe extern "C" fn start_reading( @@ -128,8 +129,26 @@ pub mod stream { unsafe { activate_event_send_ptr(read_ready_event(stream)) }; } + pub unsafe extern "C" fn close_read(stream: *mut Stream) { + let refs = unsafe { &mut *stream } + .active_instances + .fetch_sub(1, Ordering::AcqRel); + if refs == 1 { + drop(Box::from_raw(stream)); + } + } + + pub unsafe extern "C" fn close_write(stream: *mut Stream) { + // same for write (for now) + close_read(stream); + } + + pub extern "C" fn create_stream() -> *mut Stream { + Box::into_raw(Box::new(Stream::new())) + } + impl Stream { - pub fn new() -> Self { + fn new() -> Self { Self { // vtable: &STREAM_VTABLE as *const StreamVtable, read_ready_event_send: EventGenerator::new().take_handle() as *mut (), @@ -137,6 +156,7 @@ pub mod stream { read_addr: AtomicPtr::new(core::ptr::null_mut()), read_size: AtomicUsize::new(0), ready_size: AtomicIsize::new(results::BLOCKED), + active_instances: AtomicUsize::new(2), } } } From f2ffac3bfa47990ec2cbcb7453341c1840a82247 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 Jan 2025 23:32:08 +0100 Subject: [PATCH 450/672] more attempts removed --- .../stream/src/stream_world.rs | 43 ++++++------------- .../rust-client/src/async_support.rs | 26 ++--------- 2 files changed, 17 insertions(+), 52 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 186fb3b48..bf65e1224 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -12,9 +12,7 @@ pub mod test { super::super::super::__link_custom_section_describing_imports; // use _rt::stream_and_future_support::StreamHandle2; - use wit_bindgen_symmetric_rt::async_support::{ - self, AddressSend, StreamHandle2, StreamHandleRust, - }; + use wit_bindgen_symmetric_rt::async_support::{self, AddressSend, StreamHandle2}; use super::super::super::_rt; use _rt::stream_and_future_support::WitStream; @@ -32,7 +30,7 @@ pub mod test { } } - async fn write(_stream: &StreamHandleRust, _values: &[Self]) -> Option { + async fn write(_stream: &StreamHandle2, _values: &[Self]) -> Option { { // let address = values.as_ptr() as _; @@ -50,7 +48,7 @@ pub mod test { } async fn read( - stream: &StreamHandleRust, + stream: &StreamHandle2, values: &mut [::core::mem::MaybeUninit], ) -> Option { { @@ -80,7 +78,7 @@ pub mod test { let count = unsafe { ::wit_bindgen_symmetric_rt::async_support::await_stream_result( poll_fn, - StreamHandle2(stream.handle.0), + StreamHandle2(stream.0), address, values.len(), // &stream.event, @@ -257,27 +255,15 @@ pub mod exports { mod _rt { #![allow(dead_code, clippy::all)] pub mod stream_and_future_support { - // use std::sync::atomic::Ordering; - - use wit_bindgen_symmetric_rt::activate_event_send_ptr; - use wit_bindgen_symmetric_rt::async_support; - // use wit_bindgen_symmetric_rt::async_support::results; - use wit_bindgen_symmetric_rt::async_support::stream::Slice; - use wit_bindgen_symmetric_rt::async_support::wait_on; - pub use wit_bindgen_symmetric_rt::async_support::Stream as WitStream; - pub use wit_bindgen_symmetric_rt::async_support::StreamHandle2; - use wit_bindgen_symmetric_rt::async_support::StreamHandleRust; - use wit_bindgen_symmetric_rt::subscribe_event_send_ptr; - // use wit_bindgen_symmetric_rt::EventSubscription; + pub use wit_bindgen_symmetric_rt::async_support::{Stream as WitStream, StreamHandle2}; + use wit_bindgen_symmetric_rt::{ + activate_event_send_ptr, + async_support::{self, stream::Slice, wait_on}, + subscribe_event_send_ptr, + }; use { - futures::{ - // channel::oneshot, - future::FutureExt, - sink::Sink, - stream::Stream, - }, + futures::{future::FutureExt, sink::Sink, stream::Stream}, std::{ - // collections::hash_map::Entry, convert::Infallible, fmt, future::{Future, IntoFuture}, @@ -287,7 +273,6 @@ mod _rt { pin::Pin, task::{Context, Poll}, }, - // wit_bindgen_symmetric_rt::async_support::{self, Handle}, }; #[doc(hidden)] @@ -624,11 +609,11 @@ mod _rt { pub trait StreamPayload: Unpin + Sized + 'static { fn new() -> *mut WitStream; fn write( - stream: &StreamHandleRust, + stream: &StreamHandle2, values: &[Self], ) -> impl std::future::Future> + Send; fn read( - stream: &StreamHandleRust, + stream: &StreamHandle2, values: &mut [MaybeUninit], ) -> impl std::future::Future> + Send; fn cancel_write(stream: *mut WitStream); @@ -986,7 +971,7 @@ mod _rt { .take(ceiling(4 * 1024, mem::size_of::())) .collect::>(); // assert!(!event.ready()); - let stream_handle = StreamHandleRust { handle }; + let stream_handle = handle; let result = if let Some(count) = T::read(&stream_handle, &mut buffer).await { buffer.truncate(count); diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index b76890891..7f0462949 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -162,26 +162,6 @@ pub mod stream { } } -// extern "C" fn write_impl(_stream: *mut Stream, _buf: *mut (), _size: usize) -> isize { -// todo!() -// } - -// extern "C" fn read_close_impl(stream: *mut Stream) { -// read_impl(stream, core::ptr::null_mut(), 0); -// // deallocate? -// } - -// extern "C" fn write_close_impl(_stream: *mut Stream) { -// todo!() -// } - -// const STREAM_VTABLE: StreamVtable = StreamVtable { -// read: read_impl, -// close_read: read_close_impl, -// write: write_impl, -// close_write: write_close_impl, -// }; - static VTABLE: RawWakerVTable = RawWakerVTable::new( |_| RawWaker::new(core::ptr::null(), &VTABLE), // `wake` does nothing @@ -310,9 +290,9 @@ pub struct AddressSend(pub *mut ()); unsafe impl Send for AddressSend {} // unsafe impl Sync for StreamHandle2 {} -pub struct StreamHandleRust { - pub handle: StreamHandle2, -} +// pub struct StreamHandleRust { +// pub handle: StreamHandle2, +// } // this is used for reading? pub async unsafe fn await_stream_result( From b6ccc9a27cdab97f1f14351879b5ced815449e5c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 5 Jan 2025 23:54:43 +0100 Subject: [PATCH 451/672] memory leak is fixed --- .../tests/symmetric_stream/main/src/main.rs | 21 ++++----- .../stream/src/stream_world.rs | 36 +++++++++------ .../rust-client/src/async_support.rs | 45 +++++++------------ 3 files changed, 48 insertions(+), 54 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index 5bd6716bf..dc776d875 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -1,5 +1,5 @@ use wit_bindgen_symmetric_rt::{ - async_support::{self, Stream}, + async_support::{stream, Stream}, CallbackState, }; @@ -20,17 +20,13 @@ struct CallbackInfo { extern "C" fn ready(arg: *mut ()) -> CallbackState { let info = unsafe { &*arg.cast::() }; - let len = unsafe { async_support::stream::read_amount(info.stream) }; + let len = unsafe { stream::read_amount(info.stream) }; if len > 0 { for i in 0..len as usize { println!("data {}", info.data[i]); } unsafe { - async_support::stream::start_reading( - info.stream, - info.data.as_ptr().cast_mut().cast(), - DATALEN, - ); + stream::start_reading(info.stream, info.data.as_ptr().cast_mut().cast(), DATALEN); }; // call again CallbackState::Pending @@ -50,18 +46,16 @@ fn main() { }; // function should have completed (not async) assert!(continuation.is_null()); - let handle = result_stream.cast::(); + let stream = result_stream.cast::(); let mut info = Box::pin(CallbackInfo { - stream: handle, + stream, data: [0, 0], }); unsafe { - async_support::stream::start_reading(handle, info.data.as_mut_ptr().cast(), DATALEN); + stream::start_reading(stream, info.data.as_mut_ptr().cast(), DATALEN); }; let subscription = unsafe { - wit_bindgen_symmetric_rt::subscribe_event_send_ptr(async_support::stream::read_ready_event( - handle, - )) + wit_bindgen_symmetric_rt::subscribe_event_send_ptr(stream::read_ready_event(stream)) }; println!("Register read in main"); wit_bindgen_symmetric_rt::register( @@ -70,4 +64,5 @@ fn main() { (&*info as *const CallbackInfo).cast_mut().cast(), ); wit_bindgen_symmetric_rt::run(); + unsafe { stream::close_read(stream) }; } diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index bf65e1224..eb785cb09 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -215,16 +215,17 @@ pub mod exports { result.cast() } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn __callback_create( - ctx: *mut u8, - event0: i32, - event1: i32, - event2: i32, - ) -> i32 { - ::wit_bindgen_symmetric_rt::async_support::callback(ctx, event0, event1, event2) - } + // #[doc(hidden)] + // #[allow(non_snake_case)] + // pub unsafe fn __callback_create( + // ctx: *mut u8, + // event0: i32, + // event1: i32, + // event2: i32, + // ) -> i32 { + // todo!() + // // ::wit_bindgen_symmetric_rt::async_support::callback(ctx, event0, event1, event2) + // } pub trait Guest { fn create() -> impl ::core::future::Future< Output = _rt::stream_and_future_support::StreamReader, @@ -258,7 +259,11 @@ mod _rt { pub use wit_bindgen_symmetric_rt::async_support::{Stream as WitStream, StreamHandle2}; use wit_bindgen_symmetric_rt::{ activate_event_send_ptr, - async_support::{self, stream::Slice, wait_on}, + async_support::{ + self, results, + stream::{self, Slice}, + wait_on, + }, subscribe_event_send_ptr, }; use { @@ -845,8 +850,12 @@ mod _rt { // } // }, // }); - let stream = self.handle.0; - unsafe { activate_event_send_ptr(async_support::stream::read_ready_event(stream)) }; + if !unsafe { stream::is_write_closed(self.handle.0) } { + unsafe { + stream::finish_writing(self.handle.0, results::CLOSED); + } + } + unsafe { stream::close_write(self.handle.0) }; } } @@ -1080,6 +1089,7 @@ mod _rt { unsafe { activate_event_send_ptr(async_support::stream::write_ready_event(self.handle.0)) }; + unsafe { stream::close_read(self.handle.0) }; } } diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 7f0462949..a25b54d43 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -27,20 +27,6 @@ struct FutureState { waiting_for: Option, } -// #[repr(C)] -// pub struct StreamVtable { -// // magic value for EOF(-1) and block(MIN) -// // asynchronous function, if this blocks wait for read ready event -// pub read: extern "C" fn(stream: *mut Stream, buf: *mut (), size: usize) -> isize, -// pub close_read: extern "C" fn(stream: *mut Stream), - -// pub write: extern "C" fn(stream: *mut Stream, buf: *mut (), size: usize) -> isize, -// pub close_write: extern "C" fn(stream: *mut Stream), -// // post WASI 0.3, CPB -// // pub allocate: fn(stream: *mut ()) -> (*mut (), isize), -// // pub publish: fn(stream: *mut (), size: usize), -// } - pub use stream::{results, Stream}; pub mod stream { @@ -97,10 +83,14 @@ pub mod stream { pub unsafe extern "C" fn is_ready_to_write(stream: *const Stream) -> bool { !unsafe { &*stream } .read_addr - .load(Ordering::SeqCst) + .load(Ordering::Acquire) .is_null() } + pub unsafe extern "C" fn is_write_closed(stream: *const Stream) -> bool { + unsafe { &*stream }.ready_size.load(Ordering::Acquire) == results::CLOSED + } + #[repr(C)] pub struct Slice { pub addr: *mut (), @@ -134,7 +124,14 @@ pub mod stream { .active_instances .fetch_sub(1, Ordering::AcqRel); if refs == 1 { - drop(Box::from_raw(stream)); + let obj = Box::from_raw(stream); + drop(EventGenerator::from_handle( + obj.read_ready_event_send as usize, + )); + drop(EventGenerator::from_handle( + obj.write_ready_event_send as usize, + )); + drop(obj); } } @@ -268,10 +265,10 @@ pub async unsafe fn await_result( } } -#[doc(hidden)] -pub unsafe fn callback(_ctx: *mut u8, _event0: i32, _event1: i32, _event2: i32) -> i32 { - todo!() -} +// #[doc(hidden)] +// pub unsafe fn callback(_ctx: *mut u8, _event0: i32, _event1: i32, _event2: i32) -> i32 { +// todo!() +// } pub fn spawn(future: impl Future + 'static + Send) { let wait_for = first_poll(future, |()| ()); @@ -290,10 +287,6 @@ pub struct AddressSend(pub *mut ()); unsafe impl Send for AddressSend {} // unsafe impl Sync for StreamHandle2 {} -// pub struct StreamHandleRust { -// pub handle: StreamHandle2, -// } - // this is used for reading? pub async unsafe fn await_stream_result( import: unsafe extern "C" fn(*mut Stream, *mut (), usize) -> isize, @@ -306,13 +299,9 @@ pub async unsafe fn await_stream_result( match result { results::BLOCKED => { let event = unsafe { subscribe_event_send_ptr(stream::read_ready_event(stream.0)) }; - // (&*stream.0).read_ready_event_send) }; event.reset(); wait_on(event).await; let v = stream::read_amount(stream.0); - // unsafe { &mut *stream.0 } - // .ready_size - // .swap(results::BLOCKED, Ordering::SeqCst); if let results::CLOSED | results::CANCELED = v { None } else { From fe42b8d681e2705b27c91607dd82ae4613878917 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 6 Jan 2025 00:08:15 +0100 Subject: [PATCH 452/672] revise atomic and shorten path --- .../stream/src/stream_world.rs | 29 ++++------------ .../rust-client/src/async_support.rs | 2 +- crates/symmetric_executor/src/lib.rs | 33 ++++--------------- 3 files changed, 13 insertions(+), 51 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index eb785cb09..c3898e44b 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -701,10 +701,7 @@ mod _rt { fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let me = self.get_mut(); - // This is for reading - // let ready = - // unsafe { &*me.handle.0 }.ready_size.load(Ordering::SeqCst) != results::BLOCKED; - let ready = unsafe { async_support::stream::is_ready_to_write(me.handle.0) }; + let ready = unsafe { stream::is_ready_to_write(me.handle.0) }; // see also StreamReader::poll_next if !ready && me.future.is_none() { @@ -712,9 +709,7 @@ mod _rt { me.future = Some(Box::pin(async move { let handle_local = handle; let subscr = unsafe { - subscribe_event_send_ptr(async_support::stream::write_ready_event( - handle_local.0, - )) + subscribe_event_send_ptr(stream::write_ready_event(handle_local.0)) }; subscr.reset(); wait_on(subscr).await; @@ -739,16 +734,15 @@ mod _rt { let item_len = item.len(); let me = self.get_mut(); let stream = me.handle.0; - let Slice { addr, size } = unsafe { async_support::stream::start_writing(stream) }; + let Slice { addr, size } = unsafe { stream::start_writing(stream) }; assert!(size >= item_len); - // let outptr = addr.cast::>(); let slice = unsafe { std::slice::from_raw_parts_mut(addr.cast::>(), item_len) }; for (a, b) in slice.iter_mut().zip(item.drain(..)) { a.write(b); } - unsafe { async_support::stream::finish_writing(stream, item_len as isize) }; + unsafe { stream::finish_writing(stream, item_len as isize) }; // assert!(self.future.is_none()); // async_support::with_entry(self.handle, |entry| match entry { // Entry::Vacant(_) => unreachable!(), @@ -932,12 +926,9 @@ mod _rt { // }, // }); - // let subscr = - // unsafe { subscribe_event_send_ptr((&mut *handle).read_ready_event_send) }; Self { handle: StreamHandle2(handle), future: None, - // event: subscr, _phantom: PhantomData, } } @@ -958,7 +949,6 @@ mod _rt { // }); // ManuallyDrop::new(self).handle - // todo!() ManuallyDrop::new(self).handle.0 } } @@ -970,16 +960,11 @@ mod _rt { let me = self.get_mut(); if me.future.is_none() { - // assumption: future doesn't outlive stream let handle = StreamHandle2(me.handle.0); - // duplicate handle (same assumption) - // let event = me.event.dup(); - // unsafe { EventSubscription::from_handle(me.event.handle()) }; me.future = Some(Box::pin(async move { let mut buffer = iter::repeat_with(MaybeUninit::uninit) .take(ceiling(4 * 1024, mem::size_of::())) .collect::>(); - // assert!(!event.ready()); let stream_handle = handle; let result = if let Some(count) = T::read(&stream_handle, &mut buffer).await { @@ -1086,9 +1071,7 @@ mod _rt { //todo!() // let stream = self.handle.0; // let write_ready_evt = unsafe { &*stream }.write_ready_event_send; - unsafe { - activate_event_send_ptr(async_support::stream::write_ready_event(self.handle.0)) - }; + unsafe { activate_event_send_ptr(stream::write_ready_event(self.handle.0)) }; unsafe { stream::close_read(self.handle.0) }; } } @@ -1117,7 +1100,7 @@ mod _rt { /// Creates a new Component Model `stream` with the specified payload type. pub fn new_stream() -> (StreamWriter, StreamReader) { - let handle = async_support::stream::create_stream(); + let handle = stream::create_stream(); ( StreamWriter::from_handle(handle), StreamReader::from_handle(handle), diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index a25b54d43..859e53e02 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -54,7 +54,7 @@ pub mod stream { buf: *mut (), size: usize, ) -> isize { - let old_ready = unsafe { &*stream }.ready_size.load(Ordering::SeqCst); + let old_ready = unsafe { &*stream }.ready_size.load(Ordering::Acquire); if old_ready == results::CLOSED { return old_ready; } diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index ee34a7a0c..7019a1fe0 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -8,11 +8,7 @@ use std::{ time::{Duration, SystemTime}, }; -use executor::exports::symmetric::runtime::symmetric_executor::{ - self, - CallbackState, - // GuestEventSubscription, -}; +use executor::exports::symmetric::runtime::symmetric_executor::{self, CallbackState}; const DEBUGGING: bool = cfg!(feature = "trace"); const INVALID_FD: EventFd = -1; @@ -36,7 +32,6 @@ impl symmetric_executor::GuestEventSubscription for EventSubscription { let when = SystemTime::now() + Duration::from_nanos(nanoseconds); symmetric_executor::EventSubscription::new(EventSubscription { inner: EventType::SystemTime(when), - // callback: None, }) } @@ -66,18 +61,14 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { } fn subscribe(&self) -> symmetric_executor::EventSubscription { - // let event_fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }; if DEBUGGING { println!("subscribe({:x})", Arc::as_ptr(&self.0) as usize); } - // self.0.lock().unwrap().waiting.push(event_fd); symmetric_executor::EventSubscription::new(EventSubscription { inner: EventType::Triggered { last_counter: AtomicU32::new(0), - // event_fd, event: Arc::clone(&self.0), }, - // callback: None, }) } @@ -147,7 +138,7 @@ impl symmetric_executor::Guest for Guest { unsafe { libc::FD_ZERO(rfd_ptr) }; { let mut ex = EXECUTOR.lock().unwrap(); - let old_busy = EXECUTOR_BUSY.swap(true, Ordering::SeqCst); + let old_busy = EXECUTOR_BUSY.swap(true, Ordering::Acquire); assert!(!old_busy); ex.active_tasks.iter_mut().for_each(|task| { if task.inner.ready() { @@ -165,7 +156,6 @@ impl symmetric_executor::Guest for Guest { match &task.inner { EventType::Triggered { last_counter: _, - // event_fd, event: _, } => { unsafe { libc::FD_SET(task.event_fd, rfd_ptr) }; @@ -175,7 +165,7 @@ impl symmetric_executor::Guest for Guest { } EventType::SystemTime(system_time) => { if *system_time > now { - let diff = system_time.duration_since(now).unwrap_or_default(); //.as_micros(); + 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 @@ -183,7 +173,6 @@ impl symmetric_executor::Guest for Guest { { wait.tv_sec = secs; wait.tv_usec = usecs; - // timeoutindex = n; } tvptr = core::ptr::from_mut(&mut wait); } else { @@ -195,7 +184,7 @@ impl symmetric_executor::Guest for Guest { } } }); - let old_busy = EXECUTOR_BUSY.swap(false, Ordering::SeqCst); + let old_busy = EXECUTOR_BUSY.swap(false, Ordering::Release); assert!(old_busy); ex.active_tasks.retain(|task| task.callback.is_some()); { @@ -316,11 +305,10 @@ impl symmetric_executor::Guest for Guest { } } } - // subscr.callback.replace(CallbackEntry(cb, data)); match EXECUTOR.try_lock() { Ok(mut lock) => lock.active_tasks.push(subscr), Err(_err) => { - if EXECUTOR_BUSY.load(Ordering::Relaxed) { + if EXECUTOR_BUSY.load(Ordering::Acquire) { NEW_TASKS.lock().unwrap().push(subscr); } else { // actually this is unlikely, but give it a try @@ -363,7 +351,6 @@ impl EventSubscription { // event_fd, event, } => { - // let new_event_fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }; let new_event = Arc::clone(event); let last_counter = last_counter_old.load(Ordering::Relaxed); if DEBUGGING { @@ -372,19 +359,14 @@ impl EventSubscription { Arc::as_ptr(&event) as usize ); } - // new_event.lock().unwrap().waiting.push(new_event_fd); EventType::Triggered { last_counter: AtomicU32::new(last_counter), - // event_fd: new_event_fd, event: new_event, } } EventType::SystemTime(system_time) => EventType::SystemTime(*system_time), }; - EventSubscription { - inner, - // callback: None, - } + EventSubscription { inner } } } @@ -401,7 +383,6 @@ impl Drop for QueuedEvent { match &self.inner { EventType::Triggered { last_counter: _, - // event_fd, event, } => { if DEBUGGING { @@ -422,7 +403,6 @@ impl Drop for QueuedEvent { enum EventType { Triggered { last_counter: AtomicU32, - // event_fd: EventFd, event: Arc>, }, SystemTime(SystemTime), @@ -433,7 +413,6 @@ impl EventType { match self { EventType::Triggered { last_counter, - // event_fd: _, event, } => { let current_counter = event.lock().unwrap().counter; From c8779b5c6f832be08905c95ea8218377c259ac5d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 6 Jan 2025 00:10:47 +0100 Subject: [PATCH 453/672] fix warning --- crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index c3898e44b..bdf3895cf 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -260,7 +260,7 @@ mod _rt { use wit_bindgen_symmetric_rt::{ activate_event_send_ptr, async_support::{ - self, results, + results, stream::{self, Slice}, wait_on, }, From 203428190c3ca347ed0d3f8268458e14c8c27f1f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 7 Jan 2025 22:59:44 +0100 Subject: [PATCH 454/672] document and simplify the async calling convention --- crates/cpp/tests/symmetric_async/README.md | 26 ++++++++ .../async_module/src/async_module.rs | 61 +++---------------- .../tests/symmetric_async/main/src/main.rs | 15 ++--- .../tests/symmetric_async/sleep/src/lib.rs | 7 ++- crates/cpp/tests/symmetric_stream/README.md | 24 +++++--- .../rust-client/src/async_support.rs | 9 +-- 6 files changed, 66 insertions(+), 76 deletions(-) create mode 100644 crates/cpp/tests/symmetric_async/README.md 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_module/src/async_module.rs b/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs index 5d0cd4cc3..96b509285 100644 --- a/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs +++ b/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs @@ -11,34 +11,21 @@ pub mod test { 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 sleep(nanoseconds: u64) -> () { unsafe { - //let layout0 = _rt::alloc::Layout::from_size_align_unchecked(8, 8); - let ptr0 = (&nanoseconds) as *const u64; - //_rt::alloc::alloc(layout0); - // *ptr0.add(0).cast::() = _rt::as_i64(&nanoseconds); - // let layout1 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); - let ptr1 = core::ptr::null_mut(); - // _rt::alloc::alloc(layout1); - #[link(wasm_import_module = "test:test/wait")] #[link(name = "sleep")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "[async]sleep")] - fn testX3AtestX2FwaitX00X5BasyncX5Dsleep(_: *mut u8, _: *mut u8) - -> *mut u8; + fn testX3AtestX2FwaitX00X5BasyncX5Dsleep(_: u64) -> *mut u8; } - // let layout2 = _rt::alloc::Layout::from_size_align_unchecked(8, 8); ::wit_bindgen_symmetric_rt::async_support::await_result( - testX3AtestX2FwaitX00X5BasyncX5Dsleep, - // layout2, - ptr0.cast_mut().cast(), - ptr1, + move || unsafe { testX3AtestX2FwaitX00X5BasyncX5Dsleep(nanoseconds) }, // layout2, + // ptr0.cast_mut().cast(), + // ptr1, ) .await; - // _rt::cabi_dealloc(ptr1, 0, 1); } } } @@ -61,13 +48,13 @@ pub mod exports { #[allow(non_snake_case)] pub unsafe fn _export_forward_cabi( arg0: *mut u8, + arg1: usize, arg2: *mut u8, ) -> *mut u8 { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); - let arguments = arg0.cast_const().cast::(); - let len0 = unsafe { *(arguments.add(1)) }; - let addr0 = unsafe { *arguments } as *mut u8; + let len0 = arg1; + let addr0 = arg0; let string0 = String::from( std::str::from_utf8(std::slice::from_raw_parts(addr0, len0)).unwrap(), ); @@ -82,37 +69,11 @@ pub mod exports { let output = arg2.cast::(); *unsafe { &mut *output } = ptr3 as usize; *unsafe { &mut *output.add(1) } = len3; - - // #[link(wasm_import_module = "[export]test:test/string-delay")] - // extern "C" { - // #[cfg_attr( - // target_arch = "wasm32", - // link_name = "[task-return]forward" - // )] - // fn X5BexportX5DtestX3AtestX2Fstring_delayX00X5Btask_returnX5Dforward( - // _: *mut u8, - // _: usize, - // ); - // } - // X5BexportX5DtestX3AtestX2Fstring_delayX00X5Btask_returnX5Dforward( - // ptr3.cast_mut(), - // len3, - // ); }, ); result.cast() } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn __callback_forward( - ctx: *mut u8, - event0: i32, - event1: i32, - event2: i32, - ) -> i32 { - ::wit_bindgen_symmetric_rt::async_support::callback(ctx, event0, event1, event2) - } pub trait Guest { fn forward( s: _rt::String, @@ -125,13 +86,9 @@ pub mod exports { #[cfg_attr(target_arch = "wasm32", export_name = "forward")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn X5BasyncX5DtestX3AtestX2Fstring_delayX00forward(arg0: *mut u8,arg1: *mut u8,) -> *mut u8 { - $($path_to_types)*::_export_forward_cabi::<$ty>(arg0, arg1,) + unsafe extern "C" fn X5BasyncX5DtestX3AtestX2Fstring_delayX00forward(arg0: *mut u8,arg1:usize,arg2: *mut u8,) -> *mut u8 { + $($path_to_types)*::_export_forward_cabi::<$ty>(arg0, arg1,arg2) } - // #[export_name = "[callback]forward"] - // unsafe extern "C" fn _callback_forward(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { - // $($path_to_types)*::__callback_forward(ctx, event0, event1, event2) - // } };); } #[doc(hidden)] diff --git a/crates/cpp/tests/symmetric_async/main/src/main.rs b/crates/cpp/tests/symmetric_async/main/src/main.rs index 2bfef7dbb..72c326d20 100644 --- a/crates/cpp/tests/symmetric_async/main/src/main.rs +++ b/crates/cpp/tests/symmetric_async/main/src/main.rs @@ -3,7 +3,8 @@ use wit_bindgen_symmetric_rt::{CallbackState, EventSubscription}; #[link(name = "async_module")] extern "C" { pub fn X5BasyncX5DtestX3AtestX2Fstring_delayX00forward( - args: *const (), + addr: *const u8, + len: usize, results: *mut (), ) -> *mut (); } @@ -19,11 +20,11 @@ extern "C" fn print_result(obj: *mut ()) -> CallbackState { } fn main() { - let argument1: [usize; 2] = ["A".as_ptr() as usize, 1]; let mut result1: [usize; 2] = [0, 0]; let handle1 = unsafe { X5BasyncX5DtestX3AtestX2Fstring_delayX00forward( - (&argument1 as *const usize).cast(), + "A".as_ptr(), + 1, result1.as_mut_ptr().cast(), ) }; @@ -32,11 +33,11 @@ fn main() { let string = std::str::from_utf8(&vec).unwrap(); println!("Result {string}"); - let argument2: [usize; 2] = ["B".as_ptr() as usize, 1]; let mut result2: [usize; 2] = [0, 0]; let handle2 = unsafe { X5BasyncX5DtestX3AtestX2Fstring_delayX00forward( - (&argument2 as *const usize).cast(), + "B".as_ptr(), + 1, result2.as_mut_ptr().cast(), ) }; @@ -47,11 +48,11 @@ fn main() { result2.as_mut_ptr().cast(), ); - let argument3: [usize; 2] = ["C".as_ptr() as usize, 1]; let mut result3: [usize; 2] = [0, 0]; let handle3 = unsafe { X5BasyncX5DtestX3AtestX2Fstring_delayX00forward( - (&argument3 as *const usize).cast(), + "C".as_ptr(), + 1, result3.as_mut_ptr().cast(), ) }; diff --git a/crates/cpp/tests/symmetric_async/sleep/src/lib.rs b/crates/cpp/tests/symmetric_async/sleep/src/lib.rs index 2d5ba58e8..565ac1eda 100644 --- a/crates/cpp/tests/symmetric_async/sleep/src/lib.rs +++ b/crates/cpp/tests/symmetric_async/sleep/src/lib.rs @@ -7,9 +7,10 @@ extern "C" { #[no_mangle] unsafe extern "C" fn testX3AtestX2FwaitX00X5BasyncX5Dsleep( - args: *const (), - _results: *mut (), + nanoseconds: u64, + // args: *const (), + // _results: *mut (), ) -> *mut () { - let nanoseconds = *args.cast::(); + // let nanoseconds = *args.cast::(); symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(nanoseconds) } diff --git a/crates/cpp/tests/symmetric_stream/README.md b/crates/cpp/tests/symmetric_stream/README.md index 92e318388..4382d75c0 100644 --- a/crates/cpp/tests/symmetric_stream/README.md +++ b/crates/cpp/tests/symmetric_stream/README.md @@ -7,6 +7,7 @@ A stream has the following members: - 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 @@ -14,7 +15,7 @@ A stream has the following members: - BLOCKED: -1 (normal) - CANCELLED: 0 (TBD) -## Seqence +## Sequence "take" means swap with idle value (read_addr=0, read_size=0, ready=-1) @@ -42,15 +43,16 @@ A stream has the following members: A vtable is no longer necessary, but some functions enable shared implementations (perhaps interface by WIT?) - - create stream - - read (waits) - - start_write (wait and returns buffer) - - finish (can also set eof independently of start_write) - -Perhaps: - - - close_read (read with NULL?) - - close_write (=finish(EOF)?) + - 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 @@ -64,3 +66,5 @@ Perhaps: - 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/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 859e53e02..d3f99c402 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -254,11 +254,12 @@ pub fn first_poll( /// Await the completion of a call to an async-lowered import. #[doc(hidden)] pub async unsafe fn await_result( - function: unsafe extern "C" fn(*mut u8, *mut u8) -> *mut u8, - params: *mut u8, - results: *mut u8, + function: impl Fn() -> *mut u8, + // unsafe extern "C" fn(*mut u8, *mut u8) -> *mut u8, + // params: *mut u8, + // results: *mut u8, ) { - let wait_for = function(params, results); + 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; From a281ce3e13e7eff67068451d04310e33005f6f55 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 7 Jan 2025 23:05:00 +0100 Subject: [PATCH 455/672] apply async calling simplification to stream example --- .../cpp/tests/symmetric_stream/main/src/main.rs | 4 ++-- .../cpp/tests/symmetric_stream/source/src/lib.rs | 2 +- .../symmetric_stream/stream/src/stream_world.rs | 16 ++++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index dc776d875..7ea325c7b 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -6,7 +6,7 @@ use wit_bindgen_symmetric_rt::{ #[link(name = "stream")] extern "C" { pub fn testX3AtestX2Fstream_testX00X5BasyncX5Dcreate( - args: *const (), + // args: *const (), results: *mut (), ) -> *mut (); } @@ -40,7 +40,7 @@ fn main() { let mut result_stream: *mut () = core::ptr::null_mut(); let continuation = unsafe { testX3AtestX2Fstream_testX00X5BasyncX5Dcreate( - core::ptr::null_mut(), + // core::ptr::null_mut(), (&mut result_stream as *mut *mut ()).cast(), ) }; diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index dd3133315..2d8aea8e5 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -44,7 +44,7 @@ extern "C" fn write_ready(data: *mut ()) -> CallbackState { #[allow(non_snake_case)] #[no_mangle] pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate( - _args: *mut u8, + // _args: *mut u8, results: *mut u8, ) -> *mut u8 { let stream = stream::create_stream(); diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index bdf3895cf..348decf1e 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -146,7 +146,7 @@ pub mod test { pub async fn create() -> _rt::stream_and_future_support::StreamReader { unsafe { // let layout0 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); - let ptr0 = core::ptr::null_mut(); //_rt::alloc::alloc(layout0); + // let ptr0 = core::ptr::null_mut(); //_rt::alloc::alloc(layout0); let layout1 = _rt::alloc::Layout::from_size_align_unchecked( core::mem::size_of::<*const u8>(), core::mem::size_of::<*const u8>(), @@ -158,15 +158,15 @@ pub mod test { extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "[async]create")] fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate( - _: *mut u8, + // _: *mut u8, _: *mut u8, ) -> *mut u8; } // let layout2 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); ::wit_bindgen_symmetric_rt::async_support::await_result( - testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate, - ptr0, - ptr1, + move || unsafe {testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(ptr1)}, + // ptr0, + // ptr1, ) .await; let l3 = *ptr1.add(0).cast::<*mut u8>(); @@ -199,7 +199,7 @@ pub mod exports { #[doc(hidden)] #[allow(non_snake_case)] pub unsafe fn _export_create_cabi( - _args: *mut u8, + // _args: *mut u8, results: *mut u8, ) -> *mut u8 { #[cfg(target_arch = "wasm32")] @@ -238,8 +238,8 @@ pub mod exports { #[cfg_attr(target_arch = "wasm32", export_name = "create")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn testX3AtestX2Fstream_testX00X5BasyncX5Dcreate(args: *mut u8, results: *mut u8) -> *mut u8 { - $($path_to_types)*::_export_create_cabi::<$ty>(args,results) + unsafe extern "C" fn testX3AtestX2Fstream_testX00X5BasyncX5Dcreate(results: *mut u8) -> *mut u8 { + $($path_to_types)*::_export_create_cabi::<$ty>(results) } // #[export_name = "[callback]create"] // unsafe extern "C" fn _callback_create(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { From c717537a971a29dfd137e3852d06c0ccc997ae0a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 9 Jan 2025 00:46:33 +0100 Subject: [PATCH 456/672] post merge fixes --- Cargo.lock | 84 ++++++++++++++++++++++------------- Cargo.toml | 2 +- crates/bridge/src/lib.rs | 1 + crates/core/src/abi.rs | 16 +++---- crates/cpp/src/lib.rs | 11 +++-- crates/csharp/src/function.rs | 56 +++++++++++++---------- crates/rust/src/bindgen.rs | 17 ------- 7 files changed, 104 insertions(+), 83 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 69577f89d..6b31af22a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1509,7 +1509,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.222.0", + "wasm-encoder 0.222.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-core", "wit-component", "wit-parser 0.222.0", @@ -1795,23 +1795,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3432682105d7e994565ef928ccf5856cf6af4ba3dddebedb737f61caed70f956" dependencies = [ "leb128", - "wasmparser 0.222.0", + "wasmparser 0.222.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-encoder" -version = "0.221.2" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" +version = "0.222.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6998c9b5f21eb24c5c53e99621665de8f874098c" dependencies = [ "leb128", - "wasmparser 0.221.2", + "wasmparser 0.222.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] name = "wasm-metadata" version = "0.222.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb09677e9412cb0dca2fec2cfdb49e5dfe8d82bf869c6d031bdf2ab73324f9d" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6998c9b5f21eb24c5c53e99621665de8f874098c" dependencies = [ "anyhow", "indexmap", @@ -1820,8 +1819,8 @@ dependencies = [ "serde_json", "spdx", "url", - "wasm-encoder 0.222.0", - "wasmparser 0.222.0", + "wasm-encoder 0.222.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.222.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -1843,6 +1842,16 @@ name = "wasmparser" version = "0.222.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4adf50fde1b1a49c1add6a80d47aea500c88db70551805853aa8b88f3ea27ab5" +dependencies = [ + "bitflags", + "indexmap", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.222.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6998c9b5f21eb24c5c53e99621665de8f874098c" dependencies = [ "bitflags", "hashbrown 0.15.2", @@ -1914,7 +1923,7 @@ dependencies = [ "wasmtime-slab", "wasmtime-versioned-export-macros", "wasmtime-winch", - "wat 1.220.0", + "wat 1.222.0 (registry+https://github.com/rust-lang/crates.io-index)", "windows-sys 0.52.0", ] @@ -2169,19 +2178,19 @@ dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.222.0", + "wasm-encoder 0.222.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wast" -version = "221.0.2" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" +version = "222.0.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6998c9b5f21eb24c5c53e99621665de8f874098c" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.221.2", + "wasm-encoder 0.222.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2190,15 +2199,15 @@ version = "1.222.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fde61b4b52f9a84ae31b5e8902a2cd3162ea45d8bf564c729c3288fe52f4334" dependencies = [ - "wast 222.0.0", + "wast 222.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wat" -version = "1.221.2" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" +version = "1.222.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6998c9b5f21eb24c5c53e99621665de8f874098c" dependencies = [ - "wast 221.0.2", + "wast 222.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2428,7 +2437,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.222.0", + "wasm-encoder 0.222.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -2443,8 +2452,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.222.0", - "wasmparser 0.222.0", + "wasm-encoder 0.222.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.222.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasmtime", "wasmtime-wasi", "wit-bindgen-bridge", @@ -2470,6 +2479,21 @@ dependencies = [ "wit-parser 0.222.0", ] +[[package]] +name = "wit-bindgen-cpp" +version = "0.3.0" +dependencies = [ + "anyhow", + "clap", + "heck 0.5.0", + "test-helpers", + "wasm-encoder 0.222.0 (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.36.0" @@ -2479,9 +2503,9 @@ dependencies = [ "heck 0.5.0", "indexmap", "test-helpers", - "wasm-encoder 0.222.0", + "wasm-encoder 0.222.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", - "wasmparser 0.222.0", + "wasmparser 0.222.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-core", "wit-component", "wit-parser 0.222.0", @@ -2580,8 +2604,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.222.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7ce0f2820a7fdca09e9a0cbbd39513abf2d722479b28c73d81bccd61bb21499" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6998c9b5f21eb24c5c53e99621665de8f874098c" dependencies = [ "anyhow", "bitflags", @@ -2590,10 +2613,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.222.0", + "wasm-encoder 0.222.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", - "wasmparser 0.222.0", - "wat", + "wasmparser 0.222.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wat 1.222.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-parser 0.222.0", ] @@ -2618,8 +2641,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.222.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533abd14901514db88e4107655345fdd8ab8a72fb61e85d871bd361509744c35" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6998c9b5f21eb24c5c53e99621665de8f874098c" dependencies = [ "anyhow", "id-arena", @@ -2630,7 +2652,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.222.0", + "wasmparser 0.222.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 15d0802b2..4f9b629a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,7 +78,7 @@ default = [ 'markdown', 'teavm-java', 'go', - 'csharp', + # 'csharp', 'cpp', 'bridge', 'moonbit', diff --git a/crates/bridge/src/lib.rs b/crates/bridge/src/lib.rs index 9cd2dde3c..dd39c83db 100644 --- a/crates/bridge/src/lib.rs +++ b/crates/bridge/src/lib.rs @@ -217,6 +217,7 @@ impl<'a> BridgeInterfaceGenerator<'a> { 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; diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index f5ffdfd7a..4258b4754 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -975,7 +975,9 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); self.stack.pop().unwrap() } - AbiVariant::GuestImportAsync | AbiVariant::GuestExportAsync => { + AbiVariant::GuestImportAsync + | AbiVariant::GuestExportAsync + | AbiVariant::GuestExportAsyncStackful => { unreachable!() } }; @@ -1216,13 +1218,11 @@ impl<'a, B: Bindgen> Generator<'a, B> { 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!( diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 4d54bd361..7538126a8 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1511,6 +1511,7 @@ impl CppInterfaceGenerator<'_> { 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); @@ -2220,6 +2221,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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) => { @@ -2512,15 +2514,15 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> ) { todo!() } - + fn type_future(&mut self, _id: TypeId, _name: &str, _ty: &Option, _docs: &Docs) { todo!() } - + fn type_stream(&mut self, _id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) { todo!() } - + fn type_error_context(&mut self, _id: TypeId, _name: &str, _docs: &Docs) { todo!() } @@ -3125,6 +3127,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } AbiVariant::GuestImportAsync => todo!(), AbiVariant::GuestExportAsync => todo!(), + AbiVariant::GuestExportAsyncStackful => todo!(), }, (Handle::Own(ty), false) => match self.variant { AbiVariant::GuestImport => { @@ -3149,6 +3152,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } AbiVariant::GuestImportAsync => todo!(), AbiVariant::GuestExportAsync => todo!(), + AbiVariant::GuestExportAsyncStackful => todo!(), }, (Handle::Borrow(ty), true) => { let tname = self.gen.type_name( @@ -3170,6 +3174,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } AbiVariant::GuestImportAsync => todo!(), AbiVariant::GuestExportAsync => todo!(), + AbiVariant::GuestExportAsyncStackful => todo!(), }, } } diff --git a/crates/csharp/src/function.rs b/crates/csharp/src/function.rs index faa6aad35..8bcff9976 100644 --- a/crates/csharp/src/function.rs +++ b/crates/csharp/src/function.rs @@ -8,7 +8,10 @@ use std::ops::Deref; use wit_bindgen_core::abi::{Bindgen, Bitcast, Instruction}; use wit_bindgen_core::{uwrite, uwriteln, Direction, Ns}; use wit_parser::abi::WasmType; -use wit_parser::{Docs, FunctionKind, Handle, Resolve, SizeAlign, Type, TypeDefKind, TypeId}; +use wit_parser::{ + Alignment, ArchitectureSize, Docs, FunctionKind, Handle, Resolve, SizeAlign, Type, TypeDefKind, + TypeId, +}; /// FunctionBindgen generates the C# code for calling functions defined in wit pub(crate) struct FunctionBindgen<'a, 'b> { @@ -254,22 +257,22 @@ impl Bindgen for FunctionBindgen<'_, '_> { })), Instruction::I32Load { offset } | Instruction::PointerLoad { offset } - | Instruction::LengthLoad { offset } => results.push(format!("BitConverter.ToInt32(new Span((void*)({} + {offset}), 4))",operands[0])), - Instruction::I32Load8U { offset } => results.push(format!("new Span((void*)({} + {offset}), 1)[0]",operands[0])), - Instruction::I32Load8S { offset } => results.push(format!("(sbyte)new Span((void*)({} + {offset}), 1)[0]",operands[0])), - Instruction::I32Load16U { offset } => results.push(format!("BitConverter.ToUInt16(new Span((void*)({} + {offset}), 2))",operands[0])), - Instruction::I32Load16S { offset } => results.push(format!("BitConverter.ToInt16(new Span((void*)({} + {offset}), 2))",operands[0])), - Instruction::I64Load { offset } => results.push(format!("BitConverter.ToInt64(new Span((void*)({} + {offset}), 8))",operands[0])), - Instruction::F32Load { offset } => results.push(format!("BitConverter.ToSingle(new Span((void*)({} + {offset}), 4))",operands[0])), - Instruction::F64Load { offset } => results.push(format!("BitConverter.ToDouble(new Span((void*)({} + {offset}), 8))",operands[0])), + | Instruction::LengthLoad { offset } => results.push(format!("BitConverter.ToInt32(new Span((void*)({} + {offset}), 4))",operands[0],offset = offset.size_wasm32())), + Instruction::I32Load8U { offset } => results.push(format!("new Span((void*)({} + {offset}), 1)[0]",operands[0],offset = offset.size_wasm32())), + Instruction::I32Load8S { offset } => results.push(format!("(sbyte)new Span((void*)({} + {offset}), 1)[0]",operands[0],offset = offset.size_wasm32())), + Instruction::I32Load16U { offset } => results.push(format!("BitConverter.ToUInt16(new Span((void*)({} + {offset}), 2))",operands[0],offset = offset.size_wasm32())), + Instruction::I32Load16S { offset } => results.push(format!("BitConverter.ToInt16(new Span((void*)({} + {offset}), 2))",operands[0],offset = offset.size_wasm32())), + Instruction::I64Load { offset } => results.push(format!("BitConverter.ToInt64(new Span((void*)({} + {offset}), 8))",operands[0],offset = offset.size_wasm32())), + Instruction::F32Load { offset } => results.push(format!("BitConverter.ToSingle(new Span((void*)({} + {offset}), 4))",operands[0],offset = offset.size_wasm32())), + Instruction::F64Load { offset } => results.push(format!("BitConverter.ToDouble(new Span((void*)({} + {offset}), 8))",operands[0],offset = offset.size_wasm32())), Instruction::I32Store { offset } | Instruction::PointerStore { offset } - | Instruction::LengthStore { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 4), {});", operands[1], operands[0]), - Instruction::I32Store8 { offset } => uwriteln!(self.src, "*(byte*)({} + {offset}) = (byte){};", operands[1], operands[0]), - Instruction::I32Store16 { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 2), (short){});", operands[1], operands[0]), - Instruction::I64Store { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 8), unchecked((long){}));", operands[1], operands[0]), - Instruction::F32Store { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 4), unchecked((float){}));", operands[1], operands[0]), - Instruction::F64Store { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 8), unchecked((double){}));", operands[1], operands[0]), + | Instruction::LengthStore { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 4), {});", operands[1], operands[0],offset = offset.size_wasm32()), + Instruction::I32Store8 { offset } => uwriteln!(self.src, "*(byte*)({} + {offset}) = (byte){};", operands[1], operands[0],offset = offset.size_wasm32()), + Instruction::I32Store16 { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 2), (short){});", operands[1], operands[0],offset = offset.size_wasm32()), + Instruction::I64Store { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 8), unchecked((long){}));", operands[1], operands[0],offset = offset.size_wasm32()), + Instruction::F32Store { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 4), unchecked((float){}));", operands[1], operands[0],offset = offset.size_wasm32()), + Instruction::F64Store { offset } => uwriteln!(self.src, "BitConverter.TryWriteBytes(new Span((void*)({} + {offset}), 8), unchecked((double){}));", operands[1], operands[0],offset = offset.size_wasm32()), Instruction::I64FromU64 => results.push(format!("unchecked((long)({}))", operands[0])), Instruction::I32FromChar => results.push(format!("((int){})", operands[0])), @@ -1115,7 +1118,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } } - fn return_pointer(&mut self, size: usize, align: usize) -> String { + fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> String { let ptr = self.locals.tmp("ptr"); // Use a stack-based return area for imports, because exports need @@ -1123,9 +1126,10 @@ impl Bindgen for FunctionBindgen<'_, '_> { match self.interface_gen.direction { Direction::Import => { self.import_return_pointer_area_size = - self.import_return_pointer_area_size.max(size); - self.import_return_pointer_area_align = - self.import_return_pointer_area_align.max(align); + self.import_return_pointer_area_size.max(size.size_wasm32()); + self.import_return_pointer_area_align = self + .import_return_pointer_area_align + .max(align.align_wasm32()); let (array_size, element_type) = crate::world_generator::dotnet_aligned_array( self.import_return_pointer_area_size, self.import_return_pointer_area_align, @@ -1150,10 +1154,16 @@ impl Bindgen for FunctionBindgen<'_, '_> { format!("{ptr}") } Direction::Export => { - self.interface_gen.csharp_gen.return_area_size = - self.interface_gen.csharp_gen.return_area_size.max(size); - self.interface_gen.csharp_gen.return_area_align = - self.interface_gen.csharp_gen.return_area_align.max(align); + self.interface_gen.csharp_gen.return_area_size = self + .interface_gen + .csharp_gen + .return_area_size + .max(size.size_wasm32()); + self.interface_gen.csharp_gen.return_area_align = self + .interface_gen + .csharp_gen + .return_area_align + .max(align.align_wasm32()); uwrite!( self.src, diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 2bc7c2372..249752cc9 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -974,23 +974,6 @@ impl Bindgen for FunctionBindgen<'_, '_> { ); } - Instruction::AsyncCallWasm { name, size, align } => { - let func = self.declare_import(name, &[WasmType::Pointer; 2], &[WasmType::I32]); - - let async_support = self.gen.path_to_async_support(); - let tmp = self.tmp(); - let layout = format!("layout{tmp}"); - let alloc = self.gen.path_to_std_alloc_module(); - self.push_str(&format!( - "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align});\n", - )); - let operands = operands.join(", "); - uwriteln!( - self.src, - "{async_support}::await_result({func}, {layout}, {operands}).await;" - ); - } - Instruction::CallInterface { func, .. } => { if self.async_ { let tmp = self.tmp(); From 2bc07efbb4b66495d6bad77707f1ebee1f00f657 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 9 Jan 2025 00:58:13 +0100 Subject: [PATCH 457/672] update wasm-tools --- crates/cpp/tests/symmetric_stream/Cargo.lock | 321 ++++++++++++++++++- 1 file changed, 311 insertions(+), 10 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/Cargo.lock b/crates/cpp/tests/symmetric_stream/Cargo.lock index c1bd0eecc..723b3b362 100644 --- a/crates/cpp/tests/symmetric_stream/Cargo.lock +++ b/crates/cpp/tests/symmetric_stream/Cargo.lock @@ -59,6 +59,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -71,6 +82,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures" version = "0.3.31" @@ -181,12 +201,151 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "id-arena" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.7.0" @@ -216,6 +375,12 @@ version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "log" version = "0.4.22" @@ -261,6 +426,12 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-project-lite" version = "0.2.15" @@ -383,6 +554,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "stream" version = "0.1.0" @@ -414,6 +591,27 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "unicode-ident" version = "1.0.14" @@ -426,10 +624,33 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "wasm-encoder" -version = "0.221.2" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" +version = "0.223.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "leb128", "wasmparser", @@ -437,8 +658,8 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.221.2" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" +version = "0.223.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "anyhow", "indexmap", @@ -446,14 +667,15 @@ dependencies = [ "serde_derive", "serde_json", "spdx", + "url", "wasm-encoder", "wasmparser", ] [[package]] name = "wasmparser" -version = "0.221.2" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" +version = "0.223.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "bitflags", "hashbrown", @@ -588,8 +810,8 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.221.2" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" +version = "0.223.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "anyhow", "bitflags", @@ -606,8 +828,8 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.221.2" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" +version = "0.223.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "anyhow", "id-arena", @@ -620,3 +842,82 @@ dependencies = [ "unicode-xid", "wasmparser", ] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] From c4b35cdf2187b22a2b07e6087678d0c9ce2e985f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 9 Jan 2025 23:29:46 +0100 Subject: [PATCH 458/672] update wasm-tools --- Cargo.lock | 100 ++++++++++++++++++++++++++--------------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d66d6728f..dadb5fbd1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1509,10 +1509,10 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.222.0", + "wasm-encoder 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-core", "wit-component", - "wit-parser 0.222.0", + "wit-parser 0.223.0", ] [[package]] @@ -1790,27 +1790,27 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.222.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6998c9b5f21eb24c5c53e99621665de8f874098c" +version = "0.223.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e636076193fa68103e937ac951b5f2f587624097017d764b8984d9c0f149464" dependencies = [ "leb128", - "wasmparser 0.222.0", + "wasmparser 0.223.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-encoder" version = "0.223.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e636076193fa68103e937ac951b5f2f587624097017d764b8984d9c0f149464" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "leb128", - "wasmparser 0.223.0", + "wasmparser 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] name = "wasm-metadata" -version = "0.222.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6998c9b5f21eb24c5c53e99621665de8f874098c" +version = "0.223.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "anyhow", "indexmap", @@ -1819,8 +1819,8 @@ dependencies = [ "serde_json", "spdx", "url", - "wasm-encoder 0.222.0", - "wasmparser 0.222.0", + "wasm-encoder 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -1839,25 +1839,25 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.222.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6998c9b5f21eb24c5c53e99621665de8f874098c" +version = "0.223.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a99faceb1a5a84dd6084ec4bfa4b2ab153b5793b43fd8f58b89232634afc35" dependencies = [ "bitflags", - "hashbrown 0.15.2", "indexmap", "semver", - "serde", ] [[package]] name = "wasmparser" version = "0.223.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5a99faceb1a5a84dd6084ec4bfa4b2ab153b5793b43fd8f58b89232634afc35" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "bitflags", + "hashbrown 0.15.2", "indexmap", "semver", + "serde", ] [[package]] @@ -1923,7 +1923,7 @@ dependencies = [ "wasmtime-slab", "wasmtime-versioned-export-macros", "wasmtime-winch", - "wat 1.223.0", + "wat 1.223.0 (registry+https://github.com/rust-lang/crates.io-index)", "windows-sys 0.52.0", ] @@ -2170,44 +2170,44 @@ dependencies = [ [[package]] name = "wast" -version = "222.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6998c9b5f21eb24c5c53e99621665de8f874098c" +version = "223.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59b2ba8a2ff9f06194b7be9524f92e45e70149f4dacc0d0c7ad92b59ac875e4" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.222.0", + "wasm-encoder 0.223.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wast" version = "223.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59b2ba8a2ff9f06194b7be9524f92e45e70149f4dacc0d0c7ad92b59ac875e4" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.223.0", + "wasm-encoder 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] name = "wat" -version = "1.222.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6998c9b5f21eb24c5c53e99621665de8f874098c" +version = "1.223.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "662786915c427e4918ff01eabb3c4756d4d947cd8f635761526b4cc9da2eaaad" dependencies = [ - "wast 222.0.0", + "wast 223.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wat" version = "1.223.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662786915c427e4918ff01eabb3c4756d4d947cd8f635761526b4cc9da2eaaad" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ - "wast 223.0.0", + "wast 223.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2437,11 +2437,11 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.222.0", + "wasm-encoder 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wit-bindgen-core", "wit-component", - "wit-parser 0.222.0", + "wit-parser 0.223.0", ] [[package]] @@ -2452,8 +2452,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.222.0", - "wasmparser 0.222.0", + "wasm-encoder 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasmtime", "wasmtime-wasi", "wit-bindgen-bridge", @@ -2467,7 +2467,7 @@ dependencies = [ "wit-bindgen-rust", "wit-bindgen-teavm-java", "wit-component", - "wit-parser 0.222.0", + "wit-parser 0.223.0", ] [[package]] @@ -2476,7 +2476,7 @@ version = "0.36.0" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser 0.222.0", + "wit-parser 0.223.0", ] [[package]] @@ -2487,7 +2487,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.222.0", + "wasm-encoder 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", @@ -2503,12 +2503,12 @@ dependencies = [ "heck 0.5.0", "indexmap", "test-helpers", - "wasm-encoder 0.222.0", + "wasm-encoder 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", - "wasmparser 0.222.0", + "wasmparser 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-core", "wit-component", - "wit-parser 0.222.0", + "wit-parser 0.223.0", ] [[package]] @@ -2603,8 +2603,8 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.222.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6998c9b5f21eb24c5c53e99621665de8f874098c" +version = "0.223.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "anyhow", "bitflags", @@ -2613,11 +2613,11 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.222.0", + "wasm-encoder 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", - "wasmparser 0.222.0", - "wat 1.222.0", - "wit-parser 0.222.0", + "wasmparser 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wat 1.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.223.0", ] [[package]] @@ -2640,8 +2640,8 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.222.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6998c9b5f21eb24c5c53e99621665de8f874098c" +version = "0.223.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "anyhow", "id-arena", @@ -2652,7 +2652,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.222.0", + "wasmparser 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] From 09bd794b2203b6c5a11891e10c0d7737247d54c9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 10 Jan 2025 00:40:02 +0100 Subject: [PATCH 459/672] simplify code by leveraging Joels latest changes --- .../tests/symmetric_stream/main/src/main.rs | 16 +- .../tests/symmetric_stream/source/src/lib.rs | 14 +- .../tests/symmetric_stream/stream/src/lib.rs | 6 +- .../stream/src/stream_world.rs | 1048 +---------------- .../rust-client/src/async_support.rs | 155 +-- .../src/async_support/future_support.rs | 1 + .../src/async_support/stream_support.rs | 350 ++++++ 7 files changed, 424 insertions(+), 1166 deletions(-) create mode 100644 crates/symmetric_executor/rust-client/src/async_support/future_support.rs create mode 100644 crates/symmetric_executor/rust-client/src/async_support/stream_support.rs diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index 7ea325c7b..b9bddae35 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -1,5 +1,5 @@ use wit_bindgen_symmetric_rt::{ - async_support::{stream, Stream}, + async_support::{stream_support, Stream}, CallbackState, }; @@ -20,13 +20,17 @@ struct CallbackInfo { extern "C" fn ready(arg: *mut ()) -> CallbackState { let info = unsafe { &*arg.cast::() }; - let len = unsafe { stream::read_amount(info.stream) }; + let len = unsafe { stream_support::read_amount(info.stream) }; if len > 0 { for i in 0..len as usize { println!("data {}", info.data[i]); } unsafe { - stream::start_reading(info.stream, info.data.as_ptr().cast_mut().cast(), DATALEN); + stream_support::start_reading( + info.stream, + info.data.as_ptr().cast_mut().cast(), + DATALEN, + ); }; // call again CallbackState::Pending @@ -52,10 +56,10 @@ fn main() { data: [0, 0], }); unsafe { - stream::start_reading(stream, info.data.as_mut_ptr().cast(), DATALEN); + stream_support::start_reading(stream, info.data.as_mut_ptr().cast(), DATALEN); }; let subscription = unsafe { - wit_bindgen_symmetric_rt::subscribe_event_send_ptr(stream::read_ready_event(stream)) + wit_bindgen_symmetric_rt::subscribe_event_send_ptr(stream_support::read_ready_event(stream)) }; println!("Register read in main"); wit_bindgen_symmetric_rt::register( @@ -64,5 +68,5 @@ fn main() { (&*info as *const CallbackInfo).cast_mut().cast(), ); wit_bindgen_symmetric_rt::run(); - unsafe { stream::close_read(stream) }; + unsafe { stream_support::close_read(stream) }; } diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index 2d8aea8e5..f05506bc7 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -3,7 +3,7 @@ use std::sync::atomic::{AtomicU32, Ordering}; use wit_bindgen_symmetric_rt::{ async_support::{ results, - stream::{self, Slice}, + stream_support::{self, Slice}, Stream, }, register, subscribe_event_send_ptr, CallbackState, EventSubscription, @@ -15,10 +15,10 @@ extern "C" fn timer_call(data: *mut ()) -> CallbackState { let count = COUNT.fetch_add(1, Ordering::AcqRel); let stream: *mut Stream = data.cast(); if count <= 5 { - let Slice { addr, size } = unsafe { stream::start_writing(stream) }; + let Slice { addr, size } = unsafe { stream_support::start_writing(stream) }; assert!(size >= 1); *unsafe { &mut *addr.cast::() } = count; - unsafe { stream::finish_writing(stream, 1) }; + unsafe { stream_support::finish_writing(stream, 1) }; } CallbackState::Ready } @@ -28,8 +28,8 @@ extern "C" fn write_ready(data: *mut ()) -> CallbackState { if count > 5 { let stream: *mut Stream = data.cast(); // EOF - unsafe { stream::finish_writing(stream, results::CLOSED) }; - unsafe { stream::close_write(stream) }; + unsafe { stream_support::finish_writing(stream, results::CLOSED) }; + unsafe { stream_support::close_write(stream) }; CallbackState::Ready } else { if count == 1 { @@ -47,8 +47,8 @@ pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate( // _args: *mut u8, results: *mut u8, ) -> *mut u8 { - let stream = stream::create_stream(); - let event = unsafe { subscribe_event_send_ptr(stream::write_ready_event(stream)) }; + let stream = stream_support::create_stream(); + let event = unsafe { subscribe_event_send_ptr(stream_support::write_ready_event(stream)) }; register(event, write_ready, stream.cast()); *unsafe { &mut *results.cast::<*mut Stream>() } = stream; std::ptr::null_mut() diff --git a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs index 8f5010a3e..b5335abfa 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs @@ -1,5 +1,5 @@ use futures::{SinkExt, StreamExt}; -use stream_world::{stream_and_future_support, test::test::stream_source::create}; +use stream_world::test::test::stream_source::create; use wit_bindgen_symmetric_rt::async_support; mod stream_world; @@ -9,8 +9,8 @@ stream_world::export!(MyStruct with_types_in stream_world); struct MyStruct; impl stream_world::exports::test::test::stream_test::Guest for MyStruct { - async fn create() -> stream_and_future_support::StreamReader { - let (mut writer, reader) = stream_and_future_support::new_stream(); + async fn create() -> async_support::StreamReader { + let (mut writer, reader) = async_support::stream_support::new_stream(); let mut input = create().await; async_support::spawn(async move { diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 348decf1e..d0037c522 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -11,172 +11,33 @@ pub mod test { static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; - // use _rt::stream_and_future_support::StreamHandle2; - use wit_bindgen_symmetric_rt::async_support::{self, AddressSend, StreamHandle2}; + // use wit_bindgen_symmetric_rt::async_support::{self, AddressSend, StreamHandle2}; use super::super::super::_rt; - use _rt::stream_and_future_support::WitStream; - - impl _rt::stream_and_future_support::StreamPayload for u32 { - fn new() -> *mut WitStream { - { - // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - // extern "C" { - // #[link_name = "[stream-new-0]create"] - // fn new() -> u32; - // } - // unsafe { new() } - todo!() - } - } - - async fn write(_stream: &StreamHandle2, _values: &[Self]) -> Option { - { - // let address = values.as_ptr() as _; - - // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - // extern "C" { - // #[link_name = "[async][stream-write-0]create"] - // fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; - // } - - // unsafe { - // ::wit_bindgen_symmetric_rt::async_support::await_stream_result(wit_import, stream, address, u32::try_from(values.len()).unwrap()).await - // } - todo!() - } - } - - async fn read( - stream: &StreamHandle2, - values: &mut [::core::mem::MaybeUninit], - ) -> Option { - { - // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - // extern "C" { - // #[link_name = "[async][stream-read-0]create"] - // fn wit_import(_: u32, _: *mut u8, _: u32) -> u32; - // } - let poll_fn = async_support::stream::start_reading; - // &*((&*(stream.handle.0)).vtable) }.read; - // match &stream.event { - // SubscriptionType::None => { - // let subscr = unsafe { - // subscribe_event_send_ptr( - // (&mut *stream.handle.0).read_ready_event_send, - // ) - // }; - // stream.event = SubscriptionType::Read(subscr) - // } - // SubscriptionType::Read(_event_subscription) => (), - // SubscriptionType::Write(_event_subscription) => unreachable!(), - // } - // let SubscriptionType::Read(subscr) = &stream.event else { - // unreachable!() - // }; - let address = AddressSend(values.as_mut_ptr() as _); - let count = unsafe { - ::wit_bindgen_symmetric_rt::async_support::await_stream_result( - poll_fn, - StreamHandle2(stream.0), - address, - values.len(), - // &stream.event, - ) - .await - }; - #[allow(unused)] - if let Some(count) = count { - let value = (); - } - count - } - } - - fn cancel_write(_writer: *mut WitStream) { - { - // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - // extern "C" { - // #[link_name = "[stream-cancel-write-0]create"] - // fn cancel(_: u32) -> u32; - // } - // unsafe { cancel(writer) }; - todo!() - } - } - - fn cancel_read(_reader: *mut WitStream) { - { - // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - // extern "C" { - // #[link_name = "[stream-cancel-read-0]create"] - // fn cancel(_: u32) -> u32; - // } - // unsafe { cancel(reader) }; - todo!() - } - } - - fn close_writable(_writer: *mut WitStream) { - { - // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - // extern "C" { - // #[link_name = "[stream-close-writable-0]create"] - // fn drop(_: u32, _: u32); - // } - // unsafe { drop(writer, 0) } - todo!() - } - } - - fn close_readable(_reader: *mut WitStream) { - { - // #[link(wasm_import_module = "[import-payload]test:test/stream-source")] - // extern "C" { - // #[link_name = "[stream-close-readable-0]create"] - // fn drop(_: u32); - // } - // unsafe { drop(reader) } - todo!() - } - } - } #[allow(unused_unsafe, clippy::all)] - pub async fn create() -> _rt::stream_and_future_support::StreamReader { + pub async fn create() -> wit_bindgen_symmetric_rt::async_support::StreamReader { unsafe { // let layout0 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); // let ptr0 = core::ptr::null_mut(); //_rt::alloc::alloc(layout0); - let layout1 = _rt::alloc::Layout::from_size_align_unchecked( - core::mem::size_of::<*const u8>(), - core::mem::size_of::<*const u8>(), - ); + let layout1 = _rt::alloc::Layout::from_size_align_unchecked(4, 4); let ptr1 = _rt::alloc::alloc(layout1); #[link(wasm_import_module = "test:test/stream-source")] #[link(name = "source")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "[async]create")] - fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate( - // _: *mut u8, - _: *mut u8, - ) -> *mut u8; + fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(_: *mut u8) -> *mut u8; } // let layout2 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); - ::wit_bindgen_symmetric_rt::async_support::await_result( - move || unsafe {testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(ptr1)}, - // ptr0, - // ptr1, - ) + ::wit_bindgen_symmetric_rt::async_support::await_result(move || unsafe { + testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(ptr1) + }) .await; let l3 = *ptr1.add(0).cast::<*mut u8>(); let result4 = - _rt::stream_and_future_support::StreamReader::from_handle(l3.cast()); - _rt::cabi_dealloc( - ptr1, - core::mem::size_of::<*const u8>(), - core::mem::size_of::<*const u8>(), - ); + wit_bindgen_symmetric_rt::async_support::StreamReader::new(l3.cast()); + _rt::cabi_dealloc(ptr1, 4, 4); result4 } } @@ -198,10 +59,7 @@ pub mod exports { use super::super::super::super::_rt; #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_create_cabi( - // _args: *mut u8, - results: *mut u8, - ) -> *mut u8 { + pub unsafe fn _export_create_cabi(results: *mut u8) -> *mut u8 { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); let result0 = T::create(); @@ -228,7 +86,7 @@ pub mod exports { // } pub trait Guest { fn create() -> impl ::core::future::Future< - Output = _rt::stream_and_future_support::StreamReader, + Output = ::wit_bindgen_symmetric_rt::async_support::StreamReader, > + 'static; } #[doc(hidden)] @@ -254,863 +112,6 @@ pub mod exports { } } mod _rt { - #![allow(dead_code, clippy::all)] - pub mod stream_and_future_support { - pub use wit_bindgen_symmetric_rt::async_support::{Stream as WitStream, StreamHandle2}; - use wit_bindgen_symmetric_rt::{ - activate_event_send_ptr, - async_support::{ - results, - stream::{self, Slice}, - wait_on, - }, - subscribe_event_send_ptr, - }; - use { - futures::{future::FutureExt, sink::Sink, stream::Stream}, - std::{ - convert::Infallible, - fmt, - future::{Future, IntoFuture}, - iter, - marker::PhantomData, - mem::{self, ManuallyDrop, MaybeUninit}, - pin::Pin, - task::{Context, Poll}, - }, - }; - - #[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; - 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 { - handle: u32, - _phantom: PhantomData, - } - - impl fmt::Debug for FutureWriter { - 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 { - writer: Option>, - future: Pin>>, - } - - impl Future for CancelableWrite { - 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 CancelableWrite { - /// 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 { - self.cancel_mut() - } - - fn cancel_mut(&mut self) -> FutureWriter { - // 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 - todo!() - } - } - - impl Drop for CancelableWrite { - fn drop(&mut self) { - if self.writer.is_some() { - self.cancel_mut(); - } - } - } - - impl FutureWriter { - /// Write the specified value to this `future`. - pub fn write(self, _v: T) -> CancelableWrite { - // 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>> - // } - // 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)), - // }, - // }), - // } - todo!() - } - } - - impl Drop for FutureWriter { - 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); - // } - // }, - // }); - todo!() - } - } - - /// Represents a read operation which may be canceled prior to completion. - pub struct CancelableRead { - reader: Option>, - future: Pin>>>, - } - - impl Future for CancelableRead { - type Output = Option; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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 CancelableRead { - /// 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 { - self.cancel_mut() - } - - fn cancel_mut(&mut self) -> FutureReader { - // 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 - todo!() - } - } - - impl Drop for CancelableRead { - fn drop(&mut self) { - if self.reader.is_some() { - self.cancel_mut(); - } - } - } - - /// Represents the readable end of a Component Model `future`. - pub struct FutureReader { - handle: u32, - _phantom: PhantomData, - } - - impl fmt::Debug for FutureReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FutureReader") - .field("handle", &self.handle) - .finish() - } - } - - impl FutureReader { - #[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, - // } - todo!() - } - - #[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 - todo!() - } - } - - impl IntoFuture for FutureReader { - type Output = Option; - type IntoFuture = CancelableRead; - - /// 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>>, - // 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()))) - // } - // }, - // }), - // } - todo!() - } - } - - impl Drop for FutureReader { - 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!(), - // }, - // }); - todo!() - } - } - - #[doc(hidden)] - pub trait StreamPayload: Unpin + Sized + 'static { - fn new() -> *mut WitStream; - fn write( - stream: &StreamHandle2, - values: &[Self], - ) -> impl std::future::Future> + Send; - fn read( - stream: &StreamHandle2, - values: &mut [MaybeUninit], - ) -> impl std::future::Future> + Send; - fn cancel_write(stream: *mut WitStream); - fn cancel_read(stream: *mut WitStream); - fn close_writable(stream: *mut WitStream); - fn close_readable(stream: *mut WitStream); - } - - struct CancelWriteOnDrop { - handle: Option<*mut WitStream>, - _phantom: PhantomData, - } - - impl Drop for CancelWriteOnDrop { - 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), - // }, - // }); - // } - todo!() - } - } - - /// Represents the writable end of a Component Model `stream`. - pub struct StreamWriter { - handle: StreamHandle2, - future: Option + 'static + Send>>>, - // event: EventSubscription, - _phantom: PhantomData, - } - - impl StreamWriter { - /// 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; - } - - #[doc(hidden)] - pub fn from_handle(handle: *mut WitStream) -> Self { - // let subscr = - // unsafe { subscribe_event_send_ptr((&mut *handle).write_ready_event_send) }; - // let subscr_copy = subscr.dup(); - // let ready = Box::pin(async move { - // wait_on(subscr).await; - // }); - Self { - handle: StreamHandle2(handle), - future: None, //Some(ready), - // event: subscr, - _phantom: PhantomData, - } - } - } - - impl fmt::Debug for StreamWriter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StreamWriter") - .field("handle", &self.handle.0) - .finish() - } - } - - impl Sink> for StreamWriter { - type Error = Infallible; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let me = self.get_mut(); - - let ready = unsafe { stream::is_ready_to_write(me.handle.0) }; - - // see also StreamReader::poll_next - if !ready && me.future.is_none() { - let handle = StreamHandle2(me.handle.0); - me.future = Some(Box::pin(async move { - let handle_local = handle; - let subscr = unsafe { - subscribe_event_send_ptr(stream::write_ready_event(handle_local.0)) - }; - subscr.reset(); - wait_on(subscr).await; - }) - as Pin + 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) -> Result<(), Self::Error> { - let item_len = item.len(); - let me = self.get_mut(); - let stream = me.handle.0; - let Slice { addr, size } = unsafe { stream::start_writing(stream) }; - assert!(size >= item_len); - let slice = unsafe { - std::slice::from_raw_parts_mut(addr.cast::>(), item_len) - }; - for (a, b) in slice.iter_mut().zip(item.drain(..)) { - a.write(b); - } - unsafe { stream::finish_writing(stream, item_len as isize) }; - // 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:: { - // 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:: { - // 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(()) - - // wait before next element is written - // let subscr_copy = me.event.dup(); - Ok(()) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.poll_ready(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.poll_ready(cx) - } - } - - impl Drop for StreamWriter { - 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); - // } - // }, - // }); - if !unsafe { stream::is_write_closed(self.handle.0) } { - unsafe { - stream::finish_writing(self.handle.0, results::CLOSED); - } - } - unsafe { stream::close_write(self.handle.0) }; - } - } - - struct CancelReadOnDrop { - handle: Option<*mut WitStream>, - _phantom: PhantomData, - } - - impl Drop for CancelReadOnDrop { - 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), - // }, - // }); - // } - todo!() - } - } - - /// Represents the readable end of a Component Model `stream`. - pub struct StreamReader { - handle: StreamHandle2, - future: Option>> + 'static + Send>>>, - // event: EventSubscription, - _phantom: PhantomData, - } - - impl StreamReader { - /// 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 fmt::Debug for StreamReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StreamReader") - .field("handle", &self.handle.0) - .finish() - } - } - - impl StreamReader { - #[doc(hidden)] - pub fn from_handle(handle: *mut WitStream) -> 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: StreamHandle2(handle), - future: None, - _phantom: PhantomData, - } - } - - #[doc(hidden)] - pub fn into_handle(self) -> *mut WitStream { - // 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 - ManuallyDrop::new(self).handle.0 - } - } - - impl Stream for StreamReader { - type Item = Vec; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let me = self.get_mut(); - - if me.future.is_none() { - let handle = StreamHandle2(me.handle.0); - me.future = Some(Box::pin(async move { - let mut buffer = iter::repeat_with(MaybeUninit::uninit) - .take(ceiling(4 * 1024, mem::size_of::())) - .collect::>(); - let stream_handle = handle; - let result = if let Some(count) = T::read(&stream_handle, &mut buffer).await - { - buffer.truncate(count); - Some(unsafe { mem::transmute::>, Vec>(buffer) }) - } else { - None - }; - // let _ = stream_handle.event.take_handle(); - // cancel_on_drop.handle = None; - // drop(cancel_on_drop); - result - // T::read(me.handle.0, &mut buffer) - }) - as Pin + Send>>); - } - // 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:: { - // handle: Some(handle), - // _phantom: PhantomData, - // }; - // Box::pin(async move { - // let mut buffer = iter::repeat_with(MaybeUninit::uninit) - // .take(ceiling(64 * 1024, mem::size_of::())) - // .collect::>(); - - // let result = if let Some(count) = T::read(handle, &mut buffer).await { - // buffer.truncate(count); - // Some(unsafe { - // mem::transmute::>, Vec>(buffer) - // }) - // } else { - // None - // }; - // cancel_on_drop.handle = None; - // drop(cancel_on_drop); - // result - // }) as Pin>> - // } - // Handle::LocalOpen => { - // let (tx, rx) = oneshot::channel(); - // entry.insert(Handle::LocalWaiting(tx)); - // let mut cancel_on_drop = CancelReadOnDrop:: { - // 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 Drop for StreamReader { - 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!(), - // }, - // }); - //todo!() - // let stream = self.handle.0; - // let write_ready_evt = unsafe { &*stream }.write_ready_event_send; - unsafe { activate_event_send_ptr(stream::write_ready_event(self.handle.0)) }; - unsafe { stream::close_read(self.handle.0) }; - } - } - - /// Creates a new Component Model `future` with the specified payload type. - pub fn new_future() -> (FutureWriter, FutureReader) { - // 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, - // }, - // ) - todo!() - } - - /// Creates a new Component Model `stream` with the specified payload type. - pub fn new_stream() -> (StreamWriter, StreamReader) { - let handle = stream::create_stream(); - ( - StreamWriter::from_handle(handle), - StreamReader::from_handle(handle), - ) - } - - fn ceiling(x: usize, y: usize) -> usize { - (x / y) + if x % y == 0 { 0 } else { 1 } - } - } pub use alloc_crate::alloc; pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { if size == 0 { @@ -1126,8 +127,31 @@ mod _rt { } extern crate alloc as alloc_crate; } -#[allow(unused_imports)] -pub use _rt::stream_and_future_support; +pub mod wit_stream { + #![allow(dead_code, clippy::all)] + + use wit_bindgen_symmetric_rt::async_support::stream_support::new_stream; + + pub trait StreamPayload: Unpin + Sized + 'static { + // fn new() -> *mut (); + //u32, &'static ::wit_bindgen_symmetric_rt::async_support::StreamVtable); + } + + impl StreamPayload for u32 { + // fn new() -> *mut () { + // todo!() + // } + } + + pub fn new() -> ( + ::wit_bindgen_symmetric_rt::async_support::StreamWriter, + ::wit_bindgen_symmetric_rt::async_support::StreamReader, + ) { + new_stream() + } +} +// #[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. diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index d3f99c402..dc4463463 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -12,6 +12,17 @@ use crate::{ subscribe_event_send_ptr, }; +mod future_support; +// later make it non-pub +pub mod stream_support; + +pub use { + // future_support::{FutureReader, FutureVtable, FutureWriter}, + stream_support::{results, Stream, StreamHandle2, StreamReader, StreamWriter}, +}; + +pub use futures; + // 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 {} @@ -27,137 +38,10 @@ struct FutureState { waiting_for: Option, } -pub use stream::{results, Stream}; - -pub mod stream { - use std::sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}; - - use crate::{activate_event_send_ptr, EventGenerator}; - - pub mod results { - pub const BLOCKED: isize = -1; - pub const CLOSED: isize = isize::MIN; - pub const CANCELED: isize = 0; - } - - pub struct Stream { - read_ready_event_send: *mut (), - write_ready_event_send: *mut (), - read_addr: AtomicPtr<()>, - read_size: AtomicUsize, - ready_size: AtomicIsize, - active_instances: AtomicUsize, - } - - pub unsafe extern "C" fn start_reading( - stream: *mut Stream, - buf: *mut (), - size: usize, - ) -> isize { - let old_ready = unsafe { &*stream }.ready_size.load(Ordering::Acquire); - if old_ready == results::CLOSED { - return old_ready; - } - assert!(old_ready == results::BLOCKED); - let old_size = unsafe { &mut *stream } - .read_size - .swap(size, Ordering::Acquire); - assert_eq!(old_size, 0); - let old_ptr = unsafe { &mut *stream } - .read_addr - .swap(buf, Ordering::Release); - assert_eq!(old_ptr, std::ptr::null_mut()); - let write_evt = unsafe { &mut *stream }.write_ready_event_send; - unsafe { activate_event_send_ptr(write_evt) }; - results::BLOCKED - } - - pub unsafe extern "C" fn read_ready_event(stream: *const Stream) -> *mut () { - unsafe { (&*stream).read_ready_event_send } - } - - pub unsafe extern "C" fn write_ready_event(stream: *const Stream) -> *mut () { - unsafe { (&*stream).write_ready_event_send } - } +// pub use stream::{results, Stream}; - pub unsafe extern "C" fn is_ready_to_write(stream: *const Stream) -> bool { - !unsafe { &*stream } - .read_addr - .load(Ordering::Acquire) - .is_null() - } - - pub unsafe extern "C" fn is_write_closed(stream: *const Stream) -> bool { - unsafe { &*stream }.ready_size.load(Ordering::Acquire) == results::CLOSED - } - - #[repr(C)] - pub struct Slice { - pub addr: *mut (), - pub size: usize, - } - - pub unsafe extern "C" fn start_writing(stream: *mut Stream) -> Slice { - let size = unsafe { &*stream }.read_size.swap(0, Ordering::Acquire); - let addr = unsafe { &*stream } - .read_addr - .swap(core::ptr::null_mut(), Ordering::Release); - Slice { addr, size } - } - - pub unsafe extern "C" fn read_amount(stream: *const Stream) -> isize { - unsafe { &*stream } - .ready_size - .swap(results::BLOCKED, Ordering::Acquire) - } - - pub unsafe extern "C" fn finish_writing(stream: *mut Stream, elements: isize) { - let old_ready = unsafe { &*stream } - .ready_size - .swap(elements as isize, Ordering::Release); - assert_eq!(old_ready, results::BLOCKED); - unsafe { activate_event_send_ptr(read_ready_event(stream)) }; - } - - pub unsafe extern "C" fn close_read(stream: *mut Stream) { - let refs = unsafe { &mut *stream } - .active_instances - .fetch_sub(1, Ordering::AcqRel); - if refs == 1 { - let obj = Box::from_raw(stream); - drop(EventGenerator::from_handle( - obj.read_ready_event_send as usize, - )); - drop(EventGenerator::from_handle( - obj.write_ready_event_send as usize, - )); - drop(obj); - } - } - - pub unsafe extern "C" fn close_write(stream: *mut Stream) { - // same for write (for now) - close_read(stream); - } - - pub extern "C" fn create_stream() -> *mut Stream { - Box::into_raw(Box::new(Stream::new())) - } - - impl Stream { - fn new() -> Self { - Self { - // vtable: &STREAM_VTABLE as *const StreamVtable, - read_ready_event_send: EventGenerator::new().take_handle() as *mut (), - write_ready_event_send: EventGenerator::new().take_handle() as *mut (), - read_addr: AtomicPtr::new(core::ptr::null_mut()), - read_size: AtomicUsize::new(0), - ready_size: AtomicIsize::new(results::BLOCKED), - active_instances: AtomicUsize::new(2), - } - } - } -} +// pub mod stream { +// } static VTABLE: RawWakerVTable = RawWakerVTable::new( |_| RawWaker::new(core::ptr::null(), &VTABLE), @@ -277,12 +161,6 @@ pub fn spawn(future: impl Future + 'static + Send) { drop(wait_for); } -// Stream handles are Send, so wrap them -#[repr(transparent)] -pub struct StreamHandle2(pub *mut Stream); -unsafe impl Send for StreamHandle2 {} -unsafe impl Sync for StreamHandle2 {} - #[repr(transparent)] pub struct AddressSend(pub *mut ()); unsafe impl Send for AddressSend {} @@ -299,10 +177,11 @@ pub async unsafe fn await_stream_result( let result = import(stream_copy, address.0, count); match result { results::BLOCKED => { - let event = unsafe { subscribe_event_send_ptr(stream::read_ready_event(stream.0)) }; + let event = + unsafe { subscribe_event_send_ptr(stream_support::read_ready_event(stream.0)) }; event.reset(); wait_on(event).await; - let v = stream::read_amount(stream.0); + let v = stream_support::read_amount(stream.0); if let results::CLOSED | results::CANCELED = v { None } else { 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..8b1378917 --- /dev/null +++ b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs @@ -0,0 +1 @@ + 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..ea6f80de8 --- /dev/null +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -0,0 +1,350 @@ +use std::sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}; + +use crate::{ + activate_event_send_ptr, async_support::wait_on, subscribe_event_send_ptr, EventGenerator, +}; +use { + futures::sink::Sink, + std::{ + convert::Infallible, + fmt, + future::Future, + iter, + marker::PhantomData, + mem::{self, ManuallyDrop, 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 { + handle: StreamHandle2, + future: Option + 'static + Send>>>, + _phantom: PhantomData, +} + +impl StreamWriter { + #[doc(hidden)] + pub fn new(handle: *mut Stream) -> Self { + Self { + handle: StreamHandle2(handle), + future: None, + _phantom: PhantomData, + } + } + + pub fn cancel(&mut self) { + todo!() + } +} + +impl fmt::Debug for StreamWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamWriter") + .field("handle", &self.handle.0) + .finish() + } +} + +impl Sink> for StreamWriter { + type Error = Infallible; + + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let me = self.get_mut(); + + let ready = unsafe { is_ready_to_write(me.handle.0) }; + + // see also StreamReader::poll_next + if !ready && me.future.is_none() { + let handle = StreamHandle2(me.handle.0); + me.future = Some(Box::pin(async move { + let handle_local = handle; + let subscr = unsafe { subscribe_event_send_ptr(write_ready_event(handle_local.0)) }; + subscr.reset(); + wait_on(subscr).await; + }) as Pin + 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) -> Result<(), Self::Error> { + let item_len = item.len(); + let me = self.get_mut(); + let stream = me.handle.0; + let Slice { addr, size } = unsafe { start_writing(stream) }; + assert!(size >= item_len); + let slice = + unsafe { std::slice::from_raw_parts_mut(addr.cast::>(), item_len) }; + for (a, b) in slice.iter_mut().zip(item.drain(..)) { + a.write(b); + } + unsafe { finish_writing(stream, item_len as isize) }; + Ok(()) + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.poll_ready(cx) + } +} + +impl Drop for StreamWriter { + fn drop(&mut self) { + if !unsafe { is_write_closed(self.handle.0) } { + unsafe { + finish_writing(self.handle.0, results::CLOSED); + } + } + unsafe { close_write(self.handle.0) }; + } +} + +/// Represents the readable end of a Component Model `stream`. +pub struct StreamReader { + handle: StreamHandle2, + future: Option>> + 'static + Send>>>, + // event: EventSubscription, + _phantom: PhantomData, +} + +impl StreamReader { + /// 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 fmt::Debug for StreamReader { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StreamReader") + .field("handle", &self.handle.0) + .finish() + } +} + +impl StreamReader { + #[doc(hidden)] + pub fn new(handle: *mut Stream) -> Self { + Self { + handle: StreamHandle2(handle), + future: None, + _phantom: PhantomData, + } + } + #[doc(hidden)] + pub fn into_handle(self) -> *mut Stream { + ManuallyDrop::new(self).handle.0 + } +} + +impl futures::stream::Stream for StreamReader { + type Item = Vec; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let me = self.get_mut(); + + if me.future.is_none() { + let handle = StreamHandle2(me.handle.0); + me.future = Some(Box::pin(async move { + let mut buffer = iter::repeat_with(MaybeUninit::uninit) + .take(ceiling(4 * 1024, mem::size_of::())) + .collect::>(); + let stream_handle = handle; + let result = if let Some(count) = { + let poll_fn = start_reading; + let address = super::AddressSend(buffer.as_mut_ptr() as _); + let count = unsafe { + super::await_stream_result( + poll_fn, + stream_handle, + address, + buffer.len(), + // &stream.event, + ) + .await + }; + #[allow(unused)] + if let Some(count) = count { + let value = (); + } + count + } + // T::read(&stream_handle, &mut buffer).await + { + buffer.truncate(count); + Some(unsafe { mem::transmute::>, Vec>(buffer) }) + } else { + None + }; + result + }) as Pin + 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 Drop for StreamReader { + fn drop(&mut self) { + unsafe { activate_event_send_ptr(write_ready_event(self.handle.0)) }; + unsafe { close_read(self.handle.0) }; + } +} + +pub struct Stream { + read_ready_event_send: *mut (), + write_ready_event_send: *mut (), + read_addr: AtomicPtr<()>, + read_size: AtomicUsize, + ready_size: AtomicIsize, + active_instances: AtomicUsize, +} + +pub unsafe extern "C" fn start_reading(stream: *mut Stream, buf: *mut (), size: usize) -> isize { + let old_ready = unsafe { &*stream }.ready_size.load(Ordering::Acquire); + if old_ready == results::CLOSED { + return old_ready; + } + assert!(old_ready == results::BLOCKED); + let old_size = unsafe { &mut *stream } + .read_size + .swap(size, Ordering::Acquire); + assert_eq!(old_size, 0); + let old_ptr = unsafe { &mut *stream } + .read_addr + .swap(buf, Ordering::Release); + assert_eq!(old_ptr, std::ptr::null_mut()); + let write_evt = unsafe { &mut *stream }.write_ready_event_send; + unsafe { activate_event_send_ptr(write_evt) }; + results::BLOCKED +} + +pub unsafe extern "C" fn read_ready_event(stream: *const Stream) -> *mut () { + unsafe { (&*stream).read_ready_event_send } +} + +pub unsafe extern "C" fn write_ready_event(stream: *const Stream) -> *mut () { + unsafe { (&*stream).write_ready_event_send } +} + +pub unsafe extern "C" fn is_ready_to_write(stream: *const Stream) -> bool { + !unsafe { &*stream } + .read_addr + .load(Ordering::Acquire) + .is_null() +} + +pub unsafe extern "C" fn is_write_closed(stream: *const Stream) -> bool { + unsafe { &*stream }.ready_size.load(Ordering::Acquire) == results::CLOSED +} + +#[repr(C)] +pub struct Slice { + pub addr: *mut (), + pub size: usize, +} + +pub unsafe extern "C" fn start_writing(stream: *mut Stream) -> Slice { + let size = unsafe { &*stream }.read_size.swap(0, Ordering::Acquire); + let addr = unsafe { &*stream } + .read_addr + .swap(core::ptr::null_mut(), Ordering::Release); + Slice { addr, size } +} + +pub unsafe extern "C" fn read_amount(stream: *const Stream) -> isize { + unsafe { &*stream } + .ready_size + .swap(results::BLOCKED, Ordering::Acquire) +} + +pub unsafe extern "C" fn finish_writing(stream: *mut Stream, elements: isize) { + let old_ready = unsafe { &*stream } + .ready_size + .swap(elements as isize, Ordering::Release); + assert_eq!(old_ready, results::BLOCKED); + unsafe { activate_event_send_ptr(read_ready_event(stream)) }; +} + +pub unsafe extern "C" fn close_read(stream: *mut Stream) { + let refs = unsafe { &mut *stream } + .active_instances + .fetch_sub(1, Ordering::AcqRel); + if refs == 1 { + let obj = Box::from_raw(stream); + drop(EventGenerator::from_handle( + obj.read_ready_event_send as usize, + )); + drop(EventGenerator::from_handle( + obj.write_ready_event_send as usize, + )); + drop(obj); + } +} + +pub unsafe extern "C" fn close_write(stream: *mut Stream) { + // same for write (for now) + close_read(stream); +} + +pub extern "C" fn create_stream() -> *mut Stream { + Box::into_raw(Box::new(Stream::new())) +} + +impl Stream { + fn new() -> Self { + Self { + // vtable: &STREAM_VTABLE as *const StreamVtable, + read_ready_event_send: EventGenerator::new().take_handle() as *mut (), + write_ready_event_send: EventGenerator::new().take_handle() as *mut (), + read_addr: AtomicPtr::new(core::ptr::null_mut()), + read_size: AtomicUsize::new(0), + ready_size: AtomicIsize::new(results::BLOCKED), + active_instances: AtomicUsize::new(2), + } + } +} + +// Stream handles are Send, so wrap them +#[repr(transparent)] +pub struct StreamHandle2(pub *mut Stream); +unsafe impl Send for StreamHandle2 {} +unsafe impl Sync for StreamHandle2 {} + +pub fn new_stream() -> (StreamWriter, StreamReader) { + let handle = create_stream(); + (StreamWriter::new(handle), StreamReader::new(handle)) +} From abd68330a3ca71ccb3137847335eafd8ec508729 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 11 Jan 2025 17:15:10 +0100 Subject: [PATCH 460/672] adapt to new version, simplify --- crates/core/src/abi.rs | 2 -- crates/cpp/tests/symmetric_stream/Cargo.lock | 10 +++---- .../tests/symmetric_stream/source/Cargo.toml | 4 +-- .../tests/symmetric_stream/stream/Cargo.toml | 7 ++--- .../stream/src/stream_world.rs | 28 ------------------- crates/symmetric_executor/Cargo.lock | 10 +++---- crates/symmetric_executor/Cargo.toml | 4 +-- .../symmetric_executor/rust-client/Cargo.toml | 2 +- .../rust-client/src/async_support.rs | 28 ++++--------------- .../symmetric_executor/rust-client/src/lib.rs | 2 +- 10 files changed, 24 insertions(+), 73 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 4258b4754..179a987dc 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -904,8 +904,6 @@ 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); } diff --git a/crates/cpp/tests/symmetric_stream/Cargo.lock b/crates/cpp/tests/symmetric_stream/Cargo.lock index 723b3b362..8fcba0dd6 100644 --- a/crates/cpp/tests/symmetric_stream/Cargo.lock +++ b/crates/cpp/tests/symmetric_stream/Cargo.lock @@ -749,7 +749,7 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "wit-bindgen" -version = "0.36.0" +version = "0.37.0" dependencies = [ "wit-bindgen-rt", "wit-bindgen-rust-macro", @@ -757,7 +757,7 @@ dependencies = [ [[package]] name = "wit-bindgen-core" -version = "0.36.0" +version = "0.37.0" dependencies = [ "anyhow", "heck", @@ -766,7 +766,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" -version = "0.36.0" +version = "0.37.0" dependencies = [ "bitflags", "futures", @@ -775,7 +775,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.36.0" +version = "0.37.0" dependencies = [ "anyhow", "heck", @@ -789,7 +789,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" -version = "0.36.0" +version = "0.37.0" dependencies = [ "anyhow", "prettyplease", diff --git a/crates/cpp/tests/symmetric_stream/source/Cargo.toml b/crates/cpp/tests/symmetric_stream/source/Cargo.toml index 03f1e5961..710191356 100644 --- a/crates/cpp/tests/symmetric_stream/source/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/source/Cargo.toml @@ -4,8 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] -symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor" } -wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../../../../symmetric_executor/rust-client" } +symmetric_executor = { path = "../../../../symmetric_executor" } +wit-bindgen-symmetric-rt = { path = "../../../../symmetric_executor/rust-client" } [lib] crate-type = ["cdylib"] diff --git a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml index e6c7045a0..777ab9e13 100644 --- a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml @@ -5,10 +5,9 @@ edition = "2021" [dependencies] futures = "0.3.31" -source = { version = "0.1.0", path = "../source" } -wit-bindgen = { version = "0.36.0", path = "../../../../guest-rust" } -#wit-bindgen-rt = { version = "0.36.0", path = "../../../../guest-rust/rt" } -wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../../../../symmetric_executor/rust-client" } +source = { path = "../source" } +wit-bindgen = { path = "../../../../guest-rust" } +wit-bindgen-symmetric-rt = { path = "../../../../symmetric_executor/rust-client" } [lib] crate-type = ["cdylib"] diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index d0037c522..8ad11e75a 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -11,15 +11,11 @@ pub mod test { static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; - // use wit_bindgen_symmetric_rt::async_support::{self, AddressSend, StreamHandle2}; - use super::super::super::_rt; #[allow(unused_unsafe, clippy::all)] pub async fn create() -> wit_bindgen_symmetric_rt::async_support::StreamReader { unsafe { - // let layout0 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); - // let ptr0 = core::ptr::null_mut(); //_rt::alloc::alloc(layout0); let layout1 = _rt::alloc::Layout::from_size_align_unchecked(4, 4); let ptr1 = _rt::alloc::alloc(layout1); @@ -29,7 +25,6 @@ pub mod test { #[cfg_attr(target_arch = "wasm32", link_name = "[async]create")] fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(_: *mut u8) -> *mut u8; } - // let layout2 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); ::wit_bindgen_symmetric_rt::async_support::await_result(move || unsafe { testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(ptr1) }) @@ -56,7 +51,6 @@ pub mod exports { 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(results: *mut u8) -> *mut u8 { @@ -73,17 +67,6 @@ pub mod exports { result.cast() } - // #[doc(hidden)] - // #[allow(non_snake_case)] - // pub unsafe fn __callback_create( - // ctx: *mut u8, - // event0: i32, - // event1: i32, - // event2: i32, - // ) -> i32 { - // todo!() - // // ::wit_bindgen_symmetric_rt::async_support::callback(ctx, event0, event1, event2) - // } pub trait Guest { fn create() -> impl ::core::future::Future< Output = ::wit_bindgen_symmetric_rt::async_support::StreamReader, @@ -99,10 +82,6 @@ pub mod exports { unsafe extern "C" fn testX3AtestX2Fstream_testX00X5BasyncX5Dcreate(results: *mut u8) -> *mut u8 { $($path_to_types)*::_export_create_cabi::<$ty>(results) } - // #[export_name = "[callback]create"] - // unsafe extern "C" fn _callback_create(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { - // $($path_to_types)*::__callback_create(ctx, event0, event1, event2) - // } };); } #[doc(hidden)] @@ -133,14 +112,9 @@ pub mod wit_stream { use wit_bindgen_symmetric_rt::async_support::stream_support::new_stream; pub trait StreamPayload: Unpin + Sized + 'static { - // fn new() -> *mut (); - //u32, &'static ::wit_bindgen_symmetric_rt::async_support::StreamVtable); } impl StreamPayload for u32 { - // fn new() -> *mut () { - // todo!() - // } } pub fn new() -> ( @@ -150,8 +124,6 @@ pub mod wit_stream { new_stream() } } -// #[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. diff --git a/crates/symmetric_executor/Cargo.lock b/crates/symmetric_executor/Cargo.lock index 9089207e4..3492e35b6 100644 --- a/crates/symmetric_executor/Cargo.lock +++ b/crates/symmetric_executor/Cargo.lock @@ -500,7 +500,7 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "wit-bindgen" -version = "0.36.0" +version = "0.37.0" dependencies = [ "wit-bindgen-rt", "wit-bindgen-rust-macro", @@ -508,7 +508,7 @@ dependencies = [ [[package]] name = "wit-bindgen-core" -version = "0.36.0" +version = "0.37.0" dependencies = [ "anyhow", "heck", @@ -517,7 +517,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rt" -version = "0.36.0" +version = "0.37.0" dependencies = [ "bitflags", "futures", @@ -526,7 +526,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust" -version = "0.36.0" +version = "0.37.0" dependencies = [ "anyhow", "heck", @@ -540,7 +540,7 @@ dependencies = [ [[package]] name = "wit-bindgen-rust-macro" -version = "0.36.0" +version = "0.37.0" dependencies = [ "anyhow", "prettyplease", diff --git a/crates/symmetric_executor/Cargo.toml b/crates/symmetric_executor/Cargo.toml index e3fd9e1d8..6c2b1a459 100644 --- a/crates/symmetric_executor/Cargo.toml +++ b/crates/symmetric_executor/Cargo.toml @@ -10,8 +10,8 @@ version.workspace = true [dependencies] futures = "0.3.31" libc = "0.2.167" -wit-bindgen = { version = "0.36.0", path = "../guest-rust" } -wit-bindgen-rt = { version = "0.36.0", path = "../guest-rust/rt" } +wit-bindgen = { path = "../guest-rust" } +wit-bindgen-rt = { path = "../guest-rust/rt" } [lib] crate-type = ["cdylib"] diff --git a/crates/symmetric_executor/rust-client/Cargo.toml b/crates/symmetric_executor/rust-client/Cargo.toml index 0fc08d41d..9cbf22fa5 100644 --- a/crates/symmetric_executor/rust-client/Cargo.toml +++ b/crates/symmetric_executor/rust-client/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] futures = "0.3.31" -wit-bindgen = { version = "0.36.0", path = "../../guest-rust" } +wit-bindgen = { path = "../../guest-rust" } [features] # always off feature diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index dc4463463..52bfbdeae 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -12,17 +12,14 @@ use crate::{ subscribe_event_send_ptr, }; +pub use stream_support::{results, Stream, StreamHandle2, StreamReader, StreamWriter}; + +// pub use futures; + mod future_support; // later make it non-pub pub mod stream_support; -pub use { - // future_support::{FutureReader, FutureVtable, FutureWriter}, - stream_support::{results, Stream, StreamHandle2, StreamReader, StreamWriter}, -}; - -pub use futures; - // 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 {} @@ -38,11 +35,6 @@ struct FutureState { waiting_for: Option, } -// pub use stream::{results, Stream}; - -// pub mod stream { -// } - static VTABLE: RawWakerVTable = RawWakerVTable::new( |_| RawWaker::new(core::ptr::null(), &VTABLE), // `wake` does nothing @@ -137,12 +129,7 @@ pub fn first_poll( /// Await the completion of a call to an async-lowered import. #[doc(hidden)] -pub async unsafe fn await_result( - function: impl Fn() -> *mut u8, - // unsafe extern "C" fn(*mut u8, *mut u8) -> *mut u8, - // params: *mut u8, - // results: *mut u8, -) { +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) }; @@ -150,11 +137,6 @@ pub async unsafe fn await_result( } } -// #[doc(hidden)] -// pub unsafe fn callback(_ctx: *mut u8, _event0: i32, _event1: i32, _event2: i32) -> i32 { -// todo!() -// } - pub fn spawn(future: impl Future + 'static + Send) { let wait_for = first_poll(future, |()| ()); let wait_for = unsafe { EventSubscription::from_handle(wait_for as usize) }; diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index 134b4da51..3e5ebb7a6 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -17,7 +17,7 @@ pub fn register( } #[no_mangle] -fn cabi_realloc_wit_bindgen_0_36_0( +fn cabi_realloc_wit_bindgen_0_37_0( _old_ptr: *mut u8, _old_len: usize, _align: usize, From 06af93e4c0e23d9af9b5a6aefd73779f6f8b54b8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 11 Jan 2025 23:18:41 +0100 Subject: [PATCH 461/672] start moving stream impl into shared object --- crates/symmetric_executor/Cargo.lock | 333 +++- crates/symmetric_executor/Cargo.toml | 1 + crates/symmetric_executor/dummy-rt/Cargo.toml | 6 + crates/symmetric_executor/dummy-rt/src/lib.rs | 6 + crates/symmetric_executor/generate.sh | 1 + .../symmetric_stream/Cargo.toml | 14 + .../symmetric_stream/src/lib.rs | 104 ++ .../symmetric_stream/src/stream_impl.rs | 1384 +++++++++++++++++ crates/symmetric_executor/wit/executor.wit | 35 + crates/symmetric_executor/wit/world.wit | 5 + 10 files changed, 1879 insertions(+), 10 deletions(-) create mode 100644 crates/symmetric_executor/dummy-rt/Cargo.toml create mode 100644 crates/symmetric_executor/dummy-rt/src/lib.rs create mode 100644 crates/symmetric_executor/symmetric_stream/Cargo.toml create mode 100644 crates/symmetric_executor/symmetric_stream/src/lib.rs create mode 100644 crates/symmetric_executor/symmetric_stream/src/stream_impl.rs diff --git a/crates/symmetric_executor/Cargo.lock b/crates/symmetric_executor/Cargo.lock index 3492e35b6..b015b386c 100644 --- a/crates/symmetric_executor/Cargo.lock +++ b/crates/symmetric_executor/Cargo.lock @@ -59,6 +59,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dummy-rt" +version = "0.1.0" + [[package]] name = "equivalent" version = "1.0.1" @@ -71,6 +86,15 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "futures" version = "0.3.31" @@ -181,12 +205,151 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "id-arena" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.7.0" @@ -216,6 +379,12 @@ version = "0.2.167" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "log" version = "0.4.22" @@ -252,6 +421,12 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "pin-project-lite" version = "0.2.15" @@ -366,6 +541,12 @@ dependencies = [ "smallvec", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "symmetric_executor" version = "0.1.0" @@ -376,6 +557,14 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "symmetric_stream" +version = "0.1.0" +dependencies = [ + "dummy-rt", + "symmetric_executor", +] + [[package]] name = "syn" version = "2.0.90" @@ -387,6 +576,27 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "unicode-ident" version = "1.0.14" @@ -399,10 +609,33 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "wasm-encoder" -version = "0.221.2" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" +version = "0.223.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "leb128", "wasmparser", @@ -410,8 +643,8 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.221.2" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" +version = "0.223.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "anyhow", "indexmap", @@ -419,14 +652,15 @@ dependencies = [ "serde_derive", "serde_json", "spdx", + "url", "wasm-encoder", "wasmparser", ] [[package]] name = "wasmparser" -version = "0.221.2" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" +version = "0.223.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "bitflags", "hashbrown", @@ -553,8 +787,8 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.221.2" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" +version = "0.223.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "anyhow", "bitflags", @@ -571,8 +805,8 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.221.2" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#50a75a9c0690ad9b8280655253cf4b7b25b9c41e" +version = "0.223.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" dependencies = [ "anyhow", "id-arena", @@ -585,3 +819,82 @@ dependencies = [ "unicode-xid", "wasmparser", ] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/crates/symmetric_executor/Cargo.toml b/crates/symmetric_executor/Cargo.toml index 6c2b1a459..e67589332 100644 --- a/crates/symmetric_executor/Cargo.toml +++ b/crates/symmetric_executor/Cargo.toml @@ -1,6 +1,7 @@ [workspace] package.version = "0.1.0" package.edition = "2021" +members = [ "dummy-rt","symmetric_stream"] [package] name = "symmetric_executor" 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 index 5ca2a9855..8f8b24ab5 100755 --- a/crates/symmetric_executor/generate.sh +++ b/crates/symmetric_executor/generate.sh @@ -1,3 +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/symmetric_stream/Cargo.toml b/crates/symmetric_executor/symmetric_stream/Cargo.toml new file mode 100644 index 000000000..d0d072f13 --- /dev/null +++ b/crates/symmetric_executor/symmetric_stream/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "symmetric_stream" +version.workspace = true +edition.workspace = true + +[dependencies] +symmetric_executor = { path = ".." } + +[dependencies.wit-bindgen] +package = "dummy-rt" +path = "../dummy-rt" + +[lib] +crate-type = ["cdylib"] 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..91a6df36d --- /dev/null +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -0,0 +1,104 @@ +use stream_impl::exports::symmetric::runtime::symmetric_stream::{self, GuestAddress, GuestBuffer, GuestStreamObj}; + +mod stream_impl; + +struct Guest; + +stream_impl::export!(Guest with_types_in stream_impl); + +struct Dummy; + +impl GuestAddress for Dummy {} + +struct Buffer { + addr: *mut (), + size: usize, +} + +impl GuestBuffer for Buffer { + fn new(addr: symmetric_stream::Address,capacity: u64,) -> Self { + todo!() + } + + fn get_address(&self,) -> symmetric_stream::Address { + todo!() + } + + fn get_size(&self,) -> u64 { + todo!() + } + + fn set_size(&self,size: u64,) -> () { + todo!() + } + + fn capacity(&self,) -> u64 { + todo!() + } +} + +struct StreamObj { + +} + +impl GuestStreamObj for StreamObj { + fn new() -> Self { + todo!() + } + + fn is_write_closed(&self,) -> bool { + todo!() + } + + fn start_reading(&self,buffer: symmetric_stream::Buffer,) -> () { + todo!() + } + + fn read_ready_event(&self,) -> symmetric_stream::EventGenerator { + todo!() + } + + fn read_result(&self,) -> symmetric_stream::Buffer { + todo!() + } + + fn close_read(stream: symmetric_stream::StreamObj,) -> () { + todo!() + } + + fn is_ready_to_write(&self,) -> bool { + todo!() + } + + fn write_ready_event(&self,) -> symmetric_stream::EventGenerator { + todo!() + } + + fn start_writing(&self,) -> symmetric_stream::Buffer { + todo!() + } + + fn finish_writing(&self,buffer: symmetric_stream::Buffer,) -> () { + todo!() + } + + fn close_write(stream: symmetric_stream::StreamObj,) -> () { + todo!() + } +} + +impl symmetric_stream::Guest for Guest { + type Address = Dummy; + + type Buffer = Buffer; + + type StreamObj = StreamObj; + + fn end_of_file() -> symmetric_stream::Buffer { + todo!() + } + + fn is_end_of_file(obj: symmetric_stream::BufferBorrow<'_>,) -> bool { + todo!() + } +} 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..4a452db0d --- /dev/null +++ b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs @@ -0,0 +1,1384 @@ +// 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, + } + + 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, + } + + 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, + } + + 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, + } + + 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 EventGenerator = super::super::super::super::symmetric::runtime::symmetric_executor::EventGenerator; + + #[derive(Debug)] + #[repr(transparent)] + pub struct Address{ + handle: _rt::Resource
, + } + + type _AddressRep = Option; + + 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(val: T) -> Self { + Self::type_guard::(); + let val: _AddressRep = Some(val); + let ptr: *mut _AddressRep = + _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(&self) -> &T { + let ptr = unsafe { &*self.as_ptr::() }; + ptr.as_ref().unwrap() + } + + /// Gets mutable access to the underlying `T` which represents this + /// resource. + pub fn get_mut(&mut self) -> &mut T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.as_mut().unwrap() + } + + /// Consumes this resource and returns the underlying `T`. + pub fn into_inner(self) -> T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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() { + use core::any::TypeId; + static mut LAST_TYPE: Option = None; + unsafe { + assert!(!cfg!(target_feature = "atomics")); + let id = TypeId::of::(); + 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(handle: *mut u8) { + Self::type_guard::(); + let _ = _rt::Box::from_raw(handle as *mut _AddressRep); + } + + fn as_ptr(&self) -> *mut _AddressRep { + Address::type_guard::(); + 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(&self) -> &T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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(&self) -> *mut _AddressRep { + Address::type_guard::(); + 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, + } + + type _BufferRep = Option; + + 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(val: T) -> Self { + Self::type_guard::(); + let val: _BufferRep = Some(val); + let ptr: *mut _BufferRep = + _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(&self) -> &T { + let ptr = unsafe { &*self.as_ptr::() }; + ptr.as_ref().unwrap() + } + + /// Gets mutable access to the underlying `T` which represents this + /// resource. + pub fn get_mut(&mut self) -> &mut T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.as_mut().unwrap() + } + + /// Consumes this resource and returns the underlying `T`. + pub fn into_inner(self) -> T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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() { + use core::any::TypeId; + static mut LAST_TYPE: Option = None; + unsafe { + assert!(!cfg!(target_feature = "atomics")); + let id = TypeId::of::(); + 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(handle: *mut u8) { + Self::type_guard::(); + let _ = _rt::Box::from_raw(handle as *mut _BufferRep); + } + + fn as_ptr(&self) -> *mut _BufferRep { + Buffer::type_guard::(); + 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(&self) -> &T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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(&self) -> *mut _BufferRep { + Buffer::type_guard::(); + 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, + } + + type _StreamObjRep = Option; + + 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(val: T) -> Self { + Self::type_guard::(); + let val: _StreamObjRep = Some(val); + let ptr: *mut _StreamObjRep = + _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(&self) -> &T { + let ptr = unsafe { &*self.as_ptr::() }; + ptr.as_ref().unwrap() + } + + /// Gets mutable access to the underlying `T` which represents this + /// resource. + pub fn get_mut(&mut self) -> &mut T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.as_mut().unwrap() + } + + /// Consumes this resource and returns the underlying `T`. + pub fn into_inner(self) -> T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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() { + use core::any::TypeId; + static mut LAST_TYPE: Option = None; + unsafe { + assert!(!cfg!(target_feature = "atomics")); + let id = TypeId::of::(); + 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(handle: *mut u8) { + Self::type_guard::(); + let _ = _rt::Box::from_raw(handle as *mut _StreamObjRep); + } + + fn as_ptr(&self) -> *mut _StreamObjRep { + StreamObj::type_guard::(); + 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(&self) -> &T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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(&self) -> *mut _StreamObjRep { + StreamObj::type_guard::(); + 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(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(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(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(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(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() -> *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_is_write_closed_cabi(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(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_read_ready_event_cabi(arg0: *mut u8,) -> *mut u8 {#[cfg(target_arch="wasm32")] +_rt::run_ctors_once();let result0 = T::read_ready_event(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(arg0: *mut u8,) -> *mut u8 {#[cfg(target_arch="wasm32")] +_rt::run_ctors_once();let result0 = T::read_result(StreamObjBorrow::lift(arg0 as usize).get()); +(result0).take_handle() as *mut u8 +} +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn _export_static_stream_obj_close_read_cabi(arg0: *mut u8,) {#[cfg(target_arch="wasm32")] +_rt::run_ctors_once();T::close_read(StreamObj::from_handle(arg0 as usize)); +} +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn _export_method_stream_obj_is_ready_to_write_cabi(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_event_cabi(arg0: *mut u8,) -> *mut u8 {#[cfg(target_arch="wasm32")] +_rt::run_ctors_once();let result0 = T::write_ready_event(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(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(arg0: *mut u8,arg1: *mut u8,) {#[cfg(target_arch="wasm32")] +_rt::run_ctors_once();T::finish_writing(StreamObjBorrow::lift(arg0 as usize).get(), Buffer::from_handle(arg1 as usize)); +} +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn _export_static_stream_obj_close_write_cabi(arg0: *mut u8,) {#[cfg(target_arch="wasm32")] +_rt::run_ctors_once();T::close_write(StreamObj::from_handle(arg0 as usize)); +} +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn _export_end_of_file_cabi() -> *mut u8 {#[cfg(target_arch="wasm32")] +_rt::run_ctors_once();let result0 = T::end_of_file(); +(result0).take_handle() as *mut u8 +} +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn _export_is_end_of_file_cabi(arg0: *mut u8,) -> i32 {#[cfg(target_arch="wasm32")] +_rt::run_ctors_once();let result0 = T::is_end_of_file(BufferBorrow::lift(arg0 as usize)); +match result0 { true => 1, false => 0 } +} +pub trait Guest { + type Address: GuestAddress; + type Buffer: GuestBuffer; + type StreamObj: GuestStreamObj; + /// special EOF buffer value (should be opaque) + fn end_of_file() -> Buffer; + fn is_end_of_file(obj: BufferBorrow<'_>,) -> bool; +} +#[doc(hidden)] +#[allow(non_snake_case)] +pub unsafe fn _export_drop_address_cabi(arg0: usize) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + Address::dtor::(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(arg0: usize) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + Buffer::dtor::(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(arg0: usize) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + StreamObj::dtor::(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; + /// reading (in roughly chronological order) + fn is_write_closed(&self,) -> bool; + fn start_reading(&self,buffer: Buffer,) -> (); + fn read_ready_event(&self,) -> EventGenerator; + fn read_result(&self,) -> Buffer; + fn close_read(stream: StreamObj,) -> (); + /// writing + fn is_ready_to_write(&self,) -> bool; + fn write_ready_event(&self,) -> EventGenerator; + fn start_writing(&self,) -> Buffer; + /// how to represent EOF? Zero buffer? + fn finish_writing(&self,buffer: Buffer,) -> (); + fn close_write(stream: StreamObj,) -> (); +} +#[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.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.read-ready-event")] + #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_event(arg0: *mut u8,) -> *mut u8 { + $($path_to_types)*::_export_method_stream_obj_read_ready_event_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,) -> *mut u8 { + $($path_to_types)*::_export_method_stream_obj_read_result_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0) + } + #[cfg_attr(target_arch = "wasm32", export_name = "[static]stream-obj.close-read")] + #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BstaticX5Dstream_objX2Eclose_read(arg0: *mut u8,) { + $($path_to_types)*::_export_static_stream_obj_close_read_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0) + } + #[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-event")] + #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_event(arg0: *mut u8,) -> *mut u8 { + $($path_to_types)*::_export_method_stream_obj_write_ready_event_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: *mut u8,) { + $($path_to_types)*::_export_method_stream_obj_finish_writing_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0, arg1) + } + #[cfg_attr(target_arch = "wasm32", export_name = "[static]stream-obj.close-write")] + #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BstaticX5Dstream_objX2Eclose_write(arg0: *mut u8,) { + $($path_to_types)*::_export_static_stream_obj_close_write_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0) + } + #[cfg_attr(target_arch = "wasm32", export_name = "end-of-file")] + #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00end_of_file() -> *mut u8 { + $($path_to_types)*::_export_end_of_file_cabi::<$ty>() + } + #[cfg_attr(target_arch = "wasm32", export_name = "is-end-of-file")] + #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00is_end_of_file(arg0: *mut u8,) -> i32 { + $($path_to_types)*::_export_is_end_of_file_cabi::<$ty>(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 { + // 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, + } + + /// 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 Resource { + #[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` 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` 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) -> usize { + resource.handle.swap(0, Relaxed) + } + + #[doc(hidden)] + pub fn handle(resource: &Resource) -> usize { + resource.handle.load(Relaxed) + } + } + + impl fmt::Debug for Resource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Resource") + .field("handle", &self.handle) + .finish() + } + } + + impl Drop for Resource { + 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: 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(); + } + 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; 1673] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x87\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\x0fevent\ +-generator\x01B)\x02\x03\x02\x01\x01\x04\0\x0fevent-generator\x03\0\0\x04\0\x07a\ +ddress\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\x04se\ +lf\x08\x04sizew\x01\0\x04\0\x17[method]buffer.set-size\x01\x0b\x04\0\x17[method]\ +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\x7f\x04\0\"[method]stream-obj.is-write-clos\ +ed\x01\x0f\x01@\x02\x04self\x0e\x06buffer\x06\x01\0\x04\0\x20[method]stream-obj.\ +start-reading\x01\x10\x01i\x01\x01@\x01\x04self\x0e\0\x11\x04\0#[method]stream-o\ +bj.read-ready-event\x01\x12\x01@\x01\x04self\x0e\0\x06\x04\0\x1e[method]stream-o\ +bj.read-result\x01\x13\x01@\x01\x06stream\x0c\x01\0\x04\0\x1d[static]stream-obj.\ +close-read\x01\x14\x04\0$[method]stream-obj.is-ready-to-write\x01\x0f\x04\0$[met\ +hod]stream-obj.write-ready-event\x01\x12\x04\0\x20[method]stream-obj.start-writi\ +ng\x01\x13\x04\0![method]stream-obj.finish-writing\x01\x10\x04\0\x1e[static]stre\ +am-obj.close-write\x01\x14\x01@\0\0\x06\x04\0\x0bend-of-file\x01\x15\x01@\x01\x03\ +obj\x08\0\x7f\x04\0\x0eis-end-of-file\x01\x16\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-compone\ +nt\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 index 1d45600af..9b8b978ac 100644 --- a/crates/symmetric_executor/wit/executor.wit +++ b/crates/symmetric_executor/wit/executor.wit @@ -56,3 +56,38 @@ interface symmetric-executor { /// 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-generator}; + + 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(); + // reading (in roughly chronological order) + is-write-closed: func() -> bool; + start-reading: func(buffer: buffer); + read-ready-event: func() -> event-generator; + read-result: func() -> buffer; + close-read: static func(%stream: stream-obj); + // writing + is-ready-to-write: func() -> bool; + write-ready-event: func() -> event-generator; + start-writing: func() -> buffer; + finish-writing: func(buffer: buffer); + close-write: static func(%stream: stream-obj); + } + + // special EOF buffer value (should be opaque) + end-of-file: func() -> buffer; + is-end-of-file: func(obj: borrow) -> bool; +} diff --git a/crates/symmetric_executor/wit/world.wit b/crates/symmetric_executor/wit/world.wit index 01ff009b6..57b1fbe86 100644 --- a/crates/symmetric_executor/wit/world.wit +++ b/crates/symmetric_executor/wit/world.wit @@ -4,6 +4,11 @@ world executor { export symmetric-executor; } +world stream-impl { + export symmetric-stream; +} + world module { import symmetric-executor; + import symmetric-stream; } From 823cb0114eede2779466224e2b9d7aff0937017d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 11 Jan 2025 23:23:45 +0100 Subject: [PATCH 462/672] simplify the dependencies --- crates/cpp/tests/symmetric_stream/Cargo.lock | 719 +----------------- .../tests/symmetric_stream/stream/Cargo.toml | 6 +- crates/symmetric_executor/Cargo.lock | 715 +---------------- crates/symmetric_executor/Cargo.toml | 8 +- .../symmetric_executor/rust-client/Cargo.toml | 6 +- 5 files changed, 22 insertions(+), 1432 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/Cargo.lock b/crates/cpp/tests/symmetric_stream/Cargo.lock index 8fcba0dd6..ce44875c7 100644 --- a/crates/cpp/tests/symmetric_stream/Cargo.lock +++ b/crates/cpp/tests/symmetric_stream/Cargo.lock @@ -2,30 +2,6 @@ # 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" @@ -33,63 +9,8 @@ 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 = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[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 = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] +name = "dummy-rt" +version = "0.1.0" [[package]] name = "futures" @@ -180,213 +101,12 @@ dependencies = [ "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 = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "id-arena" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[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 = "litemap" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" - [[package]] name = "main" version = "0.1.0" @@ -402,36 +122,6 @@ 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 = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - [[package]] name = "pin-project-lite" version = "0.2.15" @@ -444,16 +134,6 @@ 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" @@ -472,56 +152,6 @@ 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" @@ -531,12 +161,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "smallvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" - [[package]] name = "source" version = "0.1.0" @@ -545,28 +169,13 @@ dependencies = [ "wit-bindgen-symmetric-rt", ] -[[package]] -name = "spdx" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae30cc7bfe3656d60ee99bf6836f472b0c53dddcbf335e253329abb16e535a2" -dependencies = [ - "smallvec", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "stream" version = "0.1.0" dependencies = [ + "dummy-rt", "futures", "source", - "wit-bindgen", "wit-bindgen-symmetric-rt", ] @@ -574,10 +183,9 @@ dependencies = [ name = "symmetric_executor" version = "0.1.0" dependencies = [ + "dummy-rt", "futures", "libc", - "wit-bindgen", - "wit-bindgen-rt", ] [[package]] @@ -591,333 +199,16 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - [[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 = "url" -version = "2.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "wasm-encoder" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" -dependencies = [ - "leb128", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" -dependencies = [ - "anyhow", - "indexmap", - "serde", - "serde_derive", - "serde_json", - "spdx", - "url", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" -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.37.0" -dependencies = [ - "wit-bindgen-rt", - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.37.0" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rt" -version = "0.37.0" -dependencies = [ - "bitflags", - "futures", - "once_cell", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.37.0" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.37.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 = [ + "dummy-rt", "futures", - "wit-bindgen", -] - -[[package]] -name = "wit-component" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - -[[package]] -name = "yoke" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerofrom" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn", ] diff --git a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml index 777ab9e13..288b4c7e0 100644 --- a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml @@ -6,8 +6,12 @@ edition = "2021" [dependencies] futures = "0.3.31" source = { path = "../source" } -wit-bindgen = { path = "../../../../guest-rust" } +#wit-bindgen = { 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/symmetric_executor/Cargo.lock b/crates/symmetric_executor/Cargo.lock index b015b386c..d32aa5c0b 100644 --- a/crates/symmetric_executor/Cargo.lock +++ b/crates/symmetric_executor/Cargo.lock @@ -2,99 +2,16 @@ # 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 = "displaydoc" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "dummy-rt" version = "0.1.0" -[[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 = "form_urlencoded" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" -dependencies = [ - "percent-encoding", -] - [[package]] name = "futures" version = "0.3.31" @@ -184,249 +101,18 @@ dependencies = [ "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 = "icu_collections" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" -dependencies = [ - "displaydoc", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locid" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_locid_transform" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_locid_transform_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_locid_transform_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" - -[[package]] -name = "icu_normalizer" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "utf16_iter", - "utf8_iter", - "write16", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" - -[[package]] -name = "icu_properties" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" -dependencies = [ - "displaydoc", - "icu_collections", - "icu_locid_transform", - "icu_properties_data", - "icu_provider", - "tinystr", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" - -[[package]] -name = "icu_provider" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" -dependencies = [ - "displaydoc", - "icu_locid", - "icu_provider_macros", - "stable_deref_trait", - "tinystr", - "writeable", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_provider_macros" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "id-arena" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" - -[[package]] -name = "idna" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" -dependencies = [ - "idna_adapter", - "smallvec", - "utf8_iter", -] - -[[package]] -name = "idna_adapter" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" -dependencies = [ - "icu_normalizer", - "icu_properties", -] - -[[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 = "litemap" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" - -[[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 = "percent-encoding" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" - [[package]] name = "pin-project-lite" version = "0.2.15" @@ -439,16 +125,6 @@ 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" @@ -467,56 +143,6 @@ 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" @@ -526,35 +152,13 @@ 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 = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - [[package]] name = "symmetric_executor" version = "0.1.0" dependencies = [ + "dummy-rt", "futures", "libc", - "wit-bindgen", - "wit-bindgen-rt", ] [[package]] @@ -576,325 +180,8 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tinystr" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" -dependencies = [ - "displaydoc", - "zerovec", -] - [[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 = "url" -version = "2.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf16_iter" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "wasm-encoder" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" -dependencies = [ - "leb128", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" -dependencies = [ - "anyhow", - "indexmap", - "serde", - "serde_derive", - "serde_json", - "spdx", - "url", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" -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.37.0" -dependencies = [ - "wit-bindgen-rt", - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.37.0" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rt" -version = "0.37.0" -dependencies = [ - "bitflags", - "futures", - "once_cell", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.37.0" -dependencies = [ - "anyhow", - "heck", - "indexmap", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.37.0" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" -dependencies = [ - "anyhow", - "bitflags", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - -[[package]] -name = "write16" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" - -[[package]] -name = "writeable" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" - -[[package]] -name = "yoke" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" -dependencies = [ - "serde", - "stable_deref_trait", - "yoke-derive", - "zerofrom", -] - -[[package]] -name = "yoke-derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerofrom" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" -dependencies = [ - "zerofrom-derive", -] - -[[package]] -name = "zerofrom-derive" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zerovec" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" -dependencies = [ - "yoke", - "zerofrom", - "zerovec-derive", -] - -[[package]] -name = "zerovec-derive" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/crates/symmetric_executor/Cargo.toml b/crates/symmetric_executor/Cargo.toml index e67589332..f85d2f5d1 100644 --- a/crates/symmetric_executor/Cargo.toml +++ b/crates/symmetric_executor/Cargo.toml @@ -11,8 +11,12 @@ version.workspace = true [dependencies] futures = "0.3.31" libc = "0.2.167" -wit-bindgen = { path = "../guest-rust" } -wit-bindgen-rt = { path = "../guest-rust/rt" } +#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"] diff --git a/crates/symmetric_executor/rust-client/Cargo.toml b/crates/symmetric_executor/rust-client/Cargo.toml index 9cbf22fa5..18ca89d75 100644 --- a/crates/symmetric_executor/rust-client/Cargo.toml +++ b/crates/symmetric_executor/rust-client/Cargo.toml @@ -7,7 +7,11 @@ edition = "2021" [dependencies] futures = "0.3.31" -wit-bindgen = { path = "../../guest-rust" } +#wit-bindgen = { path = "../../guest-rust" } + +[dependencies.wit-bindgen] +package = "dummy-rt" +path = "../dummy-rt" [features] # always off feature From 703c5f4be7fed25483395693dcee0aaab42b2722 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 11 Jan 2025 23:46:32 +0100 Subject: [PATCH 463/672] Buffer implementation --- .../symmetric_stream/src/lib.rs | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index 91a6df36d..bce36fdfc 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -1,4 +1,6 @@ -use stream_impl::exports::symmetric::runtime::symmetric_stream::{self, GuestAddress, GuestBuffer, GuestStreamObj}; +use std::{mem::transmute, sync::atomic::{AtomicUsize, Ordering}}; + +use stream_impl::exports::symmetric::runtime::symmetric_stream::{self, Address, GuestAddress, GuestBuffer, GuestStreamObj}; mod stream_impl; @@ -12,28 +14,29 @@ impl GuestAddress for Dummy {} struct Buffer { addr: *mut (), - size: usize, + capacity: usize, + size: AtomicUsize, } impl GuestBuffer for Buffer { fn new(addr: symmetric_stream::Address,capacity: u64,) -> Self { - todo!() + Self { addr: addr.take_handle() as *mut (), size: AtomicUsize::new(0), capacity: capacity as usize} } fn get_address(&self,) -> symmetric_stream::Address { - todo!() + unsafe { Address::from_handle(self.addr as usize) } } fn get_size(&self,) -> u64 { - todo!() + self.size.load(Ordering::Relaxed) as u64 } fn set_size(&self,size: u64,) -> () { - todo!() + self.size.store(size as usize, Ordering::Relaxed) } fn capacity(&self,) -> u64 { - todo!() + self.capacity as u64 } } @@ -87,6 +90,8 @@ impl GuestStreamObj for StreamObj { } } +const EOF_MARKER: usize = 1; + impl symmetric_stream::Guest for Guest { type Address = Dummy; @@ -95,10 +100,11 @@ impl symmetric_stream::Guest for Guest { type StreamObj = StreamObj; fn end_of_file() -> symmetric_stream::Buffer { - todo!() + unsafe { symmetric_stream::Buffer::from_handle(EOF_MARKER) } } fn is_end_of_file(obj: symmetric_stream::BufferBorrow<'_>,) -> bool { - todo!() + let ptr: *mut () = unsafe { transmute(obj) }; + ptr as usize == EOF_MARKER } } From 1af9f2219d8796f62eb982fa103eb605945f61b7 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 12 Jan 2025 00:22:37 +0100 Subject: [PATCH 464/672] needs more thought --- crates/symmetric_executor/Cargo.lock | 9 + crates/symmetric_executor/Cargo.toml | 2 +- .../symmetric_executor/rust-client/Cargo.toml | 2 +- .../src/async_support/stream_support.rs | 151 +- .../rust-client/src/module.rs | 1306 ++++----- .../symmetric_stream/Cargo.toml | 1 + .../symmetric_stream/src/lib.rs | 165 +- .../symmetric_stream/src/stream_impl.rs | 2409 +++++++++-------- 8 files changed, 1927 insertions(+), 2118 deletions(-) diff --git a/crates/symmetric_executor/Cargo.lock b/crates/symmetric_executor/Cargo.lock index d32aa5c0b..d4123b331 100644 --- a/crates/symmetric_executor/Cargo.lock +++ b/crates/symmetric_executor/Cargo.lock @@ -167,6 +167,7 @@ version = "0.1.0" dependencies = [ "dummy-rt", "symmetric_executor", + "wit-bindgen-symmetric-rt", ] [[package]] @@ -185,3 +186,11 @@ 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 index f85d2f5d1..1cfed0fe6 100644 --- a/crates/symmetric_executor/Cargo.toml +++ b/crates/symmetric_executor/Cargo.toml @@ -1,7 +1,7 @@ [workspace] package.version = "0.1.0" package.edition = "2021" -members = [ "dummy-rt","symmetric_stream"] +members = [ "dummy-rt","symmetric_stream","rust-client" ] [package] name = "symmetric_executor" diff --git a/crates/symmetric_executor/rust-client/Cargo.toml b/crates/symmetric_executor/rust-client/Cargo.toml index 18ca89d75..61981dda6 100644 --- a/crates/symmetric_executor/rust-client/Cargo.toml +++ b/crates/symmetric_executor/rust-client/Cargo.toml @@ -1,4 +1,4 @@ -[workspace] +#[workspace] [package] name = "wit-bindgen-symmetric-rt" 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 index ea6f80de8..9ba94817f 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -1,5 +1,6 @@ -use std::sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}; +//use std::sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}; +pub use crate::module::symmetric::runtime::symmetric_stream::StreamObj as Stream; use crate::{ activate_event_send_ptr, async_support::wait_on, subscribe_event_send_ptr, EventGenerator, }; @@ -35,7 +36,7 @@ pub struct StreamWriter { impl StreamWriter { #[doc(hidden)] - pub fn new(handle: *mut Stream) -> Self { + pub fn new(handle: Stream) -> Self { Self { handle: StreamHandle2(handle), future: None, @@ -62,14 +63,14 @@ impl Sink> for StreamWriter { fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let me = self.get_mut(); - let ready = unsafe { is_ready_to_write(me.handle.0) }; + let ready = me.handle.0.is_ready_to_write(); // see also StreamReader::poll_next if !ready && me.future.is_none() { let handle = StreamHandle2(me.handle.0); me.future = Some(Box::pin(async move { let handle_local = handle; - let subscr = unsafe { subscribe_event_send_ptr(write_ready_event(handle_local.0)) }; + let subscr = handle_local.0.write_ready_event(); subscr.reset(); wait_on(subscr).await; }) as Pin + Send>>); @@ -92,14 +93,14 @@ impl Sink> for StreamWriter { let item_len = item.len(); let me = self.get_mut(); let stream = me.handle.0; - let Slice { addr, size } = unsafe { start_writing(stream) }; + let Slice { addr, size } = stream.start_writing(); assert!(size >= item_len); let slice = unsafe { std::slice::from_raw_parts_mut(addr.cast::>(), item_len) }; for (a, b) in slice.iter_mut().zip(item.drain(..)) { a.write(b); } - unsafe { finish_writing(stream, item_len as isize) }; + stream.finish_writing(item_len as isize); Ok(()) } @@ -114,12 +115,10 @@ impl Sink> for StreamWriter { impl Drop for StreamWriter { fn drop(&mut self) { - if !unsafe { is_write_closed(self.handle.0) } { - unsafe { - finish_writing(self.handle.0, results::CLOSED); - } + if !self.handle.0.is_write_closed() { + self.handle.0.finish_writing(results::CLOSED); } - unsafe { close_write(self.handle.0) }; + self.handle.0.close_write(); } } @@ -151,7 +150,7 @@ impl fmt::Debug for StreamReader { impl StreamReader { #[doc(hidden)] - pub fn new(handle: *mut Stream) -> Self { + pub fn new(handle: Stream) -> Self { Self { handle: StreamHandle2(handle), future: None, @@ -159,8 +158,8 @@ impl StreamReader { } } #[doc(hidden)] - pub fn into_handle(self) -> *mut Stream { - ManuallyDrop::new(self).handle.0 + pub fn into_handle(self) -> *mut () { + self.handle.0.take_handle() as *mut () } } @@ -219,132 +218,20 @@ impl futures::stream::Stream for StreamReader { impl Drop for StreamReader { fn drop(&mut self) { - unsafe { activate_event_send_ptr(write_ready_event(self.handle.0)) }; - unsafe { close_read(self.handle.0) }; - } -} - -pub struct Stream { - read_ready_event_send: *mut (), - write_ready_event_send: *mut (), - read_addr: AtomicPtr<()>, - read_size: AtomicUsize, - ready_size: AtomicIsize, - active_instances: AtomicUsize, -} - -pub unsafe extern "C" fn start_reading(stream: *mut Stream, buf: *mut (), size: usize) -> isize { - let old_ready = unsafe { &*stream }.ready_size.load(Ordering::Acquire); - if old_ready == results::CLOSED { - return old_ready; - } - assert!(old_ready == results::BLOCKED); - let old_size = unsafe { &mut *stream } - .read_size - .swap(size, Ordering::Acquire); - assert_eq!(old_size, 0); - let old_ptr = unsafe { &mut *stream } - .read_addr - .swap(buf, Ordering::Release); - assert_eq!(old_ptr, std::ptr::null_mut()); - let write_evt = unsafe { &mut *stream }.write_ready_event_send; - unsafe { activate_event_send_ptr(write_evt) }; - results::BLOCKED -} - -pub unsafe extern "C" fn read_ready_event(stream: *const Stream) -> *mut () { - unsafe { (&*stream).read_ready_event_send } -} - -pub unsafe extern "C" fn write_ready_event(stream: *const Stream) -> *mut () { - unsafe { (&*stream).write_ready_event_send } -} - -pub unsafe extern "C" fn is_ready_to_write(stream: *const Stream) -> bool { - !unsafe { &*stream } - .read_addr - .load(Ordering::Acquire) - .is_null() -} - -pub unsafe extern "C" fn is_write_closed(stream: *const Stream) -> bool { - unsafe { &*stream }.ready_size.load(Ordering::Acquire) == results::CLOSED -} - -#[repr(C)] -pub struct Slice { - pub addr: *mut (), - pub size: usize, -} - -pub unsafe extern "C" fn start_writing(stream: *mut Stream) -> Slice { - let size = unsafe { &*stream }.read_size.swap(0, Ordering::Acquire); - let addr = unsafe { &*stream } - .read_addr - .swap(core::ptr::null_mut(), Ordering::Release); - Slice { addr, size } -} - -pub unsafe extern "C" fn read_amount(stream: *const Stream) -> isize { - unsafe { &*stream } - .ready_size - .swap(results::BLOCKED, Ordering::Acquire) -} - -pub unsafe extern "C" fn finish_writing(stream: *mut Stream, elements: isize) { - let old_ready = unsafe { &*stream } - .ready_size - .swap(elements as isize, Ordering::Release); - assert_eq!(old_ready, results::BLOCKED); - unsafe { activate_event_send_ptr(read_ready_event(stream)) }; -} - -pub unsafe extern "C" fn close_read(stream: *mut Stream) { - let refs = unsafe { &mut *stream } - .active_instances - .fetch_sub(1, Ordering::AcqRel); - if refs == 1 { - let obj = Box::from_raw(stream); - drop(EventGenerator::from_handle( - obj.read_ready_event_send as usize, - )); - drop(EventGenerator::from_handle( - obj.write_ready_event_send as usize, - )); - drop(obj); - } -} - -pub unsafe extern "C" fn close_write(stream: *mut Stream) { - // same for write (for now) - close_read(stream); -} - -pub extern "C" fn create_stream() -> *mut Stream { - Box::into_raw(Box::new(Stream::new())) -} - -impl Stream { - fn new() -> Self { - Self { - // vtable: &STREAM_VTABLE as *const StreamVtable, - read_ready_event_send: EventGenerator::new().take_handle() as *mut (), - write_ready_event_send: EventGenerator::new().take_handle() as *mut (), - read_addr: AtomicPtr::new(core::ptr::null_mut()), - read_size: AtomicUsize::new(0), - ready_size: AtomicIsize::new(results::BLOCKED), - active_instances: AtomicUsize::new(2), - } + self.handle.0.write_ready_event().activate(); + Stream::close_read(self.handle.0); + // unsafe { activate_event_send_ptr(write_ready_event(self.handle.0)) }; + // unsafe { close_read(self.handle.0) }; } } // Stream handles are Send, so wrap them #[repr(transparent)] -pub struct StreamHandle2(pub *mut Stream); +pub struct StreamHandle2(Stream); unsafe impl Send for StreamHandle2 {} unsafe impl Sync for StreamHandle2 {} pub fn new_stream() -> (StreamWriter, StreamReader) { - let handle = create_stream(); + let handle = Stream::new(); (StreamWriter::new(handle), StreamReader::new(handle)) } diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index d6be6cae9..5c8c60a74 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -1,4 +1,4 @@ -// Generated by `wit-bindgen` 0.36.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.37.0. DO NOT EDIT! // Options used: #[allow(dead_code, clippy::all)] pub mod symmetric { @@ -6,7 +6,7 @@ pub mod symmetric { /// 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)] + #[allow(dead_code, unused_imports, clippy::all)] pub mod symmetric_executor { #[used] #[doc(hidden)] @@ -399,7 +399,6 @@ pub mod symmetric { 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", @@ -450,6 +449,485 @@ pub mod symmetric { } } } + + /// 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 EventGenerator = + super::super::super::symmetric::runtime::symmetric_executor::EventGenerator; + + #[derive(Debug)] + #[repr(transparent)] + pub struct Address { + handle: _rt::Resource
, + } + + 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, + } + + 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, + } + + 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")] + 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)] + /// 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 read_ready_event(&self) -> EventGenerator { + 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-event" + )] + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_event( + _: *mut u8, + ) -> *mut u8; + } + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_event((self).handle() as *mut u8); + super::super::super::symmetric::runtime::symmetric_executor::EventGenerator::from_handle(ret as usize) + } + } + } + impl StreamObj { + #[allow(unused_unsafe, clippy::all)] + pub fn read_result(&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.read-result" + )] + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_result( + _: *mut u8, + ) -> *mut u8; + } + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_result((self).handle() as *mut u8); + Buffer::from_handle(ret as usize) + } + } + } + impl StreamObj { + #[allow(unused_unsafe, clippy::all)] + pub fn close_read(stream: StreamObj) -> () { + unsafe { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] + extern "C" { + #[cfg_attr( + target_arch = "wasm32", + link_name = "[static]stream-obj.close-read" + )] + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BstaticX5Dstream_objX2Eclose_read( + _: *mut u8, + ); + } + symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BstaticX5Dstream_objX2Eclose_read((&stream).take_handle() as *mut u8); + } + } + } + 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_event(&self) -> EventGenerator { + 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-event" + )] + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_event( + _: *mut u8, + ) -> *mut u8; + } + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_event((self).handle() as *mut u8); + super::super::super::symmetric::runtime::symmetric_executor::EventGenerator::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)] + pub fn finish_writing(&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.finish-writing" + )] + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing( + _: *mut u8, + _: *mut u8, + ); + } + symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing((self).handle() as *mut u8, (&buffer).take_handle() as *mut u8); + } + } + } + impl StreamObj { + #[allow(unused_unsafe, clippy::all)] + pub fn close_write(stream: StreamObj) -> () { + unsafe { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] + extern "C" { + #[cfg_attr( + target_arch = "wasm32", + link_name = "[static]stream-obj.close-write" + )] + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BstaticX5Dstream_objX2Eclose_write( + _: *mut u8, + ); + } + symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BstaticX5Dstream_objX2Eclose_write((&stream).take_handle() as *mut u8); + } + } + } + #[allow(unused_unsafe, clippy::all)] + /// special EOF buffer value (should be opaque) + pub fn end_of_file() -> Buffer { + unsafe { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "end-of-file")] + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00end_of_file( + ) -> *mut u8; + } + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00end_of_file(); + Buffer::from_handle(ret as usize) + } + } + #[allow(unused_unsafe, clippy::all)] + pub fn is_end_of_file(obj: &Buffer) -> bool { + unsafe { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "is-end-of-file")] + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00is_end_of_file( + _: *mut u8, + ) -> i32; + } + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00is_end_of_file( + (obj).handle() as *mut u8, + ); + _rt::bool_lift(ret as u8) + } + } + } } } mod _rt { @@ -588,803 +1066,15 @@ mod _rt { self as i64 } } - #[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; - 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 { - handle: u32, - _phantom: PhantomData, - } - - impl fmt::Debug for FutureWriter { - 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 { - writer: Option>, - future: Pin>>, - } - - impl Future for CancelableWrite { - 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 CancelableWrite { - /// 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 { - self.cancel_mut() - } - - fn cancel_mut(&mut self) -> FutureWriter { - 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 Drop for CancelableWrite { - fn drop(&mut self) { - if self.writer.is_some() { - self.cancel_mut(); - } - } - } - - impl FutureWriter { - /// Write the specified value to this `future`. - pub fn write(self, v: T) -> CancelableWrite { - 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>> - } - 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 Drop for FutureWriter { - 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 { - reader: Option>, - future: Pin>>>, - } - - impl Future for CancelableRead { - type Output = Option; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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 CancelableRead { - /// 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 { - self.cancel_mut() - } - - fn cancel_mut(&mut self) -> FutureReader { - 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 Drop for CancelableRead { - fn drop(&mut self) { - if self.reader.is_some() { - self.cancel_mut(); - } - } - } - - /// Represents the readable end of a Component Model `future`. - pub struct FutureReader { - handle: u32, - _phantom: PhantomData, - } - - impl fmt::Debug for FutureReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FutureReader") - .field("handle", &self.handle) - .finish() - } - } - - impl FutureReader { - #[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 IntoFuture for FutureReader { - type Output = Option; - type IntoFuture = CancelableRead; - - /// 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>>, - 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 Drop for FutureReader { - 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; - async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; - fn cancel_write(stream: u32); - fn cancel_read(stream: u32); - fn close_writable(stream: u32); - fn close_readable(stream: u32); - } - - struct CancelWriteOnDrop { - handle: Option, - _phantom: PhantomData, - } - - impl Drop for CancelWriteOnDrop { - 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 { - handle: u32, - future: Option + 'static>>>, - _phantom: PhantomData, - } - - impl StreamWriter { - /// 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 fmt::Debug for StreamWriter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StreamWriter") - .field("handle", &self.handle) - .finish() - } - } - - impl Sink> for StreamWriter { - type Error = Infallible; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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) -> 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:: { - 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:: { - 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> { - self.poll_ready(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.poll_ready(cx) - } - } - - impl Drop for StreamWriter { - 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 { - handle: Option, - _phantom: PhantomData, - } - - impl Drop for CancelReadOnDrop { - 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 { - handle: u32, - future: Option>> + 'static>>>, - _phantom: PhantomData, - } - - impl StreamReader { - /// 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 fmt::Debug for StreamReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StreamReader") - .field("handle", &self.handle) - .finish() - } - } - - impl StreamReader { - #[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 Stream for StreamReader { - type Item = Vec; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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:: { - handle: Some(handle), - _phantom: PhantomData, - }; - Box::pin(async move { - let mut buffer = iter::repeat_with(MaybeUninit::uninit) - .take(ceiling(64 * 1024, mem::size_of::())) - .collect::>(); - - let result = if let Some(count) = - T::read(handle, &mut buffer).await - { - buffer.truncate(count); - Some(unsafe { - mem::transmute::>, Vec>(buffer) - }) - } else { - None - }; - cancel_on_drop.handle = None; - drop(cancel_on_drop); - result - }) - as Pin>> - } - Handle::LocalOpen => { - let (tx, rx) = oneshot::channel(); - entry.insert(Handle::LocalWaiting(tx)); - let mut cancel_on_drop = CancelReadOnDrop:: { - 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 Drop for StreamReader { - 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() -> (FutureWriter, FutureReader) { - 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() -> (StreamWriter, StreamReader) { - 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 } - } - } } -#[allow(unused_imports)] -//pub use _rt::stream_and_future_support; + #[cfg(target_arch = "wasm32")] -#[link_section = "component-type:wit-bindgen:0.36.0:symmetric:runtime@0.1.0:module:encoded world"] +#[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; 789] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x98\x05\x01A\x02\x01\ -A\x02\x01B\x20\x04\0\x11callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\ +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1663] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x82\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\ @@ -1396,9 +1086,27 @@ 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\x04\0\x1esymmetric:r\ -untime/module@0.1.0\x04\0\x0b\x0c\x01\0\x06module\x03\0\0\0G\x09producers\x01\x0c\ -processed-by\x02\x0dwit-component\x070.221.2\x10wit-bindgen-rust\x060.36.0"; +\x16\x03\0*symmetric:runtime/symmetric-executor@0.1.0\x05\0\x02\x03\0\0\x0fevent\ +-generator\x01B)\x02\x03\x02\x01\x01\x04\0\x0fevent-generator\x03\0\0\x04\0\x07a\ +ddress\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\x04se\ +lf\x08\x04sizew\x01\0\x04\0\x17[method]buffer.set-size\x01\x0b\x04\0\x17[method]\ +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\x7f\x04\0\"[method]stream-obj.is-write-clos\ +ed\x01\x0f\x01@\x02\x04self\x0e\x06buffer\x06\x01\0\x04\0\x20[method]stream-obj.\ +start-reading\x01\x10\x01i\x01\x01@\x01\x04self\x0e\0\x11\x04\0#[method]stream-o\ +bj.read-ready-event\x01\x12\x01@\x01\x04self\x0e\0\x06\x04\0\x1e[method]stream-o\ +bj.read-result\x01\x13\x01@\x01\x06stream\x0c\x01\0\x04\0\x1d[static]stream-obj.\ +close-read\x01\x14\x04\0$[method]stream-obj.is-ready-to-write\x01\x0f\x04\0$[met\ +hod]stream-obj.write-ready-event\x01\x12\x04\0\x20[method]stream-obj.start-writi\ +ng\x01\x13\x04\0![method]stream-obj.finish-writing\x01\x10\x04\0\x1e[static]stre\ +am-obj.close-write\x01\x14\x01@\0\0\x06\x04\0\x0bend-of-file\x01\x15\x01@\x01\x03\ +obj\x08\0\x7f\x04\0\x0eis-end-of-file\x01\x16\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\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x070\ +.223.0\x10wit-bindgen-rust\x060.37.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/symmetric_executor/symmetric_stream/Cargo.toml b/crates/symmetric_executor/symmetric_stream/Cargo.toml index d0d072f13..6f5d50aa2 100644 --- a/crates/symmetric_executor/symmetric_stream/Cargo.toml +++ b/crates/symmetric_executor/symmetric_stream/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true [dependencies] symmetric_executor = { path = ".." } +wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../rust-client" } [dependencies.wit-bindgen] package = "dummy-rt" diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index bce36fdfc..aa9c4fd24 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -1,6 +1,12 @@ -use std::{mem::transmute, sync::atomic::{AtomicUsize, Ordering}}; +use std::{ + mem::transmute, + sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}, +}; -use stream_impl::exports::symmetric::runtime::symmetric_stream::{self, Address, GuestAddress, GuestBuffer, GuestStreamObj}; +use stream_impl::exports::symmetric::runtime::symmetric_stream::{ + self, Address, GuestAddress, GuestBuffer, GuestStreamObj, +}; +use wit_bindgen_symmetric_rt::EventGenerator; mod stream_impl; @@ -19,73 +25,132 @@ struct Buffer { } 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 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 { + fn get_address(&self) -> symmetric_stream::Address { unsafe { Address::from_handle(self.addr as usize) } } - fn get_size(&self,) -> u64 { + fn get_size(&self) -> u64 { self.size.load(Ordering::Relaxed) as u64 } - fn set_size(&self,size: u64,) -> () { + fn set_size(&self, size: u64) -> () { self.size.store(size as usize, Ordering::Relaxed) } - fn capacity(&self,) -> u64 { + fn capacity(&self) -> u64 { self.capacity as u64 } } -struct StreamObj { +mod results { + pub const BLOCKED: isize = -1; + pub const CLOSED: isize = isize::MIN; + pub const CANCELED: isize = 0; +} +struct StreamObj { + read_ready_event_send: *mut (), + write_ready_event_send: *mut (), + read_addr: AtomicPtr<()>, + read_size: AtomicUsize, + ready_size: AtomicIsize, + active_instances: AtomicUsize, } impl GuestStreamObj for StreamObj { fn new() -> Self { - todo!() - } - - fn is_write_closed(&self,) -> bool { - todo!() - } - - fn start_reading(&self,buffer: symmetric_stream::Buffer,) -> () { - todo!() - } - - fn read_ready_event(&self,) -> symmetric_stream::EventGenerator { - todo!() - } - - fn read_result(&self,) -> symmetric_stream::Buffer { - todo!() - } - - fn close_read(stream: symmetric_stream::StreamObj,) -> () { - todo!() - } - - fn is_ready_to_write(&self,) -> bool { - todo!() - } - - fn write_ready_event(&self,) -> symmetric_stream::EventGenerator { - todo!() - } - - fn start_writing(&self,) -> symmetric_stream::Buffer { - todo!() - } - - fn finish_writing(&self,buffer: symmetric_stream::Buffer,) -> () { - todo!() - } - - fn close_write(stream: symmetric_stream::StreamObj,) -> () { + Self { + read_ready_event_send: EventGenerator::new().take_handle() as *mut (), + write_ready_event_send: EventGenerator::new().take_handle() as *mut (), + read_addr: AtomicPtr::new(core::ptr::null_mut()), + read_size: AtomicUsize::new(0), + ready_size: AtomicIsize::new(results::BLOCKED), + active_instances: AtomicUsize::new(2), + } + } + + fn is_write_closed(&self) -> bool { + self.ready_size.load(Ordering::Acquire) == results::CLOSED + } + + fn start_reading(&self, buffer: symmetric_stream::Buffer) -> () { + let buf = buffer.get().get_address().take_handle() as *mut (); + let size = buffer.get().get_capacity(); + let old_ready = self.ready_size.load(Ordering::Acquire); + if old_ready == results::CLOSED { + return old_ready; + } + assert!(old_ready == results::BLOCKED); + let old_size = unsafe { &mut *stream } + .read_size + .swap(size, Ordering::Acquire); + assert_eq!(old_size, 0); + let old_ptr = unsafe { &mut *stream } + .read_addr + .swap(buf, Ordering::Release); + assert_eq!(old_ptr, std::ptr::null_mut()); + let write_evt = unsafe { &mut *stream }.write_ready_event_send; + unsafe { activate_event_send_ptr(write_evt) }; + results::BLOCKED + } + + fn read_ready_event(&self) -> symmetric_stream::EventGenerator { + unsafe { + symmetric_stream::EventGenerator::from_handle(self.read_ready_event_send as usize) + } + } + + fn read_result(&self) -> symmetric_stream::Buffer { + self.ready_size.swap(results::BLOCKED, Ordering::Acquire) + } + + fn close_read(stream: symmetric_stream::StreamObj) -> () { + let refs = unsafe { &mut *stream } + .active_instances + .fetch_sub(1, Ordering::AcqRel); + if refs == 1 { + let obj = Box::from_raw(stream); + drop(EventGenerator::from_handle( + obj.read_ready_event_send as usize, + )); + drop(EventGenerator::from_handle( + obj.write_ready_event_send as usize, + )); + drop(obj); + } + } + + fn is_ready_to_write(&self) -> bool { + self.read_addr.load(Ordering::Acquire).is_null() + } + + fn write_ready_event(&self) -> symmetric_stream::EventGenerator { + self.write_ready_event_send + } + + fn start_writing(&self) -> symmetric_stream::Buffer { + let size = self.read_size.swap(0, Ordering::Acquire); + let addr = self + .read_addr + .swap(core::ptr::null_mut(), Ordering::Release); + Slice { addr, size } + } + + fn finish_writing(&self, buffer: symmetric_stream::Buffer) -> () { + let old_ready = self.ready_size.swap(elements as isize, Ordering::Release); + assert_eq!(old_ready, results::BLOCKED); + unsafe { activate_event_send_ptr(read_ready_event(stream)) }; + } + + fn close_write(stream: symmetric_stream::StreamObj) -> () { todo!() } } @@ -103,7 +168,7 @@ impl symmetric_stream::Guest for Guest { unsafe { symmetric_stream::Buffer::from_handle(EOF_MARKER) } } - fn is_end_of_file(obj: symmetric_stream::BufferBorrow<'_>,) -> bool { + fn is_end_of_file(obj: symmetric_stream::BufferBorrow<'_>) -> bool { let ptr: *mut () = unsafe { transmute(obj) }; ptr as usize == EOF_MARKER } diff --git a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs index 4a452db0d..05634d6de 100644 --- a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs +++ b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs @@ -2,1049 +2,1192 @@ // 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, - } - - 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); + 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, } - 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, - } - - 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); + 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) + } } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data(_handle); - } - } - } - - /// The receiving side of an event - - #[derive(Debug)] - #[repr(transparent)] - pub struct EventSubscription{ - handle: _rt::Resource, - } - - 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); + 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); + } + } } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription(_handle); - } - } - } - - /// A user controlled event - - #[derive(Debug)] - #[repr(transparent)] - pub struct EventGenerator{ - handle: _rt::Resource, - } - - impl EventGenerator{ - #[doc(hidden)] - pub unsafe fn from_handle(handle: usize) -> Self { - Self { - handle: _rt::Resource::from_handle(handle), - } - } + /// This wraps opaque user data, freed by the callback once + /// it returns ready - #[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); + #[derive(Debug)] + #[repr(transparent)] + pub struct CallbackData { + handle: _rt::Resource, } - 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 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) + } } - } - } - } - impl CallStatus{ - #[doc(hidden)] - pub unsafe fn _lift(val: u8) -> CallStatus{ - if !cfg!(debug_assertions) { - return ::core::mem::transmute(val); - } + 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); + } + } + } - match val { - 0 => CallStatus::Started, - 1 => CallStatus::NotStarted, + /// The receiving side of an event - _ => 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() + #[derive(Debug)] + #[repr(transparent)] + pub struct EventSubscription { + handle: _rt::Resource, } - CallbackState::Ready => { - f.debug_tuple("CallbackState::Ready").finish() + + 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) + } } - } - } - } - impl CallbackState{ - #[doc(hidden)] - pub unsafe fn _lift(val: u8) -> CallbackState{ - if !cfg!(debug_assertions) { - return ::core::mem::transmute(val); - } + 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); + } + } + } - match val { - 0 => CallbackState::Pending, - 1 => CallbackState::Ready, + /// A user controlled event - _ => 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; + #[derive(Debug)] + #[repr(transparent)] + pub struct EventGenerator { + handle: _rt::Resource, } - 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, ); + + 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) + } } - 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; + + 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); + } + } } - 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; + + /// 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, } - 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, ); + 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(), + } + } } - 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); - } - } - } + impl CallStatus { + #[doc(hidden)] + pub unsafe fn _lift(val: u8) -> CallStatus { + if !cfg!(debug_assertions) { + return ::core::mem::transmute(val); + } - } -} -#[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 EventGenerator = super::super::super::super::symmetric::runtime::symmetric_executor::EventGenerator; - - #[derive(Debug)] - #[repr(transparent)] - pub struct Address{ - handle: _rt::Resource
, - } + match val { + 0 => CallStatus::Started, + 1 => CallStatus::NotStarted, - type _AddressRep = Option; - - 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(val: T) -> Self { - Self::type_guard::(); - let val: _AddressRep = Some(val); - let ptr: *mut _AddressRep = - _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(&self) -> &T { - let ptr = unsafe { &*self.as_ptr::() }; - ptr.as_ref().unwrap() - } - - /// Gets mutable access to the underlying `T` which represents this - /// resource. - pub fn get_mut(&mut self) -> &mut T { - let ptr = unsafe { &mut *self.as_ptr::() }; - ptr.as_mut().unwrap() - } - - /// Consumes this resource and returns the underlying `T`. - pub fn into_inner(self) -> T { - let ptr = unsafe { &mut *self.as_ptr::() }; - 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() { - use core::any::TypeId; - static mut LAST_TYPE: Option = None; - unsafe { - assert!(!cfg!(target_feature = "atomics")); - let id = TypeId::of::(); - match LAST_TYPE { - Some(ty) => assert!(ty == id, "cannot use two types with this resource type"), - None => LAST_TYPE = Some(id), - } + _ => panic!("invalid enum discriminant"), + } + } } - } - - #[doc(hidden)] - pub unsafe fn dtor(handle: *mut u8) { - Self::type_guard::(); - let _ = _rt::Box::from_raw(handle as *mut _AddressRep); - } - - fn as_ptr(&self) -> *mut _AddressRep { - Address::type_guard::(); - 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, + /// 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(), + } + } } - } - - /// Gets access to the underlying `T` in this resource. - pub fn get(&self) -> &T { - let ptr = unsafe { &mut *self.as_ptr::() }; - 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(&self) -> *mut _AddressRep { - Address::type_guard::(); - self.rep.cast() - } - } + impl CallbackState { + #[doc(hidden)] + pub unsafe fn _lift(val: u8) -> CallbackState { + if !cfg!(debug_assertions) { + return ::core::mem::transmute(val); + } - 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); - } + match val { + 0 => CallbackState::Pending, + 1 => CallbackState::Ready, - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Daddress(_handle); + _ => panic!("invalid enum discriminant"), + } + } } - } - } - /// special zero allocation/copy data type (caller provided buffer) - - #[derive(Debug)] - #[repr(transparent)] - pub struct Buffer{ - handle: _rt::Resource, - } - - type _BufferRep = Option; - - 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(val: T) -> Self { - Self::type_guard::(); - let val: _BufferRep = Some(val); - let ptr: *mut _BufferRep = - _rt::Box::into_raw(_rt::Box::new(val)); - unsafe { - Self::from_handle(T::_resource_new(ptr.cast())) + 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) + } + } } - } - - /// Gets access to the underlying `T` which represents this resource. - pub fn get(&self) -> &T { - let ptr = unsafe { &*self.as_ptr::() }; - ptr.as_ref().unwrap() - } - - /// Gets mutable access to the underlying `T` which represents this - /// resource. - pub fn get_mut(&mut self) -> &mut T { - let ptr = unsafe { &mut *self.as_ptr::() }; - ptr.as_mut().unwrap() - } - - /// Consumes this resource and returns the underlying `T`. - pub fn into_inner(self) -> T { - let ptr = unsafe { &mut *self.as_ptr::() }; - ptr.take().unwrap() - } - - #[doc(hidden)] - pub unsafe fn from_handle(handle: usize) -> Self { - Self { - handle: _rt::Resource::from_handle(handle), + 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) + } + } } - } - - #[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() { - use core::any::TypeId; - static mut LAST_TYPE: Option = None; - unsafe { - assert!(!cfg!(target_feature = "atomics")); - let id = TypeId::of::(); - match LAST_TYPE { - Some(ty) => assert!(ty == id, "cannot use two types with this resource type"), - None => LAST_TYPE = Some(id), - } + 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) + } + } } - } - - #[doc(hidden)] - pub unsafe fn dtor(handle: *mut u8) { - Self::type_guard::(); - let _ = _rt::Box::from_raw(handle as *mut _BufferRep); - } - - fn as_ptr(&self) -> *mut _BufferRep { - Buffer::type_guard::(); - 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, + 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); + } + } } - } - - /// Gets access to the underlying `T` in this resource. - pub fn get(&self) -> &T { - let ptr = unsafe { &mut *self.as_ptr::() }; - 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(&self) -> *mut _BufferRep { - Buffer::type_guard::(); - 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); + 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) + } + } } - } - } - - - #[derive(Debug)] - #[repr(transparent)] - pub struct StreamObj{ - handle: _rt::Resource, - } - - type _StreamObjRep = Option; - - 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(val: T) -> Self { - Self::type_guard::(); - let val: _StreamObjRep = Some(val); - let ptr: *mut _StreamObjRep = - _rt::Box::into_raw(_rt::Box::new(val)); - unsafe { - Self::from_handle(T::_resource_new(ptr.cast())) + 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) + } + } } - } - - /// Gets access to the underlying `T` which represents this resource. - pub fn get(&self) -> &T { - let ptr = unsafe { &*self.as_ptr::() }; - ptr.as_ref().unwrap() - } - - /// Gets mutable access to the underlying `T` which represents this - /// resource. - pub fn get_mut(&mut self) -> &mut T { - let ptr = unsafe { &mut *self.as_ptr::() }; - ptr.as_mut().unwrap() - } - - /// Consumes this resource and returns the underlying `T`. - pub fn into_inner(self) -> T { - let ptr = unsafe { &mut *self.as_ptr::() }; - 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() { - use core::any::TypeId; - static mut LAST_TYPE: Option = None; - unsafe { - assert!(!cfg!(target_feature = "atomics")); - let id = TypeId::of::(); - match LAST_TYPE { - Some(ty) => assert!(ty == id, "cannot use two types with this resource type"), - None => LAST_TYPE = Some(id), - } + 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); + } + } } - } - - #[doc(hidden)] - pub unsafe fn dtor(handle: *mut u8) { - Self::type_guard::(); - let _ = _rt::Box::from_raw(handle as *mut _StreamObjRep); - } - - fn as_ptr(&self) -> *mut _StreamObjRep { - StreamObj::type_guard::(); - 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, + #[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(); + } } - } - - /// Gets access to the underlying `T` in this resource. - pub fn get(&self) -> &T { - let ptr = unsafe { &mut *self.as_ptr::() }; - 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(&self) -> *mut _StreamObjRep { - StreamObj::type_guard::(); - 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); + #[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, + ); + } } - } } - - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn _export_constructor_buffer_cabi(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(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(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(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(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() -> *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_is_write_closed_cabi(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(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_read_ready_event_cabi(arg0: *mut u8,) -> *mut u8 {#[cfg(target_arch="wasm32")] -_rt::run_ctors_once();let result0 = T::read_ready_event(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(arg0: *mut u8,) -> *mut u8 {#[cfg(target_arch="wasm32")] -_rt::run_ctors_once();let result0 = T::read_result(StreamObjBorrow::lift(arg0 as usize).get()); -(result0).take_handle() as *mut u8 -} -#[doc(hidden)] -#[allow(non_snake_case)] -pub unsafe fn _export_static_stream_obj_close_read_cabi(arg0: *mut u8,) {#[cfg(target_arch="wasm32")] -_rt::run_ctors_once();T::close_read(StreamObj::from_handle(arg0 as usize)); -} -#[doc(hidden)] -#[allow(non_snake_case)] -pub unsafe fn _export_method_stream_obj_is_ready_to_write_cabi(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_event_cabi(arg0: *mut u8,) -> *mut u8 {#[cfg(target_arch="wasm32")] -_rt::run_ctors_once();let result0 = T::write_ready_event(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(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(arg0: *mut u8,arg1: *mut u8,) {#[cfg(target_arch="wasm32")] -_rt::run_ctors_once();T::finish_writing(StreamObjBorrow::lift(arg0 as usize).get(), Buffer::from_handle(arg1 as usize)); -} -#[doc(hidden)] -#[allow(non_snake_case)] -pub unsafe fn _export_static_stream_obj_close_write_cabi(arg0: *mut u8,) {#[cfg(target_arch="wasm32")] -_rt::run_ctors_once();T::close_write(StreamObj::from_handle(arg0 as usize)); -} -#[doc(hidden)] -#[allow(non_snake_case)] -pub unsafe fn _export_end_of_file_cabi() -> *mut u8 {#[cfg(target_arch="wasm32")] -_rt::run_ctors_once();let result0 = T::end_of_file(); -(result0).take_handle() as *mut u8 -} -#[doc(hidden)] -#[allow(non_snake_case)] -pub unsafe fn _export_is_end_of_file_cabi(arg0: *mut u8,) -> i32 {#[cfg(target_arch="wasm32")] -_rt::run_ctors_once();let result0 = T::is_end_of_file(BufferBorrow::lift(arg0 as usize)); -match result0 { true => 1, false => 0 } -} -pub trait Guest { - type Address: GuestAddress; - type Buffer: GuestBuffer; - type StreamObj: GuestStreamObj; - /// special EOF buffer value (should be opaque) - fn end_of_file() -> Buffer; - fn is_end_of_file(obj: BufferBorrow<'_>,) -> bool; -} -#[doc(hidden)] -#[allow(non_snake_case)] -pub unsafe fn _export_drop_address_cabi(arg0: usize) { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); - Address::dtor::(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(arg0: usize) { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); - Buffer::dtor::(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(arg0: usize) { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); - StreamObj::dtor::(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; - /// reading (in roughly chronological order) - fn is_write_closed(&self,) -> bool; - fn start_reading(&self,buffer: Buffer,) -> (); - fn read_ready_event(&self,) -> EventGenerator; - fn read_result(&self,) -> Buffer; - fn close_read(stream: StreamObj,) -> (); - /// writing - fn is_ready_to_write(&self,) -> bool; - fn write_ready_event(&self,) -> EventGenerator; - fn start_writing(&self,) -> Buffer; - /// how to represent EOF? Zero buffer? - fn finish_writing(&self,buffer: Buffer,) -> (); - fn close_write(stream: StreamObj,) -> (); } -#[doc(hidden)] - -macro_rules! __export_symmetric_runtime_symmetric_stream_0_1_0_cabi{ +#[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 EventGenerator = super::super::super::super::symmetric::runtime::symmetric_executor::EventGenerator; + + #[derive(Debug)] + #[repr(transparent)] + pub struct Address { + handle: _rt::Resource
, + } + + type _AddressRep = Option; + + 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(val: T) -> Self { + Self::type_guard::(); + let val: _AddressRep = Some(val); + let ptr: *mut _AddressRep = _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(&self) -> &T { + let ptr = unsafe { &*self.as_ptr::() }; + ptr.as_ref().unwrap() + } + + /// Gets mutable access to the underlying `T` which represents this + /// resource. + pub fn get_mut(&mut self) -> &mut T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.as_mut().unwrap() + } + + /// Consumes this resource and returns the underlying `T`. + pub fn into_inner(self) -> T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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() { + use core::any::TypeId; + static mut LAST_TYPE: Option = None; + unsafe { + assert!(!cfg!(target_feature = "atomics")); + let id = TypeId::of::(); + 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(handle: *mut u8) { + Self::type_guard::(); + let _ = _rt::Box::from_raw(handle as *mut _AddressRep); + } + + fn as_ptr(&self) -> *mut _AddressRep { + Address::type_guard::(); + 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(&self) -> &T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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(&self) -> *mut _AddressRep { + Address::type_guard::(); + 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, + } + + type _BufferRep = Option; + + 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(val: T) -> Self { + Self::type_guard::(); + let val: _BufferRep = Some(val); + let ptr: *mut _BufferRep = _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(&self) -> &T { + let ptr = unsafe { &*self.as_ptr::() }; + ptr.as_ref().unwrap() + } + + /// Gets mutable access to the underlying `T` which represents this + /// resource. + pub fn get_mut(&mut self) -> &mut T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.as_mut().unwrap() + } + + /// Consumes this resource and returns the underlying `T`. + pub fn into_inner(self) -> T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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() { + use core::any::TypeId; + static mut LAST_TYPE: Option = None; + unsafe { + assert!(!cfg!(target_feature = "atomics")); + let id = TypeId::of::(); + 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(handle: *mut u8) { + Self::type_guard::(); + let _ = _rt::Box::from_raw(handle as *mut _BufferRep); + } + + fn as_ptr(&self) -> *mut _BufferRep { + Buffer::type_guard::(); + 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(&self) -> &T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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(&self) -> *mut _BufferRep { + Buffer::type_guard::(); + 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, + } + + type _StreamObjRep = Option; + + 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(val: T) -> Self { + Self::type_guard::(); + let val: _StreamObjRep = Some(val); + let ptr: *mut _StreamObjRep = _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(&self) -> &T { + let ptr = unsafe { &*self.as_ptr::() }; + ptr.as_ref().unwrap() + } + + /// Gets mutable access to the underlying `T` which represents this + /// resource. + pub fn get_mut(&mut self) -> &mut T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.as_mut().unwrap() + } + + /// Consumes this resource and returns the underlying `T`. + pub fn into_inner(self) -> T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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() { + use core::any::TypeId; + static mut LAST_TYPE: Option = None; + unsafe { + assert!(!cfg!(target_feature = "atomics")); + let id = TypeId::of::(); + 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(handle: *mut u8) { + Self::type_guard::(); + let _ = _rt::Box::from_raw(handle as *mut _StreamObjRep); + } + + fn as_ptr(&self) -> *mut _StreamObjRep { + StreamObj::type_guard::(); + 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(&self) -> &T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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(&self) -> *mut _StreamObjRep { + StreamObj::type_guard::(); + 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( + 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( + 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( + 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( + 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( + 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() -> *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_is_write_closed_cabi( + 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( + 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_read_ready_event_cabi( + arg0: *mut u8, + ) -> *mut u8 { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + let result0 = T::read_ready_event(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( + arg0: *mut u8, + ) -> *mut u8 { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + let result0 = T::read_result(StreamObjBorrow::lift(arg0 as usize).get()); + (result0).take_handle() as *mut u8 + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_static_stream_obj_close_read_cabi( + arg0: *mut u8, + ) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + T::close_read(StreamObj::from_handle(arg0 as usize)); + } + #[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_event_cabi< + T: GuestStreamObj, + >( + arg0: *mut u8, + ) -> *mut u8 { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + let result0 = T::write_ready_event(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( + 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( + arg0: *mut u8, + arg1: *mut u8, + ) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + T::finish_writing( + StreamObjBorrow::lift(arg0 as usize).get(), + Buffer::from_handle(arg1 as usize), + ); + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_static_stream_obj_close_write_cabi( + arg0: *mut u8, + ) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + T::close_write(StreamObj::from_handle(arg0 as usize)); + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_end_of_file_cabi() -> *mut u8 { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + let result0 = T::end_of_file(); + (result0).take_handle() as *mut u8 + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_is_end_of_file_cabi(arg0: *mut u8) -> i32 { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + let result0 = T::is_end_of_file(BufferBorrow::lift(arg0 as usize)); + match result0 { + true => 1, + false => 0, + } + } + pub trait Guest { + type Address: GuestAddress; + type Buffer: GuestBuffer; + type StreamObj: GuestStreamObj; + /// special EOF buffer value (should be opaque) + fn end_of_file() -> Buffer; + fn is_end_of_file(obj: BufferBorrow<'_>) -> bool; + } + #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_drop_address_cabi(arg0: usize) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + Address::dtor::(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(arg0: usize) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + Buffer::dtor::(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(arg0: usize) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + StreamObj::dtor::(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; + /// reading (in roughly chronological order) + fn is_write_closed(&self) -> bool; + fn start_reading(&self, buffer: Buffer) -> (); + fn read_ready_event(&self) -> EventGenerator; + fn read_result(&self) -> Buffer; + fn close_read(stream: StreamObj) -> (); + /// writing + fn is_ready_to_write(&self) -> bool; + fn write_ready_event(&self) -> EventGenerator; + fn start_writing(&self) -> Buffer; + /// how to represent EOF? Zero buffer? + fn finish_writing(&self, buffer: Buffer) -> (); + fn close_write(stream: StreamObj) -> (); + } + #[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")] @@ -1154,158 +1297,155 @@ macro_rules! __export_symmetric_runtime_symmetric_stream_0_1_0_cabi{ };); } -#[doc(hidden)] -pub(crate) use __export_symmetric_runtime_symmetric_stream_0_1_0_cabi; - -} - -} -} + #[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 { - // 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, - } - - /// 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 Resource { - #[doc(hidden)] - pub unsafe fn from_handle(handle: usize) -> Self { - debug_assert!(handle != 0); - Self { - handle: AtomicUsize::new(handle), - _marker: marker::PhantomData, - } - } + #![allow(dead_code, clippy::all)] + + use core::fmt; + use core::marker; + use core::sync::atomic::{AtomicUsize, Ordering::Relaxed}; - /// Takes ownership of the handle owned by `resource`. + /// A type which represents a component model resource, either imported or + /// exported into this component. /// - /// Note that this ideally would be `into_handle` taking `Resource` by - /// ownership. The code generator does not enable that in all situations, - /// unfortunately, so this is provided instead. + /// 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. /// - /// 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` 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) -> usize { - resource.handle.swap(0, Relaxed) + /// 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 { + // 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, } - #[doc(hidden)] - pub fn handle(resource: &Resource) -> usize { - resource.handle.load(Relaxed) + /// 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 fmt::Debug for Resource { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Resource") - .field("handle", &self.handle) - .finish() + impl Resource { + #[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` 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` 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) -> usize { + resource.handle.swap(0, Relaxed) + } + + #[doc(hidden)] + pub fn handle(resource: &Resource) -> usize { + resource.handle.load(Relaxed) + } } - } - - impl Drop for Resource { - 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), + + impl fmt::Debug for Resource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Resource") + .field("handle", &self.handle) + .finish() } - } } - } - 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 + + impl Drop for Resource { + 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_i64(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() + 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 + } } - } - - impl AsI64 for i64 { - #[inline] - fn as_i64(self) -> i64 { - self as i64 + + pub fn as_i64(t: T) -> i64 { + t.as_i64() } - } - - impl AsI64 for u64 { - #[inline] - fn as_i64(self) -> i64 { - self as i64 + + pub trait AsI64 { + fn as_i64(self) -> i64; } - } - 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; + + 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(); + } + extern crate alloc as alloc_crate; } /// Generates `#[unsafe(no_mangle)]` functions to export the specified type as @@ -1379,6 +1519,5 @@ nt\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(); + wit_bindgen::rt::maybe_link_cabi_realloc(); } - From a49a1527db40ff04553eaa5438b276655887efc5 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 12 Jan 2025 17:55:31 +0100 Subject: [PATCH 465/672] work in progress to use a WIT interface --- .../tests/symmetric_stream/source/src/lib.rs | 36 +++--- .../stream/src/stream_world.rs | 6 +- .../rust-client/src/async_support.rs | 62 +++++----- .../src/async_support/stream_support.rs | 116 +++++++++--------- .../rust-client/src/module.rs | 88 ++++++------- .../symmetric_stream/src/lib.rs | 55 +++++---- .../symmetric_stream/src/stream_impl.rs | 80 +++++------- crates/symmetric_executor/wit/executor.wit | 6 +- 8 files changed, 210 insertions(+), 239 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index f05506bc7..175f68b38 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -1,11 +1,7 @@ use std::sync::atomic::{AtomicU32, Ordering}; use wit_bindgen_symmetric_rt::{ - async_support::{ - results, - stream_support::{self, Slice}, - Stream, - }, + async_support::{results, Stream}, register, subscribe_event_send_ptr, CallbackState, EventSubscription, }; @@ -13,23 +9,29 @@ static COUNT: AtomicU32 = AtomicU32::new(1); extern "C" fn timer_call(data: *mut ()) -> CallbackState { let count = COUNT.fetch_add(1, Ordering::AcqRel); - let stream: *mut Stream = data.cast(); + let stream: Stream = unsafe { Stream::from_handle(data as usize) }; if count <= 5 { - let Slice { addr, size } = unsafe { stream_support::start_writing(stream) }; + 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.cast::() } = count; - unsafe { stream_support::finish_writing(stream, 1) }; + *unsafe { &mut *addr } = count; + buffer.set_size(1); + stream.finish_writing(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: *mut Stream = data.cast(); + let stream: Stream = unsafe { Stream::from_handle(data as usize) }; + // let stream: *mut Stream = data.cast(); // EOF - unsafe { stream_support::finish_writing(stream, results::CLOSED) }; - unsafe { stream_support::close_write(stream) }; + stream.finish_writing(end_of_file()); + // unsafe { stream_support::finish_writing(stream, results::CLOSED) }; + // unsafe { stream_support::close_write(stream) }; CallbackState::Ready } else { if count == 1 { @@ -47,9 +49,11 @@ pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate( // _args: *mut u8, results: *mut u8, ) -> *mut u8 { - let stream = stream_support::create_stream(); - let event = unsafe { subscribe_event_send_ptr(stream_support::write_ready_event(stream)) }; - register(event, write_ready, stream.cast()); - *unsafe { &mut *results.cast::<*mut Stream>() } = stream; + let stream = StreamObj::new(); + // stream_support::create_stream(); + let event = stream.write_ready_event().subscribe(); + //unsafe { subscribe_event_send_ptr(stream_support::write_ready_event(stream)) }; + register(event, write_ready, stream.handle() as *mut ()); + *unsafe { &mut *results.cast::<*mut Stream>() } = stream.take_handle(); std::ptr::null_mut() } diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 8ad11e75a..7269a8dfe 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -111,11 +111,9 @@ pub mod wit_stream { use wit_bindgen_symmetric_rt::async_support::stream_support::new_stream; - pub trait StreamPayload: Unpin + Sized + 'static { - } + pub trait StreamPayload: Unpin + Sized + 'static {} - impl StreamPayload for u32 { - } + impl StreamPayload for u32 {} pub fn new() -> ( ::wit_bindgen_symmetric_rt::async_support::StreamWriter, diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 52bfbdeae..ae3643e4c 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -5,11 +5,8 @@ use std::{ task::{Context, Poll, RawWaker, RawWakerVTable}, }; -use crate::{ - module::symmetric::runtime::symmetric_executor::{ - self, CallbackState, EventGenerator, EventSubscription, - }, - subscribe_event_send_ptr, +use crate::module::symmetric::runtime::symmetric_executor::{ + self, CallbackState, EventGenerator, EventSubscription, }; pub use stream_support::{results, Stream, StreamHandle2, StreamReader, StreamWriter}; @@ -143,34 +140,33 @@ pub fn spawn(future: impl Future + 'static + Send) { drop(wait_for); } -#[repr(transparent)] -pub struct AddressSend(pub *mut ()); -unsafe impl Send for AddressSend {} +// #[repr(transparent)] +// pub struct AddressSend(pub *mut ()); +// unsafe impl Send for AddressSend {} // unsafe impl Sync for StreamHandle2 {} // this is used for reading? -pub async unsafe fn await_stream_result( - import: unsafe extern "C" fn(*mut Stream, *mut (), usize) -> isize, - stream: StreamHandle2, - address: AddressSend, - count: usize, -) -> Option { - let stream_copy = stream.0; - let result = import(stream_copy, address.0, count); - match result { - results::BLOCKED => { - let event = - unsafe { subscribe_event_send_ptr(stream_support::read_ready_event(stream.0)) }; - event.reset(); - wait_on(event).await; - let v = stream_support::read_amount(stream.0); - if let results::CLOSED | results::CANCELED = v { - None - } else { - Some(usize::try_from(v).unwrap()) - } - } - results::CLOSED | results::CANCELED => None, - v => Some(usize::try_from(v).unwrap()), - } -} +// pub async unsafe fn await_stream_result( +// import: unsafe extern "C" fn(&Stream, Buffer) -> Buffer, +// stream: StreamHandle2, +// buffer: Buffer, +// ) -> Option { +// let stream_copy = stream.clone(); +// let result = import(&stream, buffer); +// match result { +// results::BLOCKED => { +// let event = +// unsafe { subscribe_event_send_ptr(stream_support::read_ready_event(stream.0)) }; +// event.reset(); +// wait_on(event).await; +// let v = stream.read_result(); +// if let results::CLOSED | results::CANCELED = v { +// None +// } else { +// Some(usize::try_from(v).unwrap()) +// } +// } +// results::CLOSED | results::CANCELED => None, +// v => Some(usize::try_from(v).unwrap()), +// } +// } 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 index 9ba94817f..bc2a513a3 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -1,8 +1,8 @@ -//use std::sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}; - pub use crate::module::symmetric::runtime::symmetric_stream::StreamObj as Stream; use crate::{ - activate_event_send_ptr, async_support::wait_on, subscribe_event_send_ptr, EventGenerator, + async_support::wait_on, + module::symmetric::runtime::symmetric_stream::{self, end_of_file}, + EventGenerator, }; use { futures::sink::Sink, @@ -12,7 +12,7 @@ use { future::Future, iter, marker::PhantomData, - mem::{self, ManuallyDrop, MaybeUninit}, + mem::{self, MaybeUninit}, pin::Pin, task::{Context, Poll}, }, @@ -38,7 +38,7 @@ impl StreamWriter { #[doc(hidden)] pub fn new(handle: Stream) -> Self { Self { - handle: StreamHandle2(handle), + handle, future: None, _phantom: PhantomData, } @@ -52,7 +52,7 @@ impl StreamWriter { impl fmt::Debug for StreamWriter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("StreamWriter") - .field("handle", &self.handle.0) + .field("handle", &self.handle) .finish() } } @@ -63,14 +63,14 @@ impl Sink> for StreamWriter { fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { let me = self.get_mut(); - let ready = me.handle.0.is_ready_to_write(); + let ready = me.handle.is_ready_to_write(); // see also StreamReader::poll_next if !ready && me.future.is_none() { - let handle = StreamHandle2(me.handle.0); + let handle = me.handle.clone(); me.future = Some(Box::pin(async move { let handle_local = handle; - let subscr = handle_local.0.write_ready_event(); + let subscr = handle_local.write_ready_event().subscribe(); subscr.reset(); wait_on(subscr).await; }) as Pin + Send>>); @@ -92,15 +92,18 @@ impl Sink> for StreamWriter { fn start_send(self: Pin<&mut Self>, mut item: Vec) -> Result<(), Self::Error> { let item_len = item.len(); let me = self.get_mut(); - let stream = me.handle.0; - let Slice { addr, size } = stream.start_writing(); + 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::>(), item_len) }; for (a, b) in slice.iter_mut().zip(item.drain(..)) { a.write(b); } - stream.finish_writing(item_len as isize); + buffer.set_size(item_len as u64); + stream.finish_writing(buffer); Ok(()) } @@ -115,10 +118,9 @@ impl Sink> for StreamWriter { impl Drop for StreamWriter { fn drop(&mut self) { - if !self.handle.0.is_write_closed() { - self.handle.0.finish_writing(results::CLOSED); + if !self.handle.is_write_closed() { + self.handle.finish_writing(end_of_file()); } - self.handle.0.close_write(); } } @@ -143,7 +145,7 @@ impl StreamReader { impl fmt::Debug for StreamReader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("StreamReader") - .field("handle", &self.handle.0) + .field("handle", &self.handle) .finish() } } @@ -152,14 +154,14 @@ impl StreamReader { #[doc(hidden)] pub fn new(handle: Stream) -> Self { Self { - handle: StreamHandle2(handle), + handle, future: None, _phantom: PhantomData, } } #[doc(hidden)] pub fn into_handle(self) -> *mut () { - self.handle.0.take_handle() as *mut () + self.handle.take_handle() as *mut () } } @@ -170,39 +172,39 @@ impl futures::stream::Stream for StreamReader { let me = self.get_mut(); if me.future.is_none() { - let handle = StreamHandle2(me.handle.0); + let handle = me.handle.clone(); me.future = Some(Box::pin(async move { - let mut buffer = iter::repeat_with(MaybeUninit::uninit) - .take(ceiling(4 * 1024, mem::size_of::())) - .collect::>(); - let stream_handle = handle; - let result = if let Some(count) = { - let poll_fn = start_reading; - let address = super::AddressSend(buffer.as_mut_ptr() as _); - let count = unsafe { - super::await_stream_result( - poll_fn, - stream_handle, - address, - buffer.len(), - // &stream.event, - ) - .await - }; - #[allow(unused)] - if let Some(count) = count { - let value = (); - } - count - } + // let mut buffer = iter::repeat_with(MaybeUninit::uninit) + // .take(ceiling(4 * 1024, mem::size_of::())) + // .collect::>(); + // let stream_handle = handle; + let subsc = handle.read_ready_event().subscribe(); + subsc.reset(); + wait_on(subsc).await; + let buffer2 = handle.read_result(); + // let result = if let Some(count) = { + // let poll_fn: unsafe fn(*mut Stream, *mut (), usize) -> isize = start_reading; + // let address = super::AddressSend(buffer.as_mut_ptr() as _); + // let count = unsafe { + // super::await_stream_result(poll_fn, stream_handle, address, buffer.len()) + // .await + // }; + // #[allow(unused)] + // if let Some(count) = count { + // let value = (); + // } + // count + // } // T::read(&stream_handle, &mut buffer).await - { - buffer.truncate(count); - Some(unsafe { mem::transmute::>, Vec>(buffer) }) - } else { - None - }; - result + // { + // buffer.truncate(count); + // Some(unsafe { mem::transmute::>, Vec>(buffer) }) + // } else { + // None + // }; + todo!(); + Some(Vec::new()) + // result }) as Pin + Send>>); } @@ -218,20 +220,18 @@ impl futures::stream::Stream for StreamReader { impl Drop for StreamReader { fn drop(&mut self) { - self.handle.0.write_ready_event().activate(); - Stream::close_read(self.handle.0); - // unsafe { activate_event_send_ptr(write_ready_event(self.handle.0)) }; - // unsafe { close_read(self.handle.0) }; + self.handle.write_ready_event().activate(); } } // Stream handles are Send, so wrap them -#[repr(transparent)] -pub struct StreamHandle2(Stream); -unsafe impl Send for StreamHandle2 {} -unsafe impl Sync for StreamHandle2 {} +// #[repr(transparent)] +pub type StreamHandle2 = Stream; +// unsafe impl Send for StreamHandle2 {} +// unsafe impl Sync for StreamHandle2 {} pub fn new_stream() -> (StreamWriter, StreamReader) { let handle = Stream::new(); - (StreamWriter::new(handle), StreamReader::new(handle)) + let handle2 = handle.clone(); + (StreamWriter::new(handle), StreamReader::new(handle2)) } diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index 5c8c60a74..a064a363b 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -707,6 +707,26 @@ pub mod symmetric { } } } + 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) @@ -786,24 +806,7 @@ pub mod symmetric { } impl StreamObj { #[allow(unused_unsafe, clippy::all)] - pub fn close_read(stream: StreamObj) -> () { - unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { - #[cfg_attr( - target_arch = "wasm32", - link_name = "[static]stream-obj.close-read" - )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BstaticX5Dstream_objX2Eclose_read( - _: *mut u8, - ); - } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BstaticX5Dstream_objX2Eclose_read((&stream).take_handle() as *mut u8); - } - } - } - impl StreamObj { - #[allow(unused_unsafe, clippy::all)] + /// close-read: static func(%stream: stream-obj); /// writing pub fn is_ready_to_write(&self) -> bool { unsafe { @@ -879,24 +882,6 @@ pub mod symmetric { } } } - impl StreamObj { - #[allow(unused_unsafe, clippy::all)] - pub fn close_write(stream: StreamObj) -> () { - unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { - #[cfg_attr( - target_arch = "wasm32", - link_name = "[static]stream-obj.close-write" - )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BstaticX5Dstream_objX2Eclose_write( - _: *mut u8, - ); - } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BstaticX5Dstream_objX2Eclose_write((&stream).take_handle() as *mut u8); - } - } - } #[allow(unused_unsafe, clippy::all)] /// special EOF buffer value (should be opaque) pub fn end_of_file() -> Buffer { @@ -1072,8 +1057,8 @@ mod _rt { #[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; 1663] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x82\x0c\x01A\x02\x01\ +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1621] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xd8\x0b\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\ @@ -1087,26 +1072,25 @@ 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\x0fevent\ --generator\x01B)\x02\x03\x02\x01\x01\x04\0\x0fevent-generator\x03\0\0\x04\0\x07a\ +-generator\x01B(\x02\x03\x02\x01\x01\x04\0\x0fevent-generator\x03\0\0\x04\0\x07a\ ddress\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\x04se\ lf\x08\x04sizew\x01\0\x04\0\x17[method]buffer.set-size\x01\x0b\x04\0\x17[method]\ 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\x7f\x04\0\"[method]stream-obj.is-write-clos\ -ed\x01\x0f\x01@\x02\x04self\x0e\x06buffer\x06\x01\0\x04\0\x20[method]stream-obj.\ -start-reading\x01\x10\x01i\x01\x01@\x01\x04self\x0e\0\x11\x04\0#[method]stream-o\ -bj.read-ready-event\x01\x12\x01@\x01\x04self\x0e\0\x06\x04\0\x1e[method]stream-o\ -bj.read-result\x01\x13\x01@\x01\x06stream\x0c\x01\0\x04\0\x1d[static]stream-obj.\ -close-read\x01\x14\x04\0$[method]stream-obj.is-ready-to-write\x01\x0f\x04\0$[met\ -hod]stream-obj.write-ready-event\x01\x12\x04\0\x20[method]stream-obj.start-writi\ -ng\x01\x13\x04\0![method]stream-obj.finish-writing\x01\x10\x04\0\x1e[static]stre\ -am-obj.close-write\x01\x14\x01@\0\0\x06\x04\0\x0bend-of-file\x01\x15\x01@\x01\x03\ -obj\x08\0\x7f\x04\0\x0eis-end-of-file\x01\x16\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\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x070\ -.223.0\x10wit-bindgen-rust\x060.37.0"; +\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-reading\x01\ +\x11\x01i\x01\x01@\x01\x04self\x0e\0\x12\x04\0#[method]stream-obj.read-ready-eve\ +nt\x01\x13\x01@\x01\x04self\x0e\0\x06\x04\0\x1e[method]stream-obj.read-result\x01\ +\x14\x04\0$[method]stream-obj.is-ready-to-write\x01\x10\x04\0$[method]stream-obj\ +.write-ready-event\x01\x13\x04\0\x20[method]stream-obj.start-writing\x01\x14\x04\ +\0![method]stream-obj.finish-writing\x01\x11\x01@\0\0\x06\x04\0\x0bend-of-file\x01\ +\x15\x01@\x01\x03obj\x08\0\x7f\x04\0\x0eis-end-of-file\x01\x16\x03\0(symmetric:r\ +untime/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\0G\x09producers\x01\x0cprocessed-by\x02\x0dwi\ +t-component\x070.223.0\x10wit-bindgen-rust\x060.37.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index aa9c4fd24..683cdc912 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -89,16 +89,12 @@ impl GuestStreamObj for StreamObj { return old_ready; } assert!(old_ready == results::BLOCKED); - let old_size = unsafe { &mut *stream } - .read_size - .swap(size, Ordering::Acquire); + let old_size = self.read_size.swap(size, Ordering::Acquire); assert_eq!(old_size, 0); - let old_ptr = unsafe { &mut *stream } - .read_addr - .swap(buf, Ordering::Release); + let old_ptr = self.read_addr.swap(buf, Ordering::Release); assert_eq!(old_ptr, std::ptr::null_mut()); - let write_evt = unsafe { &mut *stream }.write_ready_event_send; - unsafe { activate_event_send_ptr(write_evt) }; + self.write_ready_event_send.activate(); + // unsafe { activate_event_send_ptr(write_evt) }; results::BLOCKED } @@ -112,21 +108,21 @@ impl GuestStreamObj for StreamObj { self.ready_size.swap(results::BLOCKED, Ordering::Acquire) } - fn close_read(stream: symmetric_stream::StreamObj) -> () { - let refs = unsafe { &mut *stream } - .active_instances - .fetch_sub(1, Ordering::AcqRel); - if refs == 1 { - let obj = Box::from_raw(stream); - drop(EventGenerator::from_handle( - obj.read_ready_event_send as usize, - )); - drop(EventGenerator::from_handle( - obj.write_ready_event_send as usize, - )); - drop(obj); - } - } + // fn close_read(stream: symmetric_stream::StreamObj) -> () { + // let refs = unsafe { &mut *stream } + // .active_instances + // .fetch_sub(1, Ordering::AcqRel); + // if refs == 1 { + // let obj = Box::from_raw(stream); + // drop(EventGenerator::from_handle( + // obj.read_ready_event_send as usize, + // )); + // drop(EventGenerator::from_handle( + // obj.write_ready_event_send as usize, + // )); + // drop(obj); + // } + // } fn is_ready_to_write(&self) -> bool { self.read_addr.load(Ordering::Acquire).is_null() @@ -141,7 +137,12 @@ impl GuestStreamObj for StreamObj { let addr = self .read_addr .swap(core::ptr::null_mut(), Ordering::Release); - Slice { addr, size } + Buffer { + addr, + capacity: 0, + size, + } + // Slice { addr, size } } fn finish_writing(&self, buffer: symmetric_stream::Buffer) -> () { @@ -150,9 +151,9 @@ impl GuestStreamObj for StreamObj { unsafe { activate_event_send_ptr(read_ready_event(stream)) }; } - fn close_write(stream: symmetric_stream::StreamObj) -> () { - todo!() - } + // fn close_write(stream: symmetric_stream::StreamObj) -> () { + // todo!() + // } } const EOF_MARKER: usize = 1; diff --git a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs index 05634d6de..620c768aa 100644 --- a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs +++ b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs @@ -953,6 +953,16 @@ pub mod exports { } #[doc(hidden)] #[allow(non_snake_case)] + pub unsafe fn _export_method_stream_obj_clone_cabi( + 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( arg0: *mut u8, ) -> i32 { @@ -999,15 +1009,6 @@ pub mod exports { } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_static_stream_obj_close_read_cabi( - arg0: *mut u8, - ) { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); - T::close_read(StreamObj::from_handle(arg0 as usize)); - } - #[doc(hidden)] - #[allow(non_snake_case)] pub unsafe fn _export_method_stream_obj_is_ready_to_write_cabi< T: GuestStreamObj, >( @@ -1058,15 +1059,6 @@ pub mod exports { } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_static_stream_obj_close_write_cabi( - arg0: *mut u8, - ) { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); - T::close_write(StreamObj::from_handle(arg0 as usize)); - } - #[doc(hidden)] - #[allow(non_snake_case)] pub unsafe fn _export_end_of_file_cabi() -> *mut u8 { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); @@ -1171,19 +1163,19 @@ pub mod exports { } 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 read_ready_event(&self) -> EventGenerator; fn read_result(&self) -> Buffer; - fn close_read(stream: StreamObj) -> (); + /// close-read: static func(%stream: stream-obj); /// writing fn is_ready_to_write(&self) -> bool; fn write_ready_event(&self) -> EventGenerator; fn start_writing(&self) -> Buffer; - /// how to represent EOF? Zero buffer? fn finish_writing(&self, buffer: Buffer) -> (); - fn close_write(stream: StreamObj) -> (); } #[doc(hidden)] @@ -1220,6 +1212,11 @@ pub mod exports { 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 { @@ -1240,11 +1237,6 @@ pub mod exports { unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_result(arg0: *mut u8,) -> *mut u8 { $($path_to_types)*::_export_method_stream_obj_read_result_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0) } - #[cfg_attr(target_arch = "wasm32", export_name = "[static]stream-obj.close-read")] - #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BstaticX5Dstream_objX2Eclose_read(arg0: *mut u8,) { - $($path_to_types)*::_export_static_stream_obj_close_read_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0) - } #[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 { @@ -1265,11 +1257,6 @@ pub mod exports { unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing(arg0: *mut u8,arg1: *mut u8,) { $($path_to_types)*::_export_method_stream_obj_finish_writing_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0, arg1) } - #[cfg_attr(target_arch = "wasm32", export_name = "[static]stream-obj.close-write")] - #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BstaticX5Dstream_objX2Eclose_write(arg0: *mut u8,) { - $($path_to_types)*::_export_static_stream_obj_close_write_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0) - } #[cfg_attr(target_arch = "wasm32", export_name = "end-of-file")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00end_of_file() -> *mut u8 { @@ -1480,8 +1467,8 @@ pub(crate) use __export_stream_impl_impl as export; #[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; 1673] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x87\x0c\x01A\x02\x01\ +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1631] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xdd\x0b\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\ @@ -1495,26 +1482,25 @@ 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\x0fevent\ --generator\x01B)\x02\x03\x02\x01\x01\x04\0\x0fevent-generator\x03\0\0\x04\0\x07a\ +-generator\x01B(\x02\x03\x02\x01\x01\x04\0\x0fevent-generator\x03\0\0\x04\0\x07a\ ddress\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\x04se\ lf\x08\x04sizew\x01\0\x04\0\x17[method]buffer.set-size\x01\x0b\x04\0\x17[method]\ 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\x7f\x04\0\"[method]stream-obj.is-write-clos\ -ed\x01\x0f\x01@\x02\x04self\x0e\x06buffer\x06\x01\0\x04\0\x20[method]stream-obj.\ -start-reading\x01\x10\x01i\x01\x01@\x01\x04self\x0e\0\x11\x04\0#[method]stream-o\ -bj.read-ready-event\x01\x12\x01@\x01\x04self\x0e\0\x06\x04\0\x1e[method]stream-o\ -bj.read-result\x01\x13\x01@\x01\x06stream\x0c\x01\0\x04\0\x1d[static]stream-obj.\ -close-read\x01\x14\x04\0$[method]stream-obj.is-ready-to-write\x01\x0f\x04\0$[met\ -hod]stream-obj.write-ready-event\x01\x12\x04\0\x20[method]stream-obj.start-writi\ -ng\x01\x13\x04\0![method]stream-obj.finish-writing\x01\x10\x04\0\x1e[static]stre\ -am-obj.close-write\x01\x14\x01@\0\0\x06\x04\0\x0bend-of-file\x01\x15\x01@\x01\x03\ -obj\x08\0\x7f\x04\0\x0eis-end-of-file\x01\x16\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-compone\ -nt\x070.223.0\x10wit-bindgen-rust\x060.37.0"; +\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-reading\x01\ +\x11\x01i\x01\x01@\x01\x04self\x0e\0\x12\x04\0#[method]stream-obj.read-ready-eve\ +nt\x01\x13\x01@\x01\x04self\x0e\0\x06\x04\0\x1e[method]stream-obj.read-result\x01\ +\x14\x04\0$[method]stream-obj.is-ready-to-write\x01\x10\x04\0$[method]stream-obj\ +.write-ready-event\x01\x13\x04\0\x20[method]stream-obj.start-writing\x01\x14\x04\ +\0![method]stream-obj.finish-writing\x01\x11\x01@\0\0\x06\x04\0\x0bend-of-file\x01\ +\x15\x01@\x01\x03obj\x08\0\x7f\x04\0\x0eis-end-of-file\x01\x16\x04\0(symmetric:r\ +untime/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\x0d\ +wit-component\x070.223.0\x10wit-bindgen-rust\x060.37.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/symmetric_executor/wit/executor.wit b/crates/symmetric_executor/wit/executor.wit index 9b8b978ac..fef7cfddc 100644 --- a/crates/symmetric_executor/wit/executor.wit +++ b/crates/symmetric_executor/wit/executor.wit @@ -73,18 +73,20 @@ interface symmetric-stream { 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); read-ready-event: func() -> event-generator; read-result: func() -> buffer; - close-read: static func(%stream: stream-obj); + // close-read: static func(%stream: stream-obj); // writing is-ready-to-write: func() -> bool; write-ready-event: func() -> event-generator; start-writing: func() -> buffer; finish-writing: func(buffer: buffer); - close-write: static func(%stream: stream-obj); + // close-write: static func(%stream: stream-obj); } // special EOF buffer value (should be opaque) From 85c45b0f508627285b735292c087001d84392196 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 12 Jan 2025 19:14:17 +0100 Subject: [PATCH 466/672] linking needs some more tricks --- crates/cpp/tests/symmetric_stream/Cargo.lock | 12 +++++ .../tests/symmetric_stream/main/Cargo.toml | 1 + .../tests/symmetric_stream/main/src/main.rs | 46 ++++++++++--------- .../tests/symmetric_stream/source/Cargo.toml | 1 + .../tests/symmetric_stream/source/src/lib.rs | 17 +++---- .../tests/symmetric_stream/stream/Cargo.toml | 1 + .../stream/src/stream_world.rs | 4 +- .../symmetric_executor/rust-client/src/lib.rs | 1 + .../symmetric_stream/src/lib.rs | 44 +++++++++++------- 9 files changed, 78 insertions(+), 49 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/Cargo.lock b/crates/cpp/tests/symmetric_stream/Cargo.lock index ce44875c7..f8e00cd67 100644 --- a/crates/cpp/tests/symmetric_stream/Cargo.lock +++ b/crates/cpp/tests/symmetric_stream/Cargo.lock @@ -113,6 +113,7 @@ version = "0.1.0" dependencies = [ "stream", "symmetric_executor", + "symmetric_stream", "wit-bindgen-symmetric-rt", ] @@ -166,6 +167,7 @@ name = "source" version = "0.1.0" dependencies = [ "symmetric_executor", + "symmetric_stream", "wit-bindgen-symmetric-rt", ] @@ -176,6 +178,7 @@ dependencies = [ "dummy-rt", "futures", "source", + "symmetric_stream", "wit-bindgen-symmetric-rt", ] @@ -188,6 +191,15 @@ dependencies = [ "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" diff --git a/crates/cpp/tests/symmetric_stream/main/Cargo.toml b/crates/cpp/tests/symmetric_stream/main/Cargo.toml index 9b9a650b2..ea7feeeea 100644 --- a/crates/cpp/tests/symmetric_stream/main/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/main/Cargo.toml @@ -6,4 +6,5 @@ edition = "2021" [dependencies] stream = { version = "0.1.0", path = "../stream" } symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor", features = ["trace"]} +symmetric_stream = { path = "../../../../symmetric_executor/symmetric_stream" } wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../../../../symmetric_executor/rust-client" } diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index b9bddae35..acb46274f 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -1,12 +1,10 @@ use wit_bindgen_symmetric_rt::{ - async_support::{stream_support, Stream}, - CallbackState, + async_support::{stream_support, Stream}, symmetric_stream::{Address, Buffer}, CallbackState }; #[link(name = "stream")] extern "C" { pub fn testX3AtestX2Fstream_testX00X5BasyncX5Dcreate( - // args: *const (), results: *mut (), ) -> *mut (); } @@ -14,24 +12,27 @@ extern "C" { const DATALEN: usize = 2; struct CallbackInfo { - stream: *mut Stream, + stream: Stream, data: [u32; DATALEN], } extern "C" fn ready(arg: *mut ()) -> CallbackState { let info = unsafe { &*arg.cast::() }; - let len = unsafe { stream_support::read_amount(info.stream) }; + let buffer = info.stream.read_result(); + let len = buffer.get_size(); + // unsafe { stream_support::read_amount(info.stream) }; if len > 0 { for i in 0..len as usize { println!("data {}", info.data[i]); } - unsafe { - stream_support::start_reading( - info.stream, - info.data.as_ptr().cast_mut().cast(), - DATALEN, - ); - }; + info.stream.start_reading(buffer); + // unsafe { + // stream_support::start_reading( + // info.stream, + // info.data.as_ptr().cast_mut().cast(), + // DATALEN, + // ); + // }; // call again CallbackState::Pending } else { @@ -50,17 +51,20 @@ fn main() { }; // function should have completed (not async) assert!(continuation.is_null()); - let stream = result_stream.cast::(); + let stream = unsafe {Stream::from_handle(result_stream as usize)}; let mut info = Box::pin(CallbackInfo { - stream, + stream: stream.clone(), data: [0, 0], }); - unsafe { - stream_support::start_reading(stream, info.data.as_mut_ptr().cast(), DATALEN); - }; - let subscription = unsafe { - wit_bindgen_symmetric_rt::subscribe_event_send_ptr(stream_support::read_ready_event(stream)) - }; + let buffer = Buffer::new(unsafe {Address::from_handle(info.data.as_mut_ptr() as usize)}, DATALEN as u64); + stream.start_reading(buffer); + // unsafe { + // stream_support::start_reading(stream, info.data.as_mut_ptr().cast(), DATALEN); + // }; + let subscription = stream.read_ready_event().subscribe(); + // unsafe { + // wit_bindgen_symmetric_rt::subscribe_event_send_ptr(stream_support::read_ready_event(stream)) + // }; println!("Register read in main"); wit_bindgen_symmetric_rt::register( subscription, @@ -68,5 +72,5 @@ fn main() { (&*info as *const CallbackInfo).cast_mut().cast(), ); wit_bindgen_symmetric_rt::run(); - unsafe { stream_support::close_read(stream) }; + // unsafe { stream_support::close_read(stream) }; } diff --git a/crates/cpp/tests/symmetric_stream/source/Cargo.toml b/crates/cpp/tests/symmetric_stream/source/Cargo.toml index 710191356..995658bad 100644 --- a/crates/cpp/tests/symmetric_stream/source/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/source/Cargo.toml @@ -5,6 +5,7 @@ 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] diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index 175f68b38..1b90fe34e 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -1,8 +1,9 @@ use std::sync::atomic::{AtomicU32, Ordering}; use wit_bindgen_symmetric_rt::{ - async_support::{results, Stream}, - register, subscribe_event_send_ptr, CallbackState, EventSubscription, + symmetric_stream, + async_support::Stream, + register, CallbackState, EventSubscription, }; static COUNT: AtomicU32 = AtomicU32::new(1); @@ -27,11 +28,8 @@ 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) }; - // let stream: *mut Stream = data.cast(); // EOF - stream.finish_writing(end_of_file()); - // unsafe { stream_support::finish_writing(stream, results::CLOSED) }; - // unsafe { stream_support::close_write(stream) }; + stream.finish_writing(symmetric_stream::end_of_file()); CallbackState::Ready } else { if count == 1 { @@ -46,14 +44,11 @@ extern "C" fn write_ready(data: *mut ()) -> CallbackState { #[allow(non_snake_case)] #[no_mangle] pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate( - // _args: *mut u8, results: *mut u8, ) -> *mut u8 { - let stream = StreamObj::new(); - // stream_support::create_stream(); + let stream = Stream::new(); let event = stream.write_ready_event().subscribe(); - //unsafe { subscribe_event_send_ptr(stream_support::write_ready_event(stream)) }; register(event, write_ready, stream.handle() as *mut ()); - *unsafe { &mut *results.cast::<*mut Stream>() } = stream.take_handle(); + *unsafe { &mut *results.cast::() } = stream.take_handle(); std::ptr::null_mut() } diff --git a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml index 288b4c7e0..43a957e03 100644 --- a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml @@ -8,6 +8,7 @@ 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" diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 7269a8dfe..02c635310 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -11,6 +11,8 @@ pub mod test { static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use wit_bindgen_symmetric_rt::async_support::Stream; + use super::super::super::_rt; #[allow(unused_unsafe, clippy::all)] @@ -31,7 +33,7 @@ pub mod test { .await; let l3 = *ptr1.add(0).cast::<*mut u8>(); let result4 = - wit_bindgen_symmetric_rt::async_support::StreamReader::new(l3.cast()); + wit_bindgen_symmetric_rt::async_support::StreamReader::new(Stream::from_handle(l3 as usize)); _rt::cabi_dealloc(ptr1, 4, 4); result4 } diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index 3e5ebb7a6..9ee31abc5 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -1,4 +1,5 @@ use module::symmetric::runtime::symmetric_executor::{self, CallbackData, CallbackFunction}; +pub use module::symmetric::runtime::symmetric_stream; pub use module::symmetric::runtime::symmetric_executor::{ run, CallbackState, EventGenerator, EventSubscription, }; diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index 683cdc912..93c68bbaa 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -6,7 +6,7 @@ use std::{ use stream_impl::exports::symmetric::runtime::symmetric_stream::{ self, Address, GuestAddress, GuestBuffer, GuestStreamObj, }; -use wit_bindgen_symmetric_rt::EventGenerator; +use wit_bindgen_symmetric_rt::{async_support::Stream, EventGenerator}; mod stream_impl; @@ -81,21 +81,22 @@ impl GuestStreamObj for StreamObj { self.ready_size.load(Ordering::Acquire) == results::CLOSED } - fn start_reading(&self, buffer: symmetric_stream::Buffer) -> () { - let buf = buffer.get().get_address().take_handle() as *mut (); - let size = buffer.get().get_capacity(); + fn start_reading(&self, buffer: symmetric_stream::Buffer) { + let buf = buffer.get::().get_address().take_handle() as *mut (); + let size = buffer.get::().capacity(); let old_ready = self.ready_size.load(Ordering::Acquire); if old_ready == results::CLOSED { - return old_ready; + todo!(); + // return old_ready; } assert!(old_ready == results::BLOCKED); - let old_size = self.read_size.swap(size, Ordering::Acquire); + let old_size = self.read_size.swap(size as usize, Ordering::Acquire); assert_eq!(old_size, 0); let old_ptr = self.read_addr.swap(buf, Ordering::Release); assert_eq!(old_ptr, std::ptr::null_mut()); - self.write_ready_event_send.activate(); + self.write_ready_event().activate(); // unsafe { activate_event_send_ptr(write_evt) }; - results::BLOCKED + // results::BLOCKED } fn read_ready_event(&self) -> symmetric_stream::EventGenerator { @@ -105,7 +106,8 @@ impl GuestStreamObj for StreamObj { } fn read_result(&self) -> symmetric_stream::Buffer { - self.ready_size.swap(results::BLOCKED, Ordering::Acquire) + self.ready_size.swap(results::BLOCKED, Ordering::Acquire); + symmetric_stream::Buffer::new(Buffer{ addr: todo!(), capacity: todo!(), size: todo!() }) } // fn close_read(stream: symmetric_stream::StreamObj) -> () { @@ -129,7 +131,8 @@ impl GuestStreamObj for StreamObj { } fn write_ready_event(&self) -> symmetric_stream::EventGenerator { - self.write_ready_event_send + //self.write_ready_event_send.clone() + unsafe { symmetric_stream::EventGenerator::from_handle(self.write_ready_event_send as usize) } } fn start_writing(&self) -> symmetric_stream::Buffer { @@ -137,23 +140,32 @@ impl GuestStreamObj for StreamObj { let addr = self .read_addr .swap(core::ptr::null_mut(), Ordering::Release); - Buffer { - addr, - capacity: 0, - size, - } + todo!() + // Buffer { + // addr, + // capacity: 0, + // size, + // } // Slice { addr, size } } fn finish_writing(&self, buffer: symmetric_stream::Buffer) -> () { + let elements = buffer.get::().get_size() as isize; let old_ready = self.ready_size.swap(elements as isize, Ordering::Release); assert_eq!(old_ready, results::BLOCKED); - unsafe { activate_event_send_ptr(read_ready_event(stream)) }; + self.read_ready_event().activate(); + // unsafe { activate_event_send_ptr(self.read_ready_event) }; } + fn clone(&self) -> symmetric_stream::StreamObj { + todo!() + } + // fn close_write(stream: symmetric_stream::StreamObj) -> () { // todo!() // } + + } const EOF_MARKER: usize = 1; From 123cf648914897bb118678ff7ff0915c9d4dafe2 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 12 Jan 2025 19:22:26 +0100 Subject: [PATCH 467/672] needs more implementation but links --- crates/symmetric_executor/rust-client/src/module.rs | 2 ++ crates/symmetric_executor/symmetric_stream/build.rs | 9 +++++++++ 2 files changed, 11 insertions(+) create mode 100644 crates/symmetric_executor/symmetric_stream/build.rs diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index a064a363b..ca1a391b9 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -399,6 +399,7 @@ pub mod symmetric { 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", @@ -581,6 +582,7 @@ pub mod symmetric { 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", 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() + ); +} From e2a1afc3b780ee20bbb4e8747eb305248a0eb006 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 12 Jan 2025 19:56:35 +0100 Subject: [PATCH 468/672] simplify interface Signed-off-by: Christof Petig --- .../tests/symmetric_stream/main/src/main.rs | 17 +-- .../tests/symmetric_stream/source/src/lib.rs | 10 +- .../stream/src/stream_world.rs | 5 +- .../src/async_support/stream_support.rs | 6 +- .../symmetric_executor/rust-client/src/lib.rs | 2 +- .../rust-client/src/module.rs | 107 +++++++++++------ .../symmetric_stream/src/lib.rs | 96 +++++++++------ .../symmetric_stream/src/stream_impl.rs | 111 ++++++++++++------ crates/symmetric_executor/wit/executor.wit | 10 +- 9 files changed, 235 insertions(+), 129 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index acb46274f..134b14972 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -1,12 +1,12 @@ use wit_bindgen_symmetric_rt::{ - async_support::{stream_support, Stream}, symmetric_stream::{Address, Buffer}, CallbackState + async_support::{stream_support, Stream}, + symmetric_stream::{Address, Buffer}, + CallbackState, }; #[link(name = "stream")] extern "C" { - pub fn testX3AtestX2Fstream_testX00X5BasyncX5Dcreate( - results: *mut (), - ) -> *mut (); + pub fn testX3AtestX2Fstream_testX00X5BasyncX5Dcreate(results: *mut ()) -> *mut (); } const DATALEN: usize = 2; @@ -51,17 +51,20 @@ fn main() { }; // function should have completed (not async) assert!(continuation.is_null()); - let stream = unsafe {Stream::from_handle(result_stream as usize)}; + let stream = unsafe { Stream::from_handle(result_stream as usize) }; 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); + let buffer = Buffer::new( + unsafe { Address::from_handle(info.data.as_mut_ptr() as usize) }, + DATALEN as u64, + ); stream.start_reading(buffer); // unsafe { // stream_support::start_reading(stream, info.data.as_mut_ptr().cast(), DATALEN); // }; - let subscription = stream.read_ready_event().subscribe(); + let subscription = stream.read_ready_subscribe(); // unsafe { // wit_bindgen_symmetric_rt::subscribe_event_send_ptr(stream_support::read_ready_event(stream)) // }; diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index 1b90fe34e..962da4316 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -1,9 +1,7 @@ use std::sync::atomic::{AtomicU32, Ordering}; use wit_bindgen_symmetric_rt::{ - symmetric_stream, - async_support::Stream, - register, CallbackState, EventSubscription, + async_support::Stream, register, symmetric_stream, CallbackState, EventSubscription, }; static COUNT: AtomicU32 = AtomicU32::new(1); @@ -43,11 +41,9 @@ extern "C" fn write_ready(data: *mut ()) -> CallbackState { #[allow(non_snake_case)] #[no_mangle] -pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate( - results: *mut u8, -) -> *mut u8 { +pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(results: *mut u8) -> *mut u8 { let stream = Stream::new(); - let event = stream.write_ready_event().subscribe(); + let event = stream.write_ready_subscribe(); register(event, write_ready, stream.handle() as *mut ()); *unsafe { &mut *results.cast::() } = stream.take_handle(); std::ptr::null_mut() diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 02c635310..2abd13939 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -32,8 +32,9 @@ pub mod test { }) .await; let l3 = *ptr1.add(0).cast::<*mut u8>(); - let result4 = - wit_bindgen_symmetric_rt::async_support::StreamReader::new(Stream::from_handle(l3 as usize)); + let result4 = wit_bindgen_symmetric_rt::async_support::StreamReader::new( + Stream::from_handle(l3 as usize), + ); _rt::cabi_dealloc(ptr1, 4, 4); result4 } 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 index bc2a513a3..e7012cb0e 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -70,7 +70,7 @@ impl Sink> for StreamWriter { let handle = me.handle.clone(); me.future = Some(Box::pin(async move { let handle_local = handle; - let subscr = handle_local.write_ready_event().subscribe(); + let subscr = handle_local.write_ready_subscribe(); subscr.reset(); wait_on(subscr).await; }) as Pin + Send>>); @@ -178,7 +178,7 @@ impl futures::stream::Stream for StreamReader { // .take(ceiling(4 * 1024, mem::size_of::())) // .collect::>(); // let stream_handle = handle; - let subsc = handle.read_ready_event().subscribe(); + let subsc = handle.read_ready_subscribe(); subsc.reset(); wait_on(subsc).await; let buffer2 = handle.read_result(); @@ -220,7 +220,7 @@ impl futures::stream::Stream for StreamReader { impl Drop for StreamReader { fn drop(&mut self) { - self.handle.write_ready_event().activate(); + self.handle.write_ready_activate(); } } diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index 9ee31abc5..bd9ecc496 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -1,8 +1,8 @@ use module::symmetric::runtime::symmetric_executor::{self, CallbackData, CallbackFunction}; -pub use module::symmetric::runtime::symmetric_stream; pub use module::symmetric::runtime::symmetric_executor::{ run, CallbackState, EventGenerator, EventSubscription, }; +pub use module::symmetric::runtime::symmetric_stream; pub mod async_support; mod module; diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index ca1a391b9..fc58cc609 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -460,8 +460,8 @@ pub mod symmetric { super::super::super::__link_custom_section_describing_imports; use super::super::super::_rt; - pub type EventGenerator = - super::super::super::symmetric::runtime::symmetric_executor::EventGenerator; + pub type EventSubscription = + super::super::super::symmetric::runtime::symmetric_executor::EventSubscription; #[derive(Debug)] #[repr(transparent)] @@ -770,20 +770,38 @@ pub mod symmetric { } impl StreamObj { #[allow(unused_unsafe, clippy::all)] - pub fn read_ready_event(&self) -> EventGenerator { + 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.read-ready-event" + link_name = "[method]stream-obj.write-ready-activate" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_event( + 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_event((self).handle() as *mut u8); - super::super::super::symmetric::runtime::symmetric_executor::EventGenerator::from_handle(ret as usize) + 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) } } } @@ -808,7 +826,6 @@ pub mod symmetric { } impl StreamObj { #[allow(unused_unsafe, clippy::all)] - /// close-read: static func(%stream: stream-obj); /// writing pub fn is_ready_to_write(&self) -> bool { unsafe { @@ -829,20 +846,20 @@ pub mod symmetric { } impl StreamObj { #[allow(unused_unsafe, clippy::all)] - pub fn write_ready_event(&self) -> EventGenerator { + 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-event" + link_name = "[method]stream-obj.write-ready-subscribe" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_event( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_event((self).handle() as *mut u8); - super::super::super::symmetric::runtime::symmetric_executor::EventGenerator::from_handle(ret as usize) + 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) } } } @@ -884,6 +901,24 @@ pub mod symmetric { } } } + 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); + } + } + } #[allow(unused_unsafe, clippy::all)] /// special EOF buffer value (should be opaque) pub fn end_of_file() -> Buffer { @@ -1059,8 +1094,8 @@ mod _rt { #[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; 1621] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xd8\x0b\x01A\x02\x01\ +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1733] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xc8\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\ @@ -1073,26 +1108,28 @@ 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\x0fevent\ --generator\x01B(\x02\x03\x02\x01\x01\x04\0\x0fevent-generator\x03\0\0\x04\0\x07a\ -ddress\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\x04se\ -lf\x08\x04sizew\x01\0\x04\0\x17[method]buffer.set-size\x01\x0b\x04\0\x17[method]\ -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-reading\x01\ -\x11\x01i\x01\x01@\x01\x04self\x0e\0\x12\x04\0#[method]stream-obj.read-ready-eve\ -nt\x01\x13\x01@\x01\x04self\x0e\0\x06\x04\0\x1e[method]stream-obj.read-result\x01\ -\x14\x04\0$[method]stream-obj.is-ready-to-write\x01\x10\x04\0$[method]stream-obj\ -.write-ready-event\x01\x13\x04\0\x20[method]stream-obj.start-writing\x01\x14\x04\ -\0![method]stream-obj.finish-writing\x01\x11\x01@\0\0\x06\x04\0\x0bend-of-file\x01\ -\x15\x01@\x01\x03obj\x08\0\x7f\x04\0\x0eis-end-of-file\x01\x16\x03\0(symmetric:r\ -untime/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\0G\x09producers\x01\x0cprocessed-by\x02\x0dwi\ -t-component\x070.223.0\x10wit-bindgen-rust\x060.37.0"; +\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\x01@\x01\x04self\x0e\0\x06\x04\0\x1e[method]stream-obj.re\ +ad-result\x01\x15\x04\0$[method]stream-obj.is-ready-to-write\x01\x10\x04\0([meth\ +od]stream-obj.write-ready-subscribe\x01\x14\x04\0\x20[method]stream-obj.start-wr\ +iting\x01\x15\x04\0![method]stream-obj.finish-writing\x01\x11\x04\0&[method]stre\ +am-obj.read-ready-activate\x01\x12\x01@\0\0\x06\x04\0\x0bend-of-file\x01\x16\x01\ +@\x01\x03obj\x08\0\x7f\x04\0\x0eis-end-of-file\x01\x17\x03\0(symmetric:runtime/s\ +ymmetric-stream@0.1.0\x05\x02\x04\0\x1esymmetric:runtime/module@0.1.0\x04\0\x0b\x0c\ +\x01\0\x06module\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-componen\ +t\x070.223.0\x10wit-bindgen-rust\x060.37.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index 93c68bbaa..bf8cf9a09 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -1,12 +1,18 @@ use std::{ mem::transmute, - sync::atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}, + sync::{ + atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}, + Arc, + }, }; use stream_impl::exports::symmetric::runtime::symmetric_stream::{ self, Address, GuestAddress, GuestBuffer, GuestStreamObj, }; -use wit_bindgen_symmetric_rt::{async_support::Stream, EventGenerator}; +use stream_impl::symmetric::runtime::symmetric_executor::{ + self, EventGenerator, EventSubscription, +}; +//use wit_bindgen_symmetric_rt::{async_support::Stream, EventGenerator}; mod stream_impl; @@ -56,58 +62,65 @@ mod results { pub const CANCELED: isize = 0; } -struct StreamObj { - read_ready_event_send: *mut (), - write_ready_event_send: *mut (), +struct StreamInner { + read_ready_event_send: EventGenerator, + write_ready_event_send: EventGenerator, read_addr: AtomicPtr<()>, read_size: AtomicUsize, ready_size: AtomicIsize, active_instances: AtomicUsize, } +struct StreamObj(Arc); + impl GuestStreamObj for StreamObj { fn new() -> Self { - Self { - read_ready_event_send: EventGenerator::new().take_handle() as *mut (), - write_ready_event_send: EventGenerator::new().take_handle() as *mut (), + 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_size: AtomicIsize::new(results::BLOCKED), active_instances: AtomicUsize::new(2), - } + }; + Self(Arc::new(inner)) } fn is_write_closed(&self) -> bool { - self.ready_size.load(Ordering::Acquire) == results::CLOSED + self.0.ready_size.load(Ordering::Acquire) == results::CLOSED } fn start_reading(&self, buffer: symmetric_stream::Buffer) { let buf = buffer.get::().get_address().take_handle() as *mut (); let size = buffer.get::().capacity(); - let old_ready = self.ready_size.load(Ordering::Acquire); + let old_ready = self.0.ready_size.load(Ordering::Acquire); if old_ready == results::CLOSED { todo!(); // return old_ready; } assert!(old_ready == results::BLOCKED); - let old_size = self.read_size.swap(size as usize, Ordering::Acquire); + let old_size = self.0.read_size.swap(size as usize, Ordering::Acquire); assert_eq!(old_size, 0); - let old_ptr = self.read_addr.swap(buf, Ordering::Release); + let old_ptr = self.0.read_addr.swap(buf, Ordering::Release); assert_eq!(old_ptr, std::ptr::null_mut()); - self.write_ready_event().activate(); + self.write_ready_activate(); // unsafe { activate_event_send_ptr(write_evt) }; // results::BLOCKED } - fn read_ready_event(&self) -> symmetric_stream::EventGenerator { - unsafe { - symmetric_stream::EventGenerator::from_handle(self.read_ready_event_send as usize) - } - } + // fn read_ready_event(&self) -> symmetric_stream::EventGenerator { + // unsafe { + // symmetric_stream::EventGenerator::from_handle(self.read_ready_event_send as usize) + // } + // } fn read_result(&self) -> symmetric_stream::Buffer { - self.ready_size.swap(results::BLOCKED, Ordering::Acquire); - symmetric_stream::Buffer::new(Buffer{ addr: todo!(), capacity: todo!(), size: todo!() }) + self.0.ready_size.swap(results::BLOCKED, Ordering::Acquire); + symmetric_stream::Buffer::new(Buffer { + addr: todo!(), + capacity: todo!(), + size: todo!(), + }) } // fn close_read(stream: symmetric_stream::StreamObj) -> () { @@ -127,17 +140,20 @@ impl GuestStreamObj for StreamObj { // } fn is_ready_to_write(&self) -> bool { - self.read_addr.load(Ordering::Acquire).is_null() + self.0.read_addr.load(Ordering::Acquire).is_null() } - fn write_ready_event(&self) -> symmetric_stream::EventGenerator { - //self.write_ready_event_send.clone() - unsafe { symmetric_stream::EventGenerator::from_handle(self.write_ready_event_send as usize) } - } + // fn write_ready_event(&self) -> symmetric_stream::EventGenerator { + // //self.write_ready_event_send.clone() + // unsafe { + // symmetric_stream::EventGenerator::from_handle(self.write_ready_event_send as usize) + // } + // } fn start_writing(&self) -> symmetric_stream::Buffer { - let size = self.read_size.swap(0, Ordering::Acquire); + let size = self.0.read_size.swap(0, Ordering::Acquire); let addr = self + .0 .read_addr .swap(core::ptr::null_mut(), Ordering::Release); todo!() @@ -151,21 +167,35 @@ impl GuestStreamObj for StreamObj { fn finish_writing(&self, buffer: symmetric_stream::Buffer) -> () { let elements = buffer.get::().get_size() as isize; - let old_ready = self.ready_size.swap(elements as isize, Ordering::Release); + let old_ready = self.0.ready_size.swap(elements as isize, Ordering::Release); assert_eq!(old_ready, results::BLOCKED); - self.read_ready_event().activate(); + self.read_ready_activate(); // unsafe { activate_event_send_ptr(self.read_ready_event) }; } fn clone(&self) -> symmetric_stream::StreamObj { - todo!() + 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(); + } + // fn close_write(stream: symmetric_stream::StreamObj) -> () { // todo!() // } - - } const EOF_MARKER: usize = 1; diff --git a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs index 620c768aa..abacc7000 100644 --- a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs +++ b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs @@ -464,7 +464,7 @@ pub mod exports { super::super::super::super::__link_custom_section_describing_imports; use super::super::super::super::_rt; - pub type EventGenerator = super::super::super::super::symmetric::runtime::symmetric_executor::EventGenerator; + pub type EventSubscription = super::super::super::super::symmetric::runtime::symmetric_executor::EventSubscription; #[derive(Debug)] #[repr(transparent)] @@ -989,12 +989,26 @@ pub mod exports { } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_method_stream_obj_read_ready_event_cabi( + 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_event(StreamObjBorrow::lift(arg0 as usize).get()); + let result0 = + T::read_ready_subscribe(StreamObjBorrow::lift(arg0 as usize).get()); (result0).take_handle() as *mut u8 } #[doc(hidden)] @@ -1024,14 +1038,15 @@ pub mod exports { } #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_method_stream_obj_write_ready_event_cabi< + 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_event(StreamObjBorrow::lift(arg0 as usize).get()); + let result0 = + T::write_ready_subscribe(StreamObjBorrow::lift(arg0 as usize).get()); (result0).take_handle() as *mut u8 } #[doc(hidden)] @@ -1059,6 +1074,17 @@ pub mod exports { } #[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()); + } + #[doc(hidden)] + #[allow(non_snake_case)] pub unsafe fn _export_end_of_file_cabi() -> *mut u8 { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); @@ -1168,14 +1194,15 @@ pub mod exports { /// reading (in roughly chronological order) fn is_write_closed(&self) -> bool; fn start_reading(&self, buffer: Buffer) -> (); - fn read_ready_event(&self) -> EventGenerator; + fn write_ready_activate(&self) -> (); + fn read_ready_subscribe(&self) -> EventSubscription; fn read_result(&self) -> Buffer; - /// close-read: static func(%stream: stream-obj); /// writing fn is_ready_to_write(&self) -> bool; - fn write_ready_event(&self) -> EventGenerator; + fn write_ready_subscribe(&self) -> EventSubscription; fn start_writing(&self) -> Buffer; fn finish_writing(&self, buffer: Buffer) -> (); + fn read_ready_activate(&self) -> (); } #[doc(hidden)] @@ -1227,10 +1254,15 @@ pub mod exports { 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.read-ready-event")] + #[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_event(arg0: *mut u8,) -> *mut u8 { - $($path_to_types)*::_export_method_stream_obj_read_ready_event_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0) + 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)] @@ -1242,10 +1274,10 @@ pub mod exports { 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-event")] + #[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_event(arg0: *mut u8,) -> *mut u8 { - $($path_to_types)*::_export_method_stream_obj_write_ready_event_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0) + 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)] @@ -1257,6 +1289,11 @@ pub mod exports { unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing(arg0: *mut u8,arg1: *mut u8,) { $($path_to_types)*::_export_method_stream_obj_finish_writing_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0, arg1) } + #[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(target_arch = "wasm32", export_name = "end-of-file")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00end_of_file() -> *mut u8 { @@ -1467,8 +1504,8 @@ pub(crate) use __export_stream_impl_impl as export; #[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; 1631] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xdd\x0b\x01A\x02\x01\ +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1743] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xcd\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\ @@ -1481,26 +1518,28 @@ 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\x0fevent\ --generator\x01B(\x02\x03\x02\x01\x01\x04\0\x0fevent-generator\x03\0\0\x04\0\x07a\ -ddress\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\x04se\ -lf\x08\x04sizew\x01\0\x04\0\x17[method]buffer.set-size\x01\x0b\x04\0\x17[method]\ -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-reading\x01\ -\x11\x01i\x01\x01@\x01\x04self\x0e\0\x12\x04\0#[method]stream-obj.read-ready-eve\ -nt\x01\x13\x01@\x01\x04self\x0e\0\x06\x04\0\x1e[method]stream-obj.read-result\x01\ -\x14\x04\0$[method]stream-obj.is-ready-to-write\x01\x10\x04\0$[method]stream-obj\ -.write-ready-event\x01\x13\x04\0\x20[method]stream-obj.start-writing\x01\x14\x04\ -\0![method]stream-obj.finish-writing\x01\x11\x01@\0\0\x06\x04\0\x0bend-of-file\x01\ -\x15\x01@\x01\x03obj\x08\0\x7f\x04\0\x0eis-end-of-file\x01\x16\x04\0(symmetric:r\ -untime/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\x0d\ -wit-component\x070.223.0\x10wit-bindgen-rust\x060.37.0"; +\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\x01@\x01\x04self\x0e\0\x06\x04\0\x1e[method]stream-obj.re\ +ad-result\x01\x15\x04\0$[method]stream-obj.is-ready-to-write\x01\x10\x04\0([meth\ +od]stream-obj.write-ready-subscribe\x01\x14\x04\0\x20[method]stream-obj.start-wr\ +iting\x01\x15\x04\0![method]stream-obj.finish-writing\x01\x11\x04\0&[method]stre\ +am-obj.read-ready-activate\x01\x12\x01@\0\0\x06\x04\0\x0bend-of-file\x01\x16\x01\ +@\x01\x03obj\x08\0\x7f\x04\0\x0eis-end-of-file\x01\x17\x04\0(symmetric:runtime/s\ +ymmetric-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)] diff --git a/crates/symmetric_executor/wit/executor.wit b/crates/symmetric_executor/wit/executor.wit index fef7cfddc..d8f758de0 100644 --- a/crates/symmetric_executor/wit/executor.wit +++ b/crates/symmetric_executor/wit/executor.wit @@ -59,7 +59,7 @@ interface symmetric-executor { // language neutral stream implementation interface symmetric-stream { - use symmetric-executor.{event-generator}; + use symmetric-executor.{event-subscription}; resource address; // special zero allocation/copy data type (caller provided buffer) @@ -78,15 +78,15 @@ interface symmetric-stream { // reading (in roughly chronological order) is-write-closed: func() -> bool; start-reading: func(buffer: buffer); - read-ready-event: func() -> event-generator; + write-ready-activate: func(); + read-ready-subscribe: func() -> event-subscription; read-result: func() -> buffer; - // close-read: static func(%stream: stream-obj); // writing is-ready-to-write: func() -> bool; - write-ready-event: func() -> event-generator; + write-ready-subscribe: func() -> event-subscription; start-writing: func() -> buffer; finish-writing: func(buffer: buffer); - // close-write: static func(%stream: stream-obj); + read-ready-activate: func(); } // special EOF buffer value (should be opaque) From 0de6876e8804de63424a967f8a4e80ba3b3b212f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 12 Jan 2025 20:11:43 +0100 Subject: [PATCH 469/672] fix crash --- .../rust-client/src/async_support/stream_support.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 index e7012cb0e..79e02ff28 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -220,7 +220,9 @@ impl futures::stream::Stream for StreamReader { impl Drop for StreamReader { fn drop(&mut self) { - self.handle.write_ready_activate(); + if self.handle.handle()!=0 { + self.handle.write_ready_activate(); + } } } From a850d156474310566239735078ee29e0b857efbc Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 12 Jan 2025 20:52:40 +0100 Subject: [PATCH 470/672] besides tear down everything works --- .../src/async_support/stream_support.rs | 19 +++++--- .../symmetric_stream/src/lib.rs | 45 ++++++++++++------- 2 files changed, 42 insertions(+), 22 deletions(-) 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 index 79e02ff28..270f7232c 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -2,6 +2,7 @@ pub use crate::module::symmetric::runtime::symmetric_stream::StreamObj as Stream use crate::{ async_support::wait_on, module::symmetric::runtime::symmetric_stream::{self, end_of_file}, + symmetric_stream::{Address, Buffer}, EventGenerator, }; use { @@ -174,14 +175,20 @@ impl futures::stream::Stream for StreamReader { if me.future.is_none() { let handle = me.handle.clone(); me.future = Some(Box::pin(async move { - // let mut buffer = iter::repeat_with(MaybeUninit::uninit) - // .take(ceiling(4 * 1024, mem::size_of::())) - // .collect::>(); + let mut buffer0 = iter::repeat_with(MaybeUninit::uninit) + .take(ceiling(4 * 1024, mem::size_of::())) + .collect::>(); // let stream_handle = handle; + 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(); + let count = buffer2.get_size(); + buffer0.truncate(count as usize); + Some(unsafe { mem::transmute::>, Vec>(buffer0) }) // let result = if let Some(count) = { // let poll_fn: unsafe fn(*mut Stream, *mut (), usize) -> isize = start_reading; // let address = super::AddressSend(buffer.as_mut_ptr() as _); @@ -202,8 +209,8 @@ impl futures::stream::Stream for StreamReader { // } else { // None // }; - todo!(); - Some(Vec::new()) + // todo!(); + // Some(Vec::new()) // result }) as Pin + Send>>); } @@ -220,7 +227,7 @@ impl futures::stream::Stream for StreamReader { impl Drop for StreamReader { fn drop(&mut self) { - if self.handle.handle()!=0 { + if self.handle.handle() != 0 { self.handle.write_ready_activate(); } } diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index bf8cf9a09..cf00ae1fc 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -1,5 +1,6 @@ use std::{ mem::transmute, + ptr::null_mut, sync::{ atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}, Arc, @@ -12,6 +13,7 @@ use stream_impl::exports::symmetric::runtime::symmetric_stream::{ use stream_impl::symmetric::runtime::symmetric_executor::{ self, EventGenerator, EventSubscription, }; +use wit_bindgen_symmetric_rt::symmetric_stream::is_end_of_file; //use wit_bindgen_symmetric_rt::{async_support::Stream, EventGenerator}; mod stream_impl; @@ -67,8 +69,9 @@ struct StreamInner { write_ready_event_send: EventGenerator, read_addr: AtomicPtr<()>, read_size: AtomicUsize, + ready_addr: AtomicPtr<()>, ready_size: AtomicIsize, - active_instances: AtomicUsize, + // active_instances: AtomicUsize, } struct StreamObj(Arc); @@ -80,8 +83,9 @@ impl GuestStreamObj for StreamObj { 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), - active_instances: AtomicUsize::new(2), + // active_instances: AtomicUsize::new(1), }; Self(Arc::new(inner)) } @@ -115,11 +119,12 @@ impl GuestStreamObj for StreamObj { // } fn read_result(&self) -> symmetric_stream::Buffer { - self.0.ready_size.swap(results::BLOCKED, Ordering::Acquire); + let size = self.0.ready_size.swap(results::BLOCKED, Ordering::Acquire); + let addr = self.0.ready_addr.swap(null_mut(), Ordering::Acquire); symmetric_stream::Buffer::new(Buffer { - addr: todo!(), - capacity: todo!(), - size: todo!(), + addr, + capacity: size as usize, + size: AtomicUsize::new(size as usize), }) } @@ -140,7 +145,7 @@ impl GuestStreamObj for StreamObj { // } fn is_ready_to_write(&self) -> bool { - self.0.read_addr.load(Ordering::Acquire).is_null() + !self.0.read_addr.load(Ordering::Acquire).is_null() } // fn write_ready_event(&self) -> symmetric_stream::EventGenerator { @@ -156,24 +161,32 @@ impl GuestStreamObj for StreamObj { .0 .read_addr .swap(core::ptr::null_mut(), Ordering::Release); - todo!() - // Buffer { - // addr, - // capacity: 0, - // size, - // } - // Slice { addr, size } + symmetric_stream::Buffer::new(Buffer { + addr, + capacity: size, + size: AtomicUsize::new(0), + }) } fn finish_writing(&self, buffer: symmetric_stream::Buffer) -> () { - let elements = buffer.get::().get_size() as isize; - let old_ready = self.0.ready_size.swap(elements as isize, Ordering::Release); + let (elements, addr) = if buffer.handle() == EOF_MARKER { + // is_end_of_file(&buffer) { + let _ = buffer.take_handle(); + (0, EOF_MARKER as usize as *mut ()) + } else { + let elements = buffer.get::().get_size() as isize; + let addr = buffer.get::().get_address().take_handle() as *mut (); + (elements, addr) + }; + 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(); // unsafe { activate_event_send_ptr(self.read_ready_event) }; } fn clone(&self) -> symmetric_stream::StreamObj { + // let _= self.0.active_instances.fetch_add(1, Ordering::AcqRel); symmetric_stream::StreamObj::new(StreamObj(Arc::clone(&self.0))) } From 1ef3ddba85d6ae737c6c9db1a22edbfbade52286 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 12 Jan 2025 21:16:42 +0100 Subject: [PATCH 471/672] better eof handling, but not yet good --- crates/cpp/tests/symmetric_stream/source/src/lib.rs | 3 ++- .../cpp/tests/symmetric_stream/stream/src/stream_world.rs | 4 ++-- crates/symmetric_executor/symmetric_stream/src/lib.rs | 7 +++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index 962da4316..4958fc686 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -44,7 +44,8 @@ extern "C" fn write_ready(data: *mut ()) -> CallbackState { pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(results: *mut u8) -> *mut u8 { let stream = Stream::new(); let event = stream.write_ready_subscribe(); - register(event, write_ready, stream.handle() as *mut ()); + let stream_copy = stream.clone(); + register(event, write_ready, stream_copy.take_handle() as *mut ()); *unsafe { &mut *results.cast::() } = stream.take_handle(); std::ptr::null_mut() } diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 2abd13939..8dc04ef5d 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -18,7 +18,7 @@ pub mod test { #[allow(unused_unsafe, clippy::all)] pub async fn create() -> wit_bindgen_symmetric_rt::async_support::StreamReader { unsafe { - let layout1 = _rt::alloc::Layout::from_size_align_unchecked(4, 4); + let layout1 = _rt::alloc::Layout::from_size_align_unchecked(8, 8); let ptr1 = _rt::alloc::alloc(layout1); #[link(wasm_import_module = "test:test/stream-source")] @@ -35,7 +35,7 @@ pub mod test { let result4 = wit_bindgen_symmetric_rt::async_support::StreamReader::new( Stream::from_handle(l3 as usize), ); - _rt::cabi_dealloc(ptr1, 4, 4); + _rt::cabi_dealloc(ptr1, 8, 8); result4 } } diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index cf00ae1fc..293107310 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -13,7 +13,7 @@ use stream_impl::exports::symmetric::runtime::symmetric_stream::{ use stream_impl::symmetric::runtime::symmetric_executor::{ self, EventGenerator, EventSubscription, }; -use wit_bindgen_symmetric_rt::symmetric_stream::is_end_of_file; +use wit_bindgen_symmetric_rt::symmetric_stream::{end_of_file, is_end_of_file}; //use wit_bindgen_symmetric_rt::{async_support::Stream, EventGenerator}; mod stream_impl; @@ -121,11 +121,14 @@ impl GuestStreamObj for StreamObj { fn read_result(&self) -> symmetric_stream::Buffer { let size = self.0.ready_size.swap(results::BLOCKED, Ordering::Acquire); let addr = self.0.ready_addr.swap(null_mut(), Ordering::Acquire); + if addr == EOF_MARKER { + end_of_file() + } else { symmetric_stream::Buffer::new(Buffer { addr, capacity: size as usize, size: AtomicUsize::new(size as usize), - }) + }) } } // fn close_read(stream: symmetric_stream::StreamObj) -> () { From f19d9d29284b731b56f03868c2c81f37b14c11ba Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 12 Jan 2025 21:48:21 +0100 Subject: [PATCH 472/672] proper EOF implementation --- .../tests/symmetric_stream/main/src/main.rs | 6 +- .../tests/symmetric_stream/source/src/lib.rs | 4 +- .../src/async_support/stream_support.rs | 16 +-- .../rust-client/src/module.rs | 105 ++++++++++-------- .../symmetric_stream/src/lib.rs | 51 ++++----- .../symmetric_stream/src/stream_impl.rs | 103 ++++++++--------- crates/symmetric_executor/wit/executor.wit | 10 +- 7 files changed, 144 insertions(+), 151 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index 134b14972..e8996bdc3 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -1,5 +1,5 @@ use wit_bindgen_symmetric_rt::{ - async_support::{stream_support, Stream}, + async_support::Stream, symmetric_stream::{Address, Buffer}, CallbackState, }; @@ -19,9 +19,9 @@ struct CallbackInfo { extern "C" fn ready(arg: *mut ()) -> CallbackState { let info = unsafe { &*arg.cast::() }; let buffer = info.stream.read_result(); - let len = buffer.get_size(); // unsafe { stream_support::read_amount(info.stream) }; - if len > 0 { + if let Some(buffer) = buffer { + let len = buffer.get_size(); for i in 0..len as usize { println!("data {}", info.data[i]); } diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index 4958fc686..2adebb50f 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -16,7 +16,7 @@ extern "C" fn timer_call(data: *mut ()) -> CallbackState { assert!(size >= 1); *unsafe { &mut *addr } = count; buffer.set_size(1); - stream.finish_writing(buffer); + stream.finish_writing(Some(buffer)); } let _ = stream.take_handle(); CallbackState::Ready @@ -27,7 +27,7 @@ extern "C" fn write_ready(data: *mut ()) -> CallbackState { if count > 5 { let stream: Stream = unsafe { Stream::from_handle(data as usize) }; // EOF - stream.finish_writing(symmetric_stream::end_of_file()); + stream.finish_writing(None); CallbackState::Ready } else { if count == 1 { 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 index 270f7232c..831c3c0d6 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -1,9 +1,7 @@ pub use crate::module::symmetric::runtime::symmetric_stream::StreamObj as Stream; use crate::{ async_support::wait_on, - module::symmetric::runtime::symmetric_stream::{self, end_of_file}, symmetric_stream::{Address, Buffer}, - EventGenerator, }; use { futures::sink::Sink, @@ -104,7 +102,7 @@ impl Sink> for StreamWriter { a.write(b); } buffer.set_size(item_len as u64); - stream.finish_writing(buffer); + stream.finish_writing(Some(buffer)); Ok(()) } @@ -120,7 +118,7 @@ impl Sink> for StreamWriter { impl Drop for StreamWriter { fn drop(&mut self) { if !self.handle.is_write_closed() { - self.handle.finish_writing(end_of_file()); + self.handle.finish_writing(None); } } } @@ -186,9 +184,13 @@ impl futures::stream::Stream for StreamReader { subsc.reset(); wait_on(subsc).await; let buffer2 = handle.read_result(); - let count = buffer2.get_size(); - buffer0.truncate(count as usize); - Some(unsafe { mem::transmute::>, Vec>(buffer0) }) + if let Some(buffer2) = buffer2 { + let count = buffer2.get_size(); + buffer0.truncate(count as usize); + Some(unsafe { mem::transmute::>, Vec>(buffer0) }) + } else { + None + } // let result = if let Some(count) = { // let poll_fn: unsafe fn(*mut Stream, *mut (), usize) -> isize = start_reading; // let address = super::AddressSend(buffer.as_mut_ptr() as _); diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index fc58cc609..3f47e9e41 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -807,8 +807,19 @@ pub mod symmetric { } impl StreamObj { #[allow(unused_unsafe, clippy::all)] - pub fn read_result(&self) -> Buffer { + /// none is EOF + pub fn read_result(&self) -> Option { unsafe { + #[cfg_attr(target_pointer_width = "64", repr(align(8)))] + #[cfg_attr(target_pointer_width = "32", repr(align(4)))] + struct RetArea( + [::core::mem::MaybeUninit; 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::(); #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] extern "C" { #[cfg_attr( @@ -817,10 +828,25 @@ pub mod symmetric { )] fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_result( _: *mut u8, - ) -> *mut u8; + _: *mut u8, + ); + } + symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_result((self).handle() as *mut u8, ptr0); + let l1 = i32::from(*ptr0.add(0).cast::()); + 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(), } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_result((self).handle() as *mut u8); - Buffer::from_handle(ret as usize) } } } @@ -884,8 +910,13 @@ pub mod symmetric { } impl StreamObj { #[allow(unused_unsafe, clippy::all)] - pub fn finish_writing(&self, buffer: Buffer) -> () { + /// none is EOF + pub fn finish_writing(&self, buffer: Option) -> () { 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( @@ -894,10 +925,11 @@ pub mod symmetric { )] fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing( _: *mut u8, + _: i32, _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing((self).handle() as *mut u8, (&buffer).take_handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing((self).handle() as *mut u8, result0_0, result0_1); } } } @@ -919,36 +951,6 @@ pub mod symmetric { } } } - #[allow(unused_unsafe, clippy::all)] - /// special EOF buffer value (should be opaque) - pub fn end_of_file() -> Buffer { - unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "end-of-file")] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00end_of_file( - ) -> *mut u8; - } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00end_of_file(); - Buffer::from_handle(ret as usize) - } - } - #[allow(unused_unsafe, clippy::all)] - pub fn is_end_of_file(obj: &Buffer) -> bool { - unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "is-end-of-file")] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00is_end_of_file( - _: *mut u8, - ) -> i32; - } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00is_end_of_file( - (obj).handle() as *mut u8, - ); - _rt::bool_lift(ret as u8) - } - } } } } @@ -1088,14 +1090,21 @@ mod _rt { self as i64 } } + pub unsafe fn invalid_enum_discriminant() -> 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; 1733] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xc8\x0c\x01A\x02\x01\ +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\ @@ -1109,7 +1118,7 @@ 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\ +-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\ @@ -1121,15 +1130,15 @@ d]buffer.capacity\x01\x0a\x01i\x04\x01@\0\0\x0c\x04\0\x17[constructor]stream-obj \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\x01@\x01\x04self\x0e\0\x06\x04\0\x1e[method]stream-obj.re\ -ad-result\x01\x15\x04\0$[method]stream-obj.is-ready-to-write\x01\x10\x04\0([meth\ -od]stream-obj.write-ready-subscribe\x01\x14\x04\0\x20[method]stream-obj.start-wr\ -iting\x01\x15\x04\0![method]stream-obj.finish-writing\x01\x11\x04\0&[method]stre\ -am-obj.read-ready-activate\x01\x12\x01@\0\0\x06\x04\0\x0bend-of-file\x01\x16\x01\ -@\x01\x03obj\x08\0\x7f\x04\0\x0eis-end-of-file\x01\x17\x03\0(symmetric:runtime/s\ -ymmetric-stream@0.1.0\x05\x02\x04\0\x1esymmetric:runtime/module@0.1.0\x04\0\x0b\x0c\ -\x01\0\x06module\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-componen\ -t\x070.223.0\x10wit-bindgen-rust\x060.37.0"; +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)] diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index 293107310..421022296 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -13,8 +13,6 @@ use stream_impl::exports::symmetric::runtime::symmetric_stream::{ use stream_impl::symmetric::runtime::symmetric_executor::{ self, EventGenerator, EventSubscription, }; -use wit_bindgen_symmetric_rt::symmetric_stream::{end_of_file, is_end_of_file}; -//use wit_bindgen_symmetric_rt::{async_support::Stream, EventGenerator}; mod stream_impl; @@ -60,8 +58,8 @@ impl GuestBuffer for Buffer { mod results { pub const BLOCKED: isize = -1; - pub const CLOSED: isize = isize::MIN; - pub const CANCELED: isize = 0; + // pub const CLOSED: isize = isize::MIN; + // pub const CANCELED: isize = 0; } struct StreamInner { @@ -71,7 +69,7 @@ struct StreamInner { read_size: AtomicUsize, ready_addr: AtomicPtr<()>, ready_size: AtomicIsize, - // active_instances: AtomicUsize, + ready_capacity: AtomicUsize, } struct StreamObj(Arc); @@ -85,20 +83,21 @@ impl GuestStreamObj for StreamObj { read_size: AtomicUsize::new(0), ready_addr: AtomicPtr::new(core::ptr::null_mut()), ready_size: AtomicIsize::new(results::BLOCKED), - // active_instances: AtomicUsize::new(1), + ready_capacity: AtomicUsize::new(0), }; Self(Arc::new(inner)) } fn is_write_closed(&self) -> bool { - self.0.ready_size.load(Ordering::Acquire) == results::CLOSED + self.0.ready_addr.load(Ordering::Acquire) as usize == EOF_MARKER } fn start_reading(&self, buffer: symmetric_stream::Buffer) { let buf = buffer.get::().get_address().take_handle() as *mut (); let size = buffer.get::().capacity(); + let old_readya = self.0.ready_addr.load(Ordering::Acquire); let old_ready = self.0.ready_size.load(Ordering::Acquire); - if old_ready == results::CLOSED { + if old_readya as usize == EOF_MARKER { todo!(); // return old_ready; } @@ -118,17 +117,18 @@ impl GuestStreamObj for StreamObj { // } // } - fn read_result(&self) -> symmetric_stream::Buffer { + fn read_result(&self) -> Option { let size = self.0.ready_size.swap(results::BLOCKED, Ordering::Acquire); let addr = self.0.ready_addr.swap(null_mut(), Ordering::Acquire); - if addr == EOF_MARKER { - end_of_file() + if addr as usize == EOF_MARKER { + None } else { - symmetric_stream::Buffer::new(Buffer { - addr, - capacity: size as usize, - size: AtomicUsize::new(size as usize), - }) } + Some(symmetric_stream::Buffer::new(Buffer { + addr, + capacity: size as usize, + size: AtomicUsize::new(size as usize), + })) + } } // fn close_read(stream: symmetric_stream::StreamObj) -> () { @@ -171,15 +171,13 @@ impl GuestStreamObj for StreamObj { }) } - fn finish_writing(&self, buffer: symmetric_stream::Buffer) -> () { - let (elements, addr) = if buffer.handle() == EOF_MARKER { - // is_end_of_file(&buffer) { - let _ = buffer.take_handle(); - (0, EOF_MARKER as usize as *mut ()) - } else { + fn finish_writing(&self, buffer: Option) -> () { + let (elements, addr) = if let Some(buffer) = buffer { let elements = buffer.get::().get_size() as isize; let addr = buffer.get::().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); @@ -222,13 +220,4 @@ impl symmetric_stream::Guest for Guest { type Buffer = Buffer; type StreamObj = StreamObj; - - fn end_of_file() -> symmetric_stream::Buffer { - unsafe { symmetric_stream::Buffer::from_handle(EOF_MARKER) } - } - - fn is_end_of_file(obj: symmetric_stream::BufferBorrow<'_>) -> bool { - let ptr: *mut () = unsafe { transmute(obj) }; - ptr as usize == EOF_MARKER - } } diff --git a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs index abacc7000..49753ab71 100644 --- a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs +++ b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs @@ -1015,11 +1015,21 @@ pub mod exports { #[allow(non_snake_case)] pub unsafe fn _export_method_stream_obj_read_result_cabi( arg0: *mut u8, - ) -> *mut u8 { + arg1: *mut u8, + ) { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); let result0 = T::read_result(StreamObjBorrow::lift(arg0 as usize).get()); - (result0).take_handle() as *mut u8 + match result0 { + Some(e) => { + *arg1.add(0).cast::() = (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::() = (0i32) as u8; + } + }; } #[doc(hidden)] #[allow(non_snake_case)] @@ -1063,13 +1073,21 @@ pub mod exports { #[allow(non_snake_case)] pub unsafe fn _export_method_stream_obj_finish_writing_cabi( arg0: *mut u8, - arg1: *mut u8, + arg1: i32, + arg2: *mut u8, ) { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); T::finish_writing( StreamObjBorrow::lift(arg0 as usize).get(), - Buffer::from_handle(arg1 as usize), + match arg1 { + 0 => None, + 1 => { + let e = Buffer::from_handle(arg2 as usize); + Some(e) + } + _ => _rt::invalid_enum_discriminant(), + }, ); } #[doc(hidden)] @@ -1083,32 +1101,10 @@ pub mod exports { _rt::run_ctors_once(); T::read_ready_activate(StreamObjBorrow::lift(arg0 as usize).get()); } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn _export_end_of_file_cabi() -> *mut u8 { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); - let result0 = T::end_of_file(); - (result0).take_handle() as *mut u8 - } - #[doc(hidden)] - #[allow(non_snake_case)] - pub unsafe fn _export_is_end_of_file_cabi(arg0: *mut u8) -> i32 { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); - let result0 = T::is_end_of_file(BufferBorrow::lift(arg0 as usize)); - match result0 { - true => 1, - false => 0, - } - } pub trait Guest { type Address: GuestAddress; type Buffer: GuestBuffer; type StreamObj: GuestStreamObj; - /// special EOF buffer value (should be opaque) - fn end_of_file() -> Buffer; - fn is_end_of_file(obj: BufferBorrow<'_>) -> bool; } #[doc(hidden)] #[allow(non_snake_case)] @@ -1196,12 +1192,14 @@ pub mod exports { fn start_reading(&self, buffer: Buffer) -> (); fn write_ready_activate(&self) -> (); fn read_ready_subscribe(&self) -> EventSubscription; - fn read_result(&self) -> Buffer; + /// none is EOF + fn read_result(&self) -> Option; /// writing fn is_ready_to_write(&self) -> bool; fn write_ready_subscribe(&self) -> EventSubscription; fn start_writing(&self) -> Buffer; - fn finish_writing(&self, buffer: Buffer) -> (); + /// none is EOF + fn finish_writing(&self, buffer: Option) -> (); fn read_ready_activate(&self) -> (); } #[doc(hidden)] @@ -1266,8 +1264,8 @@ pub mod exports { } #[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,) -> *mut u8 { - $($path_to_types)*::_export_method_stream_obj_read_result_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0) + 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)] @@ -1286,24 +1284,14 @@ pub mod exports { } #[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: *mut u8,) { - $($path_to_types)*::_export_method_stream_obj_finish_writing_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0, arg1) + 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(target_arch = "wasm32", export_name = "end-of-file")] - #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00end_of_file() -> *mut u8 { - $($path_to_types)*::_export_end_of_file_cabi::<$ty>() - } - #[cfg_attr(target_arch = "wasm32", export_name = "is-end-of-file")] - #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00is_end_of_file(arg0: *mut u8,) -> i32 { - $($path_to_types)*::_export_is_end_of_file_cabi::<$ty>(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) @@ -1469,6 +1457,13 @@ mod _rt { pub fn run_ctors_once() { wit_bindgen::rt::run_ctors_once(); } + pub unsafe fn invalid_enum_discriminant() -> T { + if cfg!(debug_assertions) { + panic!("invalid enum discriminant") + } else { + core::hint::unreachable_unchecked() + } + } extern crate alloc as alloc_crate; } @@ -1504,8 +1499,8 @@ pub(crate) use __export_stream_impl_impl as export; #[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; 1743] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xcd\x0c\x01A\x02\x01\ +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\ @@ -1519,7 +1514,7 @@ 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\ +-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\ @@ -1531,15 +1526,15 @@ d]buffer.capacity\x01\x0a\x01i\x04\x01@\0\0\x0c\x04\0\x17[constructor]stream-obj \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\x01@\x01\x04self\x0e\0\x06\x04\0\x1e[method]stream-obj.re\ -ad-result\x01\x15\x04\0$[method]stream-obj.is-ready-to-write\x01\x10\x04\0([meth\ -od]stream-obj.write-ready-subscribe\x01\x14\x04\0\x20[method]stream-obj.start-wr\ -iting\x01\x15\x04\0![method]stream-obj.finish-writing\x01\x11\x04\0&[method]stre\ -am-obj.read-ready-activate\x01\x12\x01@\0\0\x06\x04\0\x0bend-of-file\x01\x16\x01\ -@\x01\x03obj\x08\0\x7f\x04\0\x0eis-end-of-file\x01\x17\x04\0(symmetric:runtime/s\ -ymmetric-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"; +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)] diff --git a/crates/symmetric_executor/wit/executor.wit b/crates/symmetric_executor/wit/executor.wit index d8f758de0..edc79583c 100644 --- a/crates/symmetric_executor/wit/executor.wit +++ b/crates/symmetric_executor/wit/executor.wit @@ -80,16 +80,14 @@ interface symmetric-stream { start-reading: func(buffer: buffer); write-ready-activate: func(); read-ready-subscribe: func() -> event-subscription; - read-result: func() -> buffer; + // none is EOF + read-result: func() -> option; // writing is-ready-to-write: func() -> bool; write-ready-subscribe: func() -> event-subscription; start-writing: func() -> buffer; - finish-writing: func(buffer: buffer); + // none is EOF + finish-writing: func(buffer: option); read-ready-activate: func(); } - - // special EOF buffer value (should be opaque) - end-of-file: func() -> buffer; - is-end-of-file: func(obj: borrow) -> bool; } From c9f748a9fede17029e66ebb389a6c35136cfeed8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 12 Jan 2025 22:05:19 +0100 Subject: [PATCH 473/672] clean up --- .../tests/symmetric_stream/source/src/lib.rs | 2 +- .../rust-client/src/async_support.rs | 33 ------------ .../src/async_support/stream_support.rs | 29 ---------- .../symmetric_stream/src/lib.rs | 53 +++---------------- .../symmetric_stream/src/stream_impl.rs | 5 +- 5 files changed, 10 insertions(+), 112 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index 2adebb50f..d177b9f92 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -1,7 +1,7 @@ use std::sync::atomic::{AtomicU32, Ordering}; use wit_bindgen_symmetric_rt::{ - async_support::Stream, register, symmetric_stream, CallbackState, EventSubscription, + async_support::Stream, register, CallbackState, EventSubscription, }; static COUNT: AtomicU32 = AtomicU32::new(1); diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index ae3643e4c..563df635a 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -11,8 +11,6 @@ use crate::module::symmetric::runtime::symmetric_executor::{ pub use stream_support::{results, Stream, StreamHandle2, StreamReader, StreamWriter}; -// pub use futures; - mod future_support; // later make it non-pub pub mod stream_support; @@ -139,34 +137,3 @@ pub fn spawn(future: impl Future + 'static + Send) { let wait_for = unsafe { EventSubscription::from_handle(wait_for as usize) }; drop(wait_for); } - -// #[repr(transparent)] -// pub struct AddressSend(pub *mut ()); -// unsafe impl Send for AddressSend {} -// unsafe impl Sync for StreamHandle2 {} - -// this is used for reading? -// pub async unsafe fn await_stream_result( -// import: unsafe extern "C" fn(&Stream, Buffer) -> Buffer, -// stream: StreamHandle2, -// buffer: Buffer, -// ) -> Option { -// let stream_copy = stream.clone(); -// let result = import(&stream, buffer); -// match result { -// results::BLOCKED => { -// let event = -// unsafe { subscribe_event_send_ptr(stream_support::read_ready_event(stream.0)) }; -// event.reset(); -// wait_on(event).await; -// let v = stream.read_result(); -// if let results::CLOSED | results::CANCELED = v { -// None -// } else { -// Some(usize::try_from(v).unwrap()) -// } -// } -// results::CLOSED | results::CANCELED => None, -// v => Some(usize::try_from(v).unwrap()), -// } -// } 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 index 831c3c0d6..5a53538bc 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -127,7 +127,6 @@ impl Drop for StreamWriter { pub struct StreamReader { handle: StreamHandle2, future: Option>> + 'static + Send>>>, - // event: EventSubscription, _phantom: PhantomData, } @@ -176,7 +175,6 @@ impl futures::stream::Stream for StreamReader { let mut buffer0 = iter::repeat_with(MaybeUninit::uninit) .take(ceiling(4 * 1024, mem::size_of::())) .collect::>(); - // let stream_handle = handle; 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); @@ -191,29 +189,6 @@ impl futures::stream::Stream for StreamReader { } else { None } - // let result = if let Some(count) = { - // let poll_fn: unsafe fn(*mut Stream, *mut (), usize) -> isize = start_reading; - // let address = super::AddressSend(buffer.as_mut_ptr() as _); - // let count = unsafe { - // super::await_stream_result(poll_fn, stream_handle, address, buffer.len()) - // .await - // }; - // #[allow(unused)] - // if let Some(count) = count { - // let value = (); - // } - // count - // } - // T::read(&stream_handle, &mut buffer).await - // { - // buffer.truncate(count); - // Some(unsafe { mem::transmute::>, Vec>(buffer) }) - // } else { - // None - // }; - // todo!(); - // Some(Vec::new()) - // result }) as Pin + Send>>); } @@ -235,11 +210,7 @@ impl Drop for StreamReader { } } -// Stream handles are Send, so wrap them -// #[repr(transparent)] pub type StreamHandle2 = Stream; -// unsafe impl Send for StreamHandle2 {} -// unsafe impl Sync for StreamHandle2 {} pub fn new_stream() -> (StreamWriter, StreamReader) { let handle = Stream::new(); diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index 421022296..b4592d757 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -1,5 +1,4 @@ use std::{ - mem::transmute, ptr::null_mut, sync::{ atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}, @@ -10,9 +9,7 @@ use std::{ use stream_impl::exports::symmetric::runtime::symmetric_stream::{ self, Address, GuestAddress, GuestBuffer, GuestStreamObj, }; -use stream_impl::symmetric::runtime::symmetric_executor::{ - self, EventGenerator, EventSubscription, -}; +use stream_impl::symmetric::runtime::symmetric_executor::EventGenerator; mod stream_impl; @@ -58,8 +55,6 @@ impl GuestBuffer for Buffer { mod results { pub const BLOCKED: isize = -1; - // pub const CLOSED: isize = isize::MIN; - // pub const CANCELED: isize = 0; } struct StreamInner { @@ -99,7 +94,6 @@ impl GuestStreamObj for StreamObj { let old_ready = self.0.ready_size.load(Ordering::Acquire); if old_readya as usize == EOF_MARKER { todo!(); - // return old_ready; } assert!(old_ready == results::BLOCKED); let old_size = self.0.read_size.swap(size as usize, Ordering::Acquire); @@ -107,63 +101,34 @@ impl GuestStreamObj for StreamObj { let old_ptr = self.0.read_addr.swap(buf, Ordering::Release); assert_eq!(old_ptr, std::ptr::null_mut()); self.write_ready_activate(); - // unsafe { activate_event_send_ptr(write_evt) }; - // results::BLOCKED } - // fn read_ready_event(&self) -> symmetric_stream::EventGenerator { - // unsafe { - // symmetric_stream::EventGenerator::from_handle(self.read_ready_event_send as usize) - // } - // } - fn read_result(&self) -> Option { let size = self.0.ready_size.swap(results::BLOCKED, Ordering::Acquire); - let addr = self.0.ready_addr.swap(null_mut(), 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 as usize, + capacity, size: AtomicUsize::new(size as usize), })) } } - // fn close_read(stream: symmetric_stream::StreamObj) -> () { - // let refs = unsafe { &mut *stream } - // .active_instances - // .fetch_sub(1, Ordering::AcqRel); - // if refs == 1 { - // let obj = Box::from_raw(stream); - // drop(EventGenerator::from_handle( - // obj.read_ready_event_send as usize, - // )); - // drop(EventGenerator::from_handle( - // obj.write_ready_event_send as usize, - // )); - // drop(obj); - // } - // } - fn is_ready_to_write(&self) -> bool { !self.0.read_addr.load(Ordering::Acquire).is_null() } - // fn write_ready_event(&self) -> symmetric_stream::EventGenerator { - // //self.write_ready_event_send.clone() - // unsafe { - // symmetric_stream::EventGenerator::from_handle(self.write_ready_event_send as usize) - // } - // } - 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::Release); + .swap(core::ptr::null_mut(), Ordering::Relaxed); + self.0.ready_capacity.store(size, Ordering::Release); symmetric_stream::Buffer::new(Buffer { addr, capacity: size, @@ -183,11 +148,9 @@ impl GuestStreamObj for StreamObj { let _old_readya = self.0.ready_addr.swap(addr, Ordering::Release); assert_eq!(old_ready, results::BLOCKED); self.read_ready_activate(); - // unsafe { activate_event_send_ptr(self.read_ready_event) }; } fn clone(&self) -> symmetric_stream::StreamObj { - // let _= self.0.active_instances.fetch_add(1, Ordering::AcqRel); symmetric_stream::StreamObj::new(StreamObj(Arc::clone(&self.0))) } @@ -206,10 +169,6 @@ impl GuestStreamObj for StreamObj { fn read_ready_activate(&self) { self.0.read_ready_event_send.activate(); } - - // fn close_write(stream: symmetric_stream::StreamObj) -> () { - // todo!() - // } } const EOF_MARKER: usize = 1; diff --git a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs index 49753ab71..9f6e88524 100644 --- a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs +++ b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs @@ -1023,8 +1023,9 @@ pub mod exports { match result0 { Some(e) => { *arg1.add(0).cast::() = (1i32) as u8; - *arg1.add(core::mem::size_of::<*const u8>()).cast::<*mut u8>() = - (e).take_handle() as *mut u8; + *arg1 + .add(core::mem::size_of::<*const u8>()) + .cast::<*mut u8>() = (e).take_handle() as *mut u8; } None => { *arg1.add(0).cast::() = (0i32) as u8; From 1f881399fef80a6c60a60a41006bcf94719c50cd Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 13 Jan 2025 00:48:49 +0100 Subject: [PATCH 474/672] start C++ nightmare experiment --- crates/cpp/src/lib.rs | 1 + .../symmetric_async/async_cpp/generate.sh | 2 + .../async_cpp/src/async_module.cpp | 79 ++++++ .../async_cpp/src/async_module_cpp.h | 16 ++ .../symmetric_async/async_cpp/src/middle.cpp | 22 ++ .../async_cpp/src/module_cpp.h | 1 + .../async_cpp/src/wit-common.h | 1 + .../symmetric_async/async_cpp/src/wit-guest.h | 1 + .../symmetric_executor/cpp-client/generate.sh | 1 + .../symmetric_executor/cpp-client/module.cpp | 246 ++++++++++++++++++ .../cpp-client/module_cpp.h | 128 +++++++++ 11 files changed, 498 insertions(+) create mode 100644 crates/cpp/tests/symmetric_async/async_cpp/generate.sh create mode 100644 crates/cpp/tests/symmetric_async/async_cpp/src/async_module.cpp create mode 100644 crates/cpp/tests/symmetric_async/async_cpp/src/async_module_cpp.h create mode 100644 crates/cpp/tests/symmetric_async/async_cpp/src/middle.cpp create mode 120000 crates/cpp/tests/symmetric_async/async_cpp/src/module_cpp.h create mode 120000 crates/cpp/tests/symmetric_async/async_cpp/src/wit-common.h create mode 120000 crates/cpp/tests/symmetric_async/async_cpp/src/wit-guest.h create mode 100644 crates/symmetric_executor/cpp-client/generate.sh create mode 100644 crates/symmetric_executor/cpp-client/module.cpp create mode 100644 crates/symmetric_executor/cpp-client/module_cpp.h diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7538126a8..df8593610 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1986,6 +1986,7 @@ impl CppInterfaceGenerator<'_> { (false, Flavor::Result(AbiVariant::GuestImport)) | (true, Flavor::Result(AbiVariant::GuestExport)) => (), (_, Flavor::InStruct) => (), + (false, Flavor::BorrowedArgument) => (), (_, _) => todo!(), } typename 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..8d519d191 --- /dev/null +++ b/crates/cpp/tests/symmetric_async/async_cpp/src/async_module.cpp @@ -0,0 +1,79 @@ +// 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 // realloc +#include + +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 void fulfil_promise(void* data) { + std::unique_ptr> ptr((std::promise*)data); + ptr->set_value(); +} + +extern "C" void* testX3AtestX2FwaitX00X5BasyncX5Dsleep(int64_t); +std::future test::test::wait::Sleep(uint64_t nanoseconds) +{ + std::promise result; + std::future result1 = result.get_future(); + void* wait = testX3AtestX2FwaitX00X5BasyncX5Dsleep((int64_t(nanoseconds))); + if (!wait) { result.set_value(); } else { + std::unique_ptr> ptr = std::make_unique>(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; +} +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 keep_alive = std::make_shared>(std::move(std::future())); + auto task = std::async(std::launch::async, [store](std::future&& result1, + symmetric::runtime::symmetric_executor::EventGenerator &&gen, + std::shared_ptr> keep_alive){ + store(result1.get()); + gen.Activate(); + }, std::move(result1), std::move(gen), keep_alive); + std::swap(*keep_alive, task); + 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 +#include +#include +#include +#include +namespace test {namespace test {namespace wait {std::future Sleep(uint64_t nanoseconds); +// export_interface Interface(Id { idx: 0 }) +}}} +namespace exports {namespace test {namespace test {namespace string_delay {std::future 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..6d6c69e9b --- /dev/null +++ b/crates/cpp/tests/symmetric_async/async_cpp/src/middle.cpp @@ -0,0 +1,22 @@ +#include "async_module_cpp.h" +#include + +std::future exports::test::test::string_delay::Forward(std::string_view s) { + if (s[0]=='A') { + std::promise 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(5*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/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..47effc7ce --- /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 // 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, int32_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::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 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) const +{ + int32_t option2; + int32_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 = int32_t(0); + } + 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..27ca6929c --- /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 +#include +#include +#include +#include +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 ReadResult() const; + bool IsReadyToWrite() const; + symmetric_executor::EventSubscription WriteReadySubscribe() const; + Buffer StartWriting() const; + void FinishWriting(std::optional buffer) const; + void ReadReadyActivate() const; + StreamObj(wit::ResourceImportBase &&); + StreamObj(StreamObj&&) = default; + StreamObj& operator=(StreamObj&&) = default; +}; + +}}} + +#endif From 392ba147fa59f2697124bc4afda1b4a04a77cc72 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 13 Jan 2025 23:19:14 +0100 Subject: [PATCH 475/672] partially working C++ example --- crates/cpp/tests/symmetric_async/Cargo.lock | 426 +----------------- .../tests/symmetric_async/async_cpp/Makefile | 13 + .../async_cpp/src/async_module.cpp | 2 +- .../symmetric_async/async_module/Cargo.toml | 13 +- .../cpp/tests/symmetric_async/main/Cargo.toml | 6 +- .../tests/symmetric_async/sleep/Cargo.toml | 2 +- crates/symmetric_executor/cpp-client/Makefile | 9 + .../symmetric_executor/cpp-client/module.cpp | 8 +- .../cpp-client/module_cpp.h | 2 +- 9 files changed, 55 insertions(+), 426 deletions(-) create mode 100644 crates/cpp/tests/symmetric_async/async_cpp/Makefile create mode 100644 crates/symmetric_executor/cpp-client/Makefile diff --git a/crates/cpp/tests/symmetric_async/Cargo.lock b/crates/cpp/tests/symmetric_async/Cargo.lock index d801f4b8c..e309431df 100644 --- a/crates/cpp/tests/symmetric_async/Cargo.lock +++ b/crates/cpp/tests/symmetric_async/Cargo.lock @@ -2,38 +2,15 @@ # 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 = "async_module" version = "0.1.0" dependencies = [ + "dummy-rt", "futures", "sleep", "symmetric_executor", - "wit-bindgen", + "symmetric_stream", "wit-bindgen-symmetric-rt", ] @@ -44,43 +21,8 @@ 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" +name = "dummy-rt" +version = "0.1.0" [[package]] name = "futures" @@ -171,68 +113,12 @@ dependencies = [ "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 = "main" version = "0.1.0" @@ -248,30 +134,6 @@ 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" @@ -284,16 +146,6 @@ 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" @@ -312,56 +164,6 @@ 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" @@ -379,28 +181,21 @@ dependencies = [ ] [[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" +name = "symmetric_executor" +version = "0.1.0" dependencies = [ - "smallvec", + "dummy-rt", + "futures", + "libc", ] [[package]] -name = "symmetric_executor" +name = "symmetric_stream" version = "0.1.0" dependencies = [ - "futures", - "libc", - "wit-bindgen", - "wit-bindgen-rt", + "dummy-rt", + "symmetric_executor", + "wit-bindgen-symmetric-rt", ] [[package]] @@ -420,203 +215,10 @@ 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 = [ + "dummy-rt", "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/cpp/tests/symmetric_async/async_cpp/Makefile b/crates/cpp/tests/symmetric_async/async_cpp/Makefile new file mode 100644 index 000000000..ae22820bb --- /dev/null +++ b/crates/cpp/tests/symmetric_async/async_cpp/Makefile @@ -0,0 +1,13 @@ +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 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 index 8d519d191..8aed915c1 100644 --- a/crates/cpp/tests/symmetric_async/async_cpp/src/async_module.cpp +++ b/crates/cpp/tests/symmetric_async/async_cpp/src/async_module.cpp @@ -44,7 +44,7 @@ std::future test::test::wait::Sleep(uint64_t nanoseconds) return result1; } extern "C" -void* testX3AtestX2Fstring_delayX00X5BasyncX5Dforward(uint8_t* arg0, size_t arg1, uint8_t* arg2) +void* X5BasyncX5DtestX3AtestX2Fstring_delayX00forward(uint8_t* arg0, size_t arg1, uint8_t* arg2) { auto len0 = arg1; diff --git a/crates/cpp/tests/symmetric_async/async_module/Cargo.toml b/crates/cpp/tests/symmetric_async/async_module/Cargo.toml index 9589e932d..96d7509f2 100644 --- a/crates/cpp/tests/symmetric_async/async_module/Cargo.toml +++ b/crates/cpp/tests/symmetric_async/async_module/Cargo.toml @@ -5,10 +5,15 @@ edition = "2021" [dependencies] futures = "0.3.31" -sleep = { version = "0.1.0", path = "../sleep" } -symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor" } -wit-bindgen = { version = "0.36.0", path = "../../../../guest-rust" } -wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../../../../symmetric_executor/rust-client" } +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/main/Cargo.toml b/crates/cpp/tests/symmetric_async/main/Cargo.toml index d769962ef..88a81ae31 100644 --- a/crates/cpp/tests/symmetric_async/main/Cargo.toml +++ b/crates/cpp/tests/symmetric_async/main/Cargo.toml @@ -4,6 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] -async_module = { version = "0.1.0", path = "../async_module" } -symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor", features = ["trace"] } -wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../../../../symmetric_executor/rust-client" } +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/sleep/Cargo.toml b/crates/cpp/tests/symmetric_async/sleep/Cargo.toml index df2cbbea3..8d73cef54 100644 --- a/crates/cpp/tests/symmetric_async/sleep/Cargo.toml +++ b/crates/cpp/tests/symmetric_async/sleep/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor" } +symmetric_executor = { path = "../../../../symmetric_executor" } [lib] crate-type = ["cdylib"] 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/module.cpp b/crates/symmetric_executor/cpp-client/module.cpp index 47effc7ce..b24ac613d 100644 --- a/crates/symmetric_executor/cpp-client/module.cpp +++ b/crates/symmetric_executor/cpp-client/module.cpp @@ -53,7 +53,7 @@ extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5 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, int32_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() { @@ -223,17 +223,17 @@ symmetric::runtime::symmetric_stream::Buffer symmetric::runtime::symmetric_strea auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_writing((*this).get_handle()); return wit::ResourceImportBase{ret}; } -void symmetric::runtime::symmetric_stream::StreamObj::FinishWriting(std::optional buffer) const +void symmetric::runtime::symmetric_stream::StreamObj::FinishWriting(std::optional &&buffer) const { int32_t option2; - int32_t option3; + 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 = int32_t(0); + option3 = nullptr; } symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing((*this).get_handle(), option2, option3); } diff --git a/crates/symmetric_executor/cpp-client/module_cpp.h b/crates/symmetric_executor/cpp-client/module_cpp.h index 27ca6929c..e4d9a7e9f 100644 --- a/crates/symmetric_executor/cpp-client/module_cpp.h +++ b/crates/symmetric_executor/cpp-client/module_cpp.h @@ -116,7 +116,7 @@ class StreamObj : public wit::ResourceImportBase{ bool IsReadyToWrite() const; symmetric_executor::EventSubscription WriteReadySubscribe() const; Buffer StartWriting() const; - void FinishWriting(std::optional buffer) const; + void FinishWriting(std::optional &&buffer) const; void ReadReadyActivate() const; StreamObj(wit::ResourceImportBase &&); StreamObj(StreamObj&&) = default; From e0a2654f75097aaffe7a2d29714bb6c45913b491 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 13 Jan 2025 23:27:31 +0100 Subject: [PATCH 476/672] ignore --- crates/cpp/tests/symmetric_async/async_cpp/.gitignore | 2 ++ crates/cpp/tests/symmetric_async/async_cpp/Makefile | 3 +++ crates/symmetric_executor/cpp-client/.gitignore | 2 ++ 3 files changed, 7 insertions(+) create mode 100644 crates/cpp/tests/symmetric_async/async_cpp/.gitignore create mode 100644 crates/symmetric_executor/cpp-client/.gitignore 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 index ae22820bb..7bc4e0fd8 100644 --- a/crates/cpp/tests/symmetric_async/async_cpp/Makefile +++ b/crates/cpp/tests/symmetric_async/async_cpp/Makefile @@ -11,3 +11,6 @@ libasync_module.so: async_module.o middle.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/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 From 97d5c397b697c6f86c4c32326d9af54bb0aa6666 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 13 Jan 2025 23:43:16 +0100 Subject: [PATCH 477/672] correct waiting time --- crates/cpp/tests/symmetric_async/async_cpp/src/middle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/tests/symmetric_async/async_cpp/src/middle.cpp b/crates/cpp/tests/symmetric_async/async_cpp/src/middle.cpp index 6d6c69e9b..3ff4782ca 100644 --- a/crates/cpp/tests/symmetric_async/async_cpp/src/middle.cpp +++ b/crates/cpp/tests/symmetric_async/async_cpp/src/middle.cpp @@ -8,7 +8,7 @@ std::future exports::test::test::string_delay::Forward(std::string_ return result.get_future(); } else if (s[0]=='B') { return std::async(std::launch::async, [](){ - auto delay = ::test::test::wait::Sleep(5*1000*1000*1000); + auto delay = ::test::test::wait::Sleep(5ull*1000*1000*1000); delay.wait(); return wit::string::from_view("after five seconds"); }); From 6ee1647b81aa04db2eeb94206c631c7919e4876e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 14 Jan 2025 23:40:47 +0100 Subject: [PATCH 478/672] oncelock implementation --- crates/symmetric_executor/src/lib.rs | 35 +++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 7019a1fe0..439e85efb 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -3,7 +3,7 @@ use std::{ mem::transmute, sync::{ atomic::{AtomicBool, AtomicU32, Ordering}, - Arc, Mutex, + Arc, Mutex, OnceLock, }, time::{Duration, SystemTime}, }; @@ -109,10 +109,12 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { struct Executor { active_tasks: Vec, + change_event: OnceLock, } static EXECUTOR: Mutex = Mutex::new(Executor { active_tasks: Vec::new(), + change_event: OnceLock::new(), }); // while executing tasks from the loop we can't directly queue new ones static EXECUTOR_BUSY: AtomicBool = AtomicBool::new(false); @@ -125,17 +127,25 @@ impl symmetric_executor::Guest for Guest { type EventGenerator = EventGenerator; fn run() { + let change_event = *EXECUTOR + .lock() + .unwrap() + .change_event + .get_or_init(|| 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 = 0; + let mut maxfd = change_event + 1; let now = SystemTime::now(); let mut rfds = core::mem::MaybeUninit::::uninit(); let rfd_ptr = unsafe { core::ptr::from_mut(rfds.assume_init_mut()) }; 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); @@ -306,7 +316,26 @@ impl symmetric_executor::Guest for Guest { } } match EXECUTOR.try_lock() { - Ok(mut lock) => lock.active_tasks.push(subscr), + Ok(mut lock) => { + lock.active_tasks.push(subscr); + let file_signal: u64 = 1; + let fd = *lock + .change_event + .get_or_init(|| 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); From f7e0376556e08a64f0c63960f42e19e2026402b7 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 14 Jan 2025 23:47:29 +0100 Subject: [PATCH 479/672] Option works equally well --- crates/symmetric_executor/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 439e85efb..5a2bcbf5f 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -109,12 +109,12 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { struct Executor { active_tasks: Vec, - change_event: OnceLock, + change_event: Option, } static EXECUTOR: Mutex = Mutex::new(Executor { active_tasks: Vec::new(), - change_event: OnceLock::new(), + change_event: None, }); // while executing tasks from the loop we can't directly queue new ones static EXECUTOR_BUSY: AtomicBool = AtomicBool::new(false); @@ -131,7 +131,7 @@ impl symmetric_executor::Guest for Guest { .lock() .unwrap() .change_event - .get_or_init(|| unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }); + .get_or_insert_with(|| unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }); loop { let mut wait = libc::timeval { tv_sec: i64::MAX, @@ -321,7 +321,7 @@ impl symmetric_executor::Guest for Guest { let file_signal: u64 = 1; let fd = *lock .change_event - .get_or_init(|| unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }); + .get_or_insert_with(|| unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }); let result = unsafe { libc::write( fd, From 5ce35133d9b1bba5d9238d5e805e7db7b6b86a57 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 15 Jan 2025 23:20:57 +0100 Subject: [PATCH 480/672] fully operational C++ async example --- .../async_cpp/src/async_module.cpp | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) 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 index 8aed915c1..88c74dfe7 100644 --- a/crates/cpp/tests/symmetric_async/async_cpp/src/async_module.cpp +++ b/crates/cpp/tests/symmetric_async/async_cpp/src/async_module.cpp @@ -23,9 +23,10 @@ void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { return ret; } -static void fulfil_promise(void* data) { +static symmetric::runtime::symmetric_executor::CallbackState fulfil_promise(void* data) { std::unique_ptr> ptr((std::promise*)data); ptr->set_value(); + return symmetric::runtime::symmetric_executor::CallbackState::kReady; } extern "C" void* testX3AtestX2FwaitX00X5BasyncX5Dsleep(int64_t); @@ -43,6 +44,13 @@ std::future test::test::wait::Sleep(uint64_t nanoseconds) } return result1; } + +static symmetric::runtime::symmetric_executor::CallbackState wait_on_future(std::future* fut) { + fut->get(); + delete fut; + return symmetric::runtime::symmetric_executor::CallbackState::kReady; +} + extern "C" void* X5BasyncX5DtestX3AtestX2Fstring_delayX00forward(uint8_t* arg0, size_t arg1, uint8_t* arg2) { @@ -64,14 +72,15 @@ void* X5BasyncX5DtestX3AtestX2Fstring_delayX00forward(uint8_t* arg0, size_t arg1 } else { symmetric::runtime::symmetric_executor::EventGenerator gen; auto waiting = gen.Subscribe(); - auto keep_alive = std::make_shared>(std::move(std::future())); auto task = std::async(std::launch::async, [store](std::future&& result1, - symmetric::runtime::symmetric_executor::EventGenerator &&gen, - std::shared_ptr> keep_alive){ + symmetric::runtime::symmetric_executor::EventGenerator &&gen){ store(result1.get()); gen.Activate(); - }, std::move(result1), std::move(gen), keep_alive); - std::swap(*keep_alive, task); + }, std::move(result1), std::move(gen)); + auto fut = std::make_unique>(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(); } } From ee6df248b3b2edc854e5f4fe0b0dbee96c7cd314 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Fri, 17 Jan 2025 14:18:20 +0100 Subject: [PATCH 481/672] feat: add support for `stream` with no `` Signed-off-by: Roman Volosatovs --- crates/c/src/lib.rs | 4 +-- crates/core/src/abi.rs | 4 +-- crates/core/src/lib.rs | 4 +-- crates/csharp/src/interface.rs | 2 +- crates/go/src/interface.rs | 4 +-- crates/markdown/src/lib.rs | 17 ++++++---- crates/moonbit/src/lib.rs | 2 +- crates/rust/src/bindgen.rs | 10 ++++-- crates/rust/src/interface.rs | 62 ++++++++++++++++++++++------------ crates/teavm-java/src/lib.rs | 2 +- tests/codegen/streams.wit | 2 ++ 11 files changed, 72 insertions(+), 41 deletions(-) diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index fdcf018c8..5da00ff5c 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -1352,7 +1352,7 @@ void __wasm_export_{ns}_{snake}_dtor({ns}_{snake}_t* arg) {{ todo!() } - fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs) { _ = (id, name, ty, docs); todo!() } @@ -1450,7 +1450,7 @@ impl<'a> wit_bindgen_core::AnonymousTypeGenerator<'a> for InterfaceGenerator<'a> todo!("print_anonymous_type for future"); } - fn anonymous_type_stream(&mut self, _id: TypeId, _ty: &Type, _docs: &Docs) { + fn anonymous_type_stream(&mut self, _id: TypeId, _ty: &Option, _docs: &Docs) { todo!("print_anonymous_type for stream"); } diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 6d02ad788..915af7018 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -364,13 +364,13 @@ def_instruction! { /// Create an `i32` from a stream. StreamLower { - payload: &'a Type, + payload: &'a Option, ty: TypeId, } : [1] => [1], /// Create a stream from an `i32`. StreamLift { - payload: &'a Type, + payload: &'a Option, ty: TypeId, } : [1] => [1], diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index b193df61a..7511dc459 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -155,7 +155,7 @@ pub trait InterfaceGenerator<'a> { fn type_list(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs); fn type_builtin(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs); fn type_future(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs); - fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs); + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs); fn type_error_context(&mut self, id: TypeId, name: &str, docs: &Docs); fn types(&mut self, iface: InterfaceId) { let iface = &self.resolve().interfaces[iface]; @@ -195,7 +195,7 @@ pub trait AnonymousTypeGenerator<'a> { fn anonymous_type_result(&mut self, id: TypeId, ty: &Result_, docs: &Docs); fn anonymous_type_list(&mut self, id: TypeId, ty: &Type, docs: &Docs); fn anonymous_type_future(&mut self, id: TypeId, ty: &Option, docs: &Docs); - fn anonymous_type_stream(&mut self, id: TypeId, ty: &Type, docs: &Docs); + fn anonymous_type_stream(&mut self, id: TypeId, ty: &Option, docs: &Docs); fn anonymous_type_type(&mut self, id: TypeId, ty: &Type, docs: &Docs); fn anonymous_type_error_context(&mut self); diff --git a/crates/csharp/src/interface.rs b/crates/csharp/src/interface.rs index 49e56cc0b..26cb51e02 100644 --- a/crates/csharp/src/interface.rs +++ b/crates/csharp/src/interface.rs @@ -1169,7 +1169,7 @@ impl<'a> CoreInterfaceGenerator<'a> for InterfaceGenerator<'a> { todo!() } - fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs) { _ = (id, name, ty, docs); todo!() } diff --git a/crates/go/src/interface.rs b/crates/go/src/interface.rs index 39c54aef1..8d3224966 100644 --- a/crates/go/src/interface.rs +++ b/crates/go/src/interface.rs @@ -322,7 +322,7 @@ impl InterfaceGenerator<'_> { TypeDefKind::Stream(t) => { let mut src = String::new(); src.push_str("Stream"); - src.push_str(&self.ty_name(t)); + src.push_str(&self.optional_ty_name(t.as_ref())); src.push('T'); src } @@ -1271,7 +1271,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { todo!() } - fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs) { _ = (id, name, ty, docs); todo!() } diff --git a/crates/markdown/src/lib.rs b/crates/markdown/src/lib.rs index a890e8200..43db5b690 100644 --- a/crates/markdown/src/lib.rs +++ b/crates/markdown/src/lib.rs @@ -413,11 +413,16 @@ impl InterfaceGenerator<'_> { self.push_str("future"); } }, - TypeDefKind::Stream(t) => { - self.push_str("stream<"); - self.print_ty(t); - self.push_str(">"); - } + TypeDefKind::Stream(t) => match t { + Some(t) => { + self.push_str("stream<"); + self.print_ty(t); + self.push_str(">"); + } + None => { + self.push_str("stream"); + } + }, TypeDefKind::ErrorContext => { self.push_str("error-context"); } @@ -661,7 +666,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { todo!() } - fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs) { _ = (id, name, ty, docs); todo!() } diff --git a/crates/moonbit/src/lib.rs b/crates/moonbit/src/lib.rs index 72be26664..b17b8dd3c 100644 --- a/crates/moonbit/src/lib.rs +++ b/crates/moonbit/src/lib.rs @@ -1504,7 +1504,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { todo!() } - fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs) { _ = (id, name, ty, docs); todo!() } diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index b200fcacc..5fd5ee12f 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -506,9 +506,13 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::StreamLift { payload, .. } => { let async_support = self.gen.path_to_async_support(); let op = &operands[0]; - let name = self - .gen - .type_name_owned_with_id(payload, Identifier::StreamOrFuturePayload); + let name = payload + .as_ref() + .map(|ty| { + self.gen + .type_name_owned_with_id(ty, Identifier::StreamOrFuturePayload) + }) + .unwrap_or_else(|| "()".into()); let ordinal = self.gen.gen.stream_payloads.get_index_of(&name).unwrap(); let path = self.gen.path_to_root(); results.push(format!( diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 70b0d0de2..28d8a28d0 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -689,26 +689,33 @@ pub mod vtable{ordinal} {{ } } TypeDefKind::Stream(payload_type) => { - let name = self.type_name_owned(payload_type); + let name = if let Some(payload_type) = payload_type { + self.type_name_owned(payload_type) + } else { + "()".into() + }; if !self.gen.stream_payloads.contains_key(&name) { let ordinal = self.gen.stream_payloads.len(); - let size = self.sizes.size(payload_type).size_wasm32(); - let align = self.sizes.align(payload_type).align_wasm32(); + let (size, align) = if let Some(payload_type) = payload_type { + ( + self.sizes.size(payload_type), + self.sizes.align(payload_type), + ) + } else { + ( + ArchitectureSize { + bytes: 0, + pointers: 0, + }, + Alignment::default(), + ) + }; + let size = size.size_wasm32(); + let align = align.align_wasm32(); let alloc = self.path_to_std_alloc_module(); - let (lower_address, lower, lift_address, lift) = - if stream_direct(payload_type) { - let lower_address = - "let address = values.as_ptr() as *mut u8;".into(); - let lift_address = - "let address = values.as_mut_ptr() as *mut u8;".into(); - ( - lower_address, - String::new(), - lift_address, - "let value = ();\n".into(), - ) - } else { + let (lower_address, lower, lift_address, lift) = match payload_type { + Some(payload_type) if !stream_direct(payload_type) => { let address = format!( "let address = unsafe {{ {alloc}::alloc\ ({alloc}::Layout::from_size_align_unchecked\ @@ -744,7 +751,20 @@ for (index, dst) in values.iter_mut().take(count).enumerate() {{ "# ); (address.clone(), lower, address, lift) - }; + } + _ => { + let lower_address = + "let address = values.as_ptr() as *mut u8;".into(); + let lift_address = + "let address = values.as_mut_ptr() as *mut u8;".into(); + ( + lower_address, + String::new(), + lift_address, + "let value = ();\n".into(), + ) + } + }; let box_ = self.path_to_box(); let code = format!( @@ -2853,7 +2873,7 @@ impl<'a> {camel}Borrow<'a>{{ self.push_str(";\n"); } - fn type_stream(&mut self, _id: TypeId, name: &str, ty: &Type, docs: &Docs) { + fn type_stream(&mut self, _id: TypeId, name: &str, ty: &Option, docs: &Docs) { let async_support = self.path_to_async_support(); let mode = TypeMode { style: TypeOwnershipStyle::Owned, @@ -2865,7 +2885,7 @@ impl<'a> {camel}Borrow<'a>{{ self.print_generics(mode.lifetime); self.push_str(" = "); self.push_str(&format!("{async_support}::StreamReader<")); - self.print_ty(ty, mode); + self.print_optional_ty(ty.as_ref(), mode); self.push_str(">"); self.push_str(";\n"); } @@ -2979,7 +2999,7 @@ impl<'a, 'b> wit_bindgen_core::AnonymousTypeGenerator<'a> for AnonTypeGenerator< self.interface.push_str(">"); } - fn anonymous_type_stream(&mut self, _id: TypeId, ty: &Type, _docs: &Docs) { + fn anonymous_type_stream(&mut self, _id: TypeId, ty: &Option, _docs: &Docs) { let async_support = self.interface.path_to_async_support(); let mode = TypeMode { style: TypeOwnershipStyle::Owned, @@ -2988,7 +3008,7 @@ impl<'a, 'b> wit_bindgen_core::AnonymousTypeGenerator<'a> for AnonTypeGenerator< }; self.interface .push_str(&format!("{async_support}::StreamReader<")); - self.interface.print_ty(ty, mode); + self.interface.print_optional_ty(ty.as_ref(), mode); self.interface.push_str(">"); } diff --git a/crates/teavm-java/src/lib.rs b/crates/teavm-java/src/lib.rs index 014bd6ace..53d498fad 100644 --- a/crates/teavm-java/src/lib.rs +++ b/crates/teavm-java/src/lib.rs @@ -1075,7 +1075,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for InterfaceGenerator<'a> { todo!() } - fn type_stream(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs) { + fn type_stream(&mut self, id: TypeId, name: &str, ty: &Option, docs: &Docs) { _ = (id, name, ty, docs); todo!() } diff --git a/tests/codegen/streams.wit b/tests/codegen/streams.wit index 7ed696ed8..96ccfeed2 100644 --- a/tests/codegen/streams.wit +++ b/tests/codegen/streams.wit @@ -15,6 +15,7 @@ interface transmit { } interface streams { + stream-param: func(x: stream); stream-u8-param: func(x: stream); stream-u16-param: func(x: stream); stream-u32-param: func(x: stream); @@ -26,6 +27,7 @@ interface streams { stream-f32-param: func(x: stream); stream-f64-param: func(x: stream); + stream-ret: func() -> stream; stream-u8-ret: func() -> stream; stream-u16-ret: func() -> stream; stream-u32-ret: func() -> stream; From 1705cc2d8112db8863affa049ddb211d988306fb Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Fri, 17 Jan 2025 14:19:14 +0100 Subject: [PATCH 482/672] test: return `future` in future return test WIT Signed-off-by: Roman Volosatovs --- tests/codegen/futures.wit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen/futures.wit b/tests/codegen/futures.wit index 2d634a400..ff24a5098 100644 --- a/tests/codegen/futures.wit +++ b/tests/codegen/futures.wit @@ -13,7 +13,7 @@ interface futures { future-f32-param: func(x: future); future-f64-param: func(x: future); - future-ret: func(x: future); + future-ret: func() -> future; future-u8-ret: func() -> future; future-u16-ret: func() -> future; future-u32-ret: func() -> future; From 484cea338d375c16c15cbd4ef7652a788e6f9d43 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Fri, 17 Jan 2025 14:19:38 +0100 Subject: [PATCH 483/672] build: update wasm-tools Signed-off-by: Roman Volosatovs --- Cargo.lock | 80 +++++++++++++++++++++++++++++++++++++++--------------- Cargo.toml | 16 +++++++---- 2 files changed, 69 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f9f242910..bde3d8a82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1503,7 +1503,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.223.0", + "wasm-encoder 0.223.0 (git+https://github.com/bytecodealliance/wasm-tools)", "wit-bindgen-core", "wit-component", "wit-parser 0.223.0", @@ -1786,14 +1786,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e636076193fa68103e937ac951b5f2f587624097017d764b8984d9c0f149464" dependencies = [ "leb128", - "wasmparser 0.223.0", + "wasmparser 0.223.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-encoder" +version = "0.223.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#ad336345389fe243f723f0f4b8175ff9bf73375b" +dependencies = [ + "leb128", + "wasmparser 0.223.0 (git+https://github.com/bytecodealliance/wasm-tools)", ] [[package]] name = "wasm-metadata" version = "0.223.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c730c3379d3d20e5a0245b0724b924483e853588ca8fba547c1e21f19e7d735" +source = "git+https://github.com/bytecodealliance/wasm-tools#ad336345389fe243f723f0f4b8175ff9bf73375b" dependencies = [ "anyhow", "indexmap", @@ -1802,8 +1810,8 @@ dependencies = [ "serde_json", "spdx", "url", - "wasm-encoder 0.223.0", - "wasmparser 0.223.0", + "wasm-encoder 0.223.0 (git+https://github.com/bytecodealliance/wasm-tools)", + "wasmparser 0.223.0 (git+https://github.com/bytecodealliance/wasm-tools)", ] [[package]] @@ -1825,6 +1833,16 @@ name = "wasmparser" version = "0.223.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5a99faceb1a5a84dd6084ec4bfa4b2ab153b5793b43fd8f58b89232634afc35" +dependencies = [ + "bitflags", + "indexmap", + "semver", +] + +[[package]] +name = "wasmparser" +version = "0.223.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#ad336345389fe243f723f0f4b8175ff9bf73375b" dependencies = [ "bitflags", "hashbrown 0.15.2", @@ -1896,7 +1914,7 @@ dependencies = [ "wasmtime-slab", "wasmtime-versioned-export-macros", "wasmtime-winch", - "wat", + "wat 1.223.0 (registry+https://github.com/rust-lang/crates.io-index)", "windows-sys 0.52.0", ] @@ -2151,7 +2169,19 @@ dependencies = [ "leb128", "memchr", "unicode-width", - "wasm-encoder 0.223.0", + "wasm-encoder 0.223.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wast" +version = "223.0.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#ad336345389fe243f723f0f4b8175ff9bf73375b" +dependencies = [ + "bumpalo", + "leb128", + "memchr", + "unicode-width", + "wasm-encoder 0.223.0 (git+https://github.com/bytecodealliance/wasm-tools)", ] [[package]] @@ -2160,7 +2190,15 @@ version = "1.223.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "662786915c427e4918ff01eabb3c4756d4d947cd8f635761526b4cc9da2eaaad" dependencies = [ - "wast 223.0.0", + "wast 223.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wat" +version = "1.223.0" +source = "git+https://github.com/bytecodealliance/wasm-tools#ad336345389fe243f723f0f4b8175ff9bf73375b" +dependencies = [ + "wast 223.0.0 (git+https://github.com/bytecodealliance/wasm-tools)", ] [[package]] @@ -2379,7 +2417,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.223.0", + "wasm-encoder 0.223.0 (git+https://github.com/bytecodealliance/wasm-tools)", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -2394,8 +2432,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.223.0", - "wasmparser 0.223.0", + "wasm-encoder 0.223.0 (git+https://github.com/bytecodealliance/wasm-tools)", + "wasmparser 0.223.0 (git+https://github.com/bytecodealliance/wasm-tools)", "wasmtime", "wasmtime-wasi", "wit-bindgen-c", @@ -2428,9 +2466,9 @@ dependencies = [ "heck 0.5.0", "indexmap", "test-helpers", - "wasm-encoder 0.223.0", + "wasm-encoder 0.223.0 (git+https://github.com/bytecodealliance/wasm-tools)", "wasm-metadata", - "wasmparser 0.223.0", + "wasmparser 0.223.0 (git+https://github.com/bytecodealliance/wasm-tools)", "wit-bindgen-core", "wit-component", "wit-parser 0.223.0", @@ -2529,8 +2567,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.223.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c10ed2aeee4c8ec5715875f62f4a3de3608d6987165c116810d8c2908aa9d93b" +source = "git+https://github.com/bytecodealliance/wasm-tools#ad336345389fe243f723f0f4b8175ff9bf73375b" dependencies = [ "anyhow", "bitflags", @@ -2539,10 +2576,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.223.0", + "wasm-encoder 0.223.0 (git+https://github.com/bytecodealliance/wasm-tools)", "wasm-metadata", - "wasmparser 0.223.0", - "wat", + "wasmparser 0.223.0 (git+https://github.com/bytecodealliance/wasm-tools)", + "wat 1.223.0 (git+https://github.com/bytecodealliance/wasm-tools)", "wit-parser 0.223.0", ] @@ -2567,8 +2604,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.223.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92772f4dcacb804b275981eea1d920b12b377993b53307f1e33d87404e080281" +source = "git+https://github.com/bytecodealliance/wasm-tools#ad336345389fe243f723f0f4b8175ff9bf73375b" dependencies = [ "anyhow", "id-arena", @@ -2579,7 +2615,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.223.0", + "wasmparser 0.223.0 (git+https://github.com/bytecodealliance/wasm-tools)", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index cd2360df4..ef77b1af2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,11 +33,17 @@ prettyplease = "0.2.20" syn = { version = "2.0.89", features = ["printing"] } futures = "0.3.31" -wasmparser = "0.223.0" -wasm-encoder = "0.223.0" -wasm-metadata = "0.223.0" -wit-parser = "0.223.0" -wit-component = "0.223.0" +# TODO: switch git dependencies to new releases +# wasmparser = "0.224.0" +# wasm-encoder = "0.224.0" +# wasm-metadata = "0.224.0" +# wit-parser = "0.224.0" +# wit-component = "0.224.0" +wasmparser = { 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" } +wit-parser = { git = "https://github.com/bytecodealliance/wasm-tools" } +wit-component = { git = "https://github.com/bytecodealliance/wasm-tools" } wit-bindgen-core = { path = 'crates/core', version = '0.37.0' } wit-bindgen-c = { path = 'crates/c', version = '0.37.0' } From adbde38fbe9a53193409bf482528dc6e01746bfe Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 21 Jan 2025 22:14:55 +0100 Subject: [PATCH 484/672] dependency update (needs more) --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 87dfc98e0..f8f04dca2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1801,7 +1801,7 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#759d5fb921723503277b00e2cbcf903b42cbfaaa" dependencies = [ "leb128", "wasmparser 0.223.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", @@ -1810,7 +1810,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#759d5fb921723503277b00e2cbcf903b42cbfaaa" dependencies = [ "anyhow", "indexmap", @@ -1851,7 +1851,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#759d5fb921723503277b00e2cbcf903b42cbfaaa" dependencies = [ "bitflags", "hashbrown 0.15.2", @@ -2184,7 +2184,7 @@ dependencies = [ [[package]] name = "wast" version = "223.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#759d5fb921723503277b00e2cbcf903b42cbfaaa" dependencies = [ "bumpalo", "leb128", @@ -2205,7 +2205,7 @@ dependencies = [ [[package]] name = "wat" version = "1.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#759d5fb921723503277b00e2cbcf903b42cbfaaa" dependencies = [ "wast 223.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] @@ -2604,7 +2604,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#759d5fb921723503277b00e2cbcf903b42cbfaaa" dependencies = [ "anyhow", "bitflags", @@ -2641,7 +2641,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b98d3e1fe2c5c17767e8850ca6f4f0d99ffa6472" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#759d5fb921723503277b00e2cbcf903b42cbfaaa" dependencies = [ "anyhow", "id-arena", From 3398ecf72648cf048ffef692c706c71d3b00e44d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 21 Jan 2025 22:22:57 +0100 Subject: [PATCH 485/672] fix compilation after merge --- crates/cpp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index df8593610..afafbb820 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2520,7 +2520,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> todo!() } - fn type_stream(&mut self, _id: TypeId, _name: &str, _ty: &Type, _docs: &Docs) { + fn type_stream(&mut self, _id: TypeId, _name: &str, _ty: &Option, _docs: &Docs) { todo!() } From fd745c0448b658a7aef2e3f7600c44399fd2a7cc Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 23 Jan 2025 00:11:00 +0100 Subject: [PATCH 486/672] initial future prototype --- crates/cpp/tests/symmetric_future/Cargo.toml | 3 + .../tests/symmetric_future/future/Cargo.toml | 17 + .../tests/symmetric_future/future/build.rs | 9 + .../future/src/future_world.rs | 397 ++++++++++++++++++ .../tests/symmetric_future/future/src/lib.rs | 18 + crates/cpp/tests/symmetric_future/generate.sh | 2 + .../tests/symmetric_future/main/Cargo.toml | 10 + .../cpp/tests/symmetric_future/main/build.rs | 9 + .../tests/symmetric_future/main/src/main.rs | 3 + .../tests/symmetric_future/source/Cargo.toml | 12 + .../tests/symmetric_future/source/build.rs | 9 + .../tests/symmetric_future/source/src/lib.rs | 14 + .../cpp/tests/symmetric_future/wit/future.wit | 14 + .../tests/symmetric_stream/main/Cargo.toml | 6 +- .../tests/symmetric_stream/stream/src/lib.rs | 2 +- crates/symmetric_executor/src/lib.rs | 22 +- 16 files changed, 532 insertions(+), 15 deletions(-) create mode 100644 crates/cpp/tests/symmetric_future/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_future/future/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_future/future/build.rs create mode 100644 crates/cpp/tests/symmetric_future/future/src/future_world.rs create mode 100644 crates/cpp/tests/symmetric_future/future/src/lib.rs create mode 100755 crates/cpp/tests/symmetric_future/generate.sh create mode 100644 crates/cpp/tests/symmetric_future/main/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_future/main/build.rs create mode 100644 crates/cpp/tests/symmetric_future/main/src/main.rs create mode 100644 crates/cpp/tests/symmetric_future/source/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_future/source/build.rs create mode 100644 crates/cpp/tests/symmetric_future/source/src/lib.rs create mode 100644 crates/cpp/tests/symmetric_future/wit/future.wit 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..5eb71a70e --- /dev/null +++ b/crates/cpp/tests/symmetric_future/future/src/future_world.rs @@ -0,0 +1,397 @@ +// Generated by `wit-bindgen` 0.38.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 async fn create() -> wit_bindgen_symmetric_rt::async_support::FutureReader{ + unsafe { + // let layout0 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); + // let ptr0 = _rt::alloc::alloc(layout0); + let layout1 = _rt::alloc::Layout::from_size_align_unchecked(core::mem::size_of::<*const u8>(), core::mem::size_of::<*const u8>()); + let ptr1 = _rt::alloc::alloc(layout1); + + #[link(wasm_import_module = "test:test/future-source")] + #[link(name = "source")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "[async]create")] + fn testX3AtestX2Ffuture_sourceX00X5BasyncX5Dcreate(_: *mut u8) -> *mut u8; + } + // let layout2 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); + wit_bindgen_symmetric_rt::async_support::await_result(move || unsafe { testX3AtestX2Ffuture_sourceX00X5BasyncX5Dcreate(ptr1)}).await; + let l3 = *ptr1.add(0).cast::<*mut u8>(); + let result4 = wit_bindgen_symmetric_rt::async_support::FutureReader::new(l3 as usize); + _rt::cabi_dealloc(ptr1, core::mem::size_of::<*const u8>(), core::mem::size_of::<*const u8>()); + result4 + } + } + + } + + } +} +#[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(results: *mut u8) -> *mut u8 {#[cfg(target_arch="wasm32")] + _rt::run_ctors_once();let result = async move { + let result = T::create().await; + result + }; + let result = wit_bindgen_symmetric_rt::async_support::first_poll(result, move |result0| { + let outptr = results.cast::<*mut ()>(); + *(unsafe { &mut *outptr }) = result0.take_handle().cast(); + }); + result.cast() + } + // #[doc(hidden)] + // #[allow(non_snake_case)] + // pub unsafe fn __callback_create(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + // wit_bindgen_symmetric_rt::async_support::callback(ctx, event0, event1, event2) + // } + pub trait Guest { + async fn create() -> wit_bindgen_symmetric_rt::async_support::FutureReader; + } + #[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(results: *mut u8) -> *mut u8 { + $($path_to_types)*::_export_create_cabi::<$ty>(results) + } + // #[unsafe(export_name = "[callback]create")] + // unsafe extern "C" fn _callback_create(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + // $($path_to_types)*::__callback_create(ctx, event0, event1, event2) + // } + };); + } + #[doc(hidden)] + pub(crate) use __export_test_test_future_test_cabi; + + } + + } +} +} +mod _rt { + #![allow(dead_code, clippy::all)] + + pub fn as_i32(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; + pub use alloc_crate::alloc; + 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); + } + + #[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 { + // fn new() -> u32; + } + // #[doc(hidden)] + // pub mod vtable0 { + // fn write(future: u32, value: u32) -> ::core::pin::Pin>> { + // super::super::_rt::Box::pin(async move { + // #[repr(align(4))] + // struct Buffer([::core::mem::MaybeUninit::; 4]); + // let mut buffer = Buffer([::core::mem::MaybeUninit::uninit(); 4]); + // let address = buffer.0.as_mut_ptr() as *mut u8; + // unsafe { *address.add(0).cast::() = super::super::_rt::as_i32(&value); + // } + // #[link(wasm_import_module = "[import-payload]test:test/future-source")] + // extern "C" { + // #[link_name = "[async][future-write-0]create"] + // fn wit_import(_: u32, _: *mut u8) -> u32; + // } + + // unsafe { wit_bindgen_symmetric_rt::async_support::await_future_result(wit_import, future, address).await } + // }) + // } + + // fn read(future: u32) -> ::core::pin::Pin>>> { + // super::super::_rt::Box::pin(async move { + // struct Buffer([::core::mem::MaybeUninit::; 4]); + // let mut buffer = Buffer([::core::mem::MaybeUninit::uninit(); 4]); + // let address = buffer.0.as_mut_ptr() as *mut u8; + + // #[cfg(not(target_arch = "wasm32"))] + // unsafe extern "C" fn wit_import(_: u32, _: *mut u8) -> u32 { + // unreachable!() + // } + + // #[cfg(target_arch = "wasm32")] + // #[link(wasm_import_module = "[import-payload]test:test/future-source")] + // extern "C" { + // #[link_name = "[async][future-read-0]create"] + // fn wit_import(_: u32, _: *mut u8) -> u32; + // } + + // if unsafe { wit_bindgen_symmetric_rt::async_support::await_future_result(wit_import, future, address).await } { + // let value = unsafe { let l0 = *address.add(0).cast::(); + + // l0 as u32 }; + // Some(value) + // } else { + // None + // } + // }) + // } + + // fn cancel_write(writer: u32) { + // #[cfg(not(target_arch = "wasm32"))] + // { + // unreachable!(); + // } + + // #[cfg(target_arch = "wasm32")] + // { + // #[link(wasm_import_module = "[import-payload]test:test/future-source")] + // extern "C" { + // #[link_name = "[future-cancel-write-0]create"] + // fn cancel(_: u32) -> u32; + // } + // unsafe { cancel(writer) }; + // } + // } + + // fn cancel_read(reader: u32) { + // #[cfg(not(target_arch = "wasm32"))] + // { + // unreachable!(); + // } + + // #[cfg(target_arch = "wasm32")] + // { + // #[link(wasm_import_module = "[import-payload]test:test/future-source")] + // extern "C" { + // #[link_name = "[future-cancel-read-0]create"] + // fn cancel(_: u32) -> u32; + // } + // unsafe { cancel(reader) }; + // } + // } + + // fn close_writable(writer: u32) { + // #[cfg(not(target_arch = "wasm32"))] + // { + // unreachable!(); + // } + + // #[cfg(target_arch = "wasm32")] + // { + // #[link(wasm_import_module = "[import-payload]test:test/future-source")] + // extern "C" { + // #[link_name = "[future-close-writable-0]create"] + // fn drop(_: u32, _: u32); + // } + // unsafe { drop(writer, 0) } + // } + // } + + // fn close_readable(reader: u32) { + // #[cfg(not(target_arch = "wasm32"))] + // { + // unreachable!(); + // } + + // #[cfg(target_arch = "wasm32")] + // { + // #[link(wasm_import_module = "[import-payload]test:test/future-source")] + // extern "C" { + // #[link_name = "[future-close-readable-0]create"] + // fn drop(_: u32); + // } + // unsafe { drop(reader) } + // } + // } + + // pub static VTABLE: wit_bindgen_symmetric_rt::async_support::FutureVtable = wit_bindgen_symmetric_rt::async_support::FutureVtable:: { + // write, read, cancel_write, cancel_read, close_writable, close_readable + // }; + + impl super::FuturePayload for u32 { + // fn new() -> (u32, &'static wit_bindgen_symmetric_rt::async_support::FutureVtable) { + // #[cfg(not(target_arch = "wasm32"))] + // { + // unreachable!(); + // } + + // #[cfg(target_arch = "wasm32")] + // { + // #[link(wasm_import_module = "[import-payload]test:test/future-source")] + // extern "C" { + // #[link_name = "[future-new-0]create"] + // fn new() -> u32; + // } + // (unsafe { new() }, &VTABLE) + // } + // } + } +} +/// Creates a new Component Model `future` with the specified payload type. +pub fn new() -> (wit_bindgen_symmetric_rt::async_support::FutureWriter, wit_bindgen_symmetric_rt::async_support::FutureReader) { + new_future() + // let (handle, vtable) = T::new(); + // wit_bindgen_symmetric_rt::async_support::with_entry(handle, |entry| match entry { + // ::std::collections::hash_map::Entry::Vacant(entry) => { + // entry.insert(wit_bindgen_symmetric_rt::async_support::Handle::LocalOpen); + // } + // ::std::collections::hash_map::Entry::Occupied(_) => unreachable!(), + // }); + // ( + // wit_bindgen_symmetric_rt::async_support::FutureWriter::new(handle, vtable), + // wit_bindgen_symmetric_rt::async_support::FutureReader::new(handle, vtable), + // ) +} +// } + +/// 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.38.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\x01g\x01y\x01@\0\0\0\x04\0\x06create\x01\x01\x03\0\x17test:test/f\ +uture-source\x05\0\x01B\x03\x01g\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.223.0\x10wit-bindgen-rust\x060.38.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..3abe91a6b --- /dev/null +++ b/crates/cpp/tests/symmetric_future/future/src/lib.rs @@ -0,0 +1,18 @@ +mod future_world; + +use future_world::test::test::future_source::create; + +future_world::export!(MyStruct with_types_in future_world); + +struct MyStruct; + +impl future_world::exports::test::test::future_source::Guest for MyStruct { + async fn create() -> async_support::FutureReader { + let (mut write, read) = async_support::future_support::new_future(); + async_support::spawn(async move { + let input = create().await; + write.assign(input * 2); + }); + 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..db380eb5a --- /dev/null +++ b/crates/cpp/tests/symmetric_future/generate.sh @@ -0,0 +1,2 @@ +#!/bin/sh +(cd future/src ; ../../../../../../target/debug/wit-bindgen rust ../../wit/future.wit --async all --symmetric) 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..e7a11a969 --- /dev/null +++ b/crates/cpp/tests/symmetric_future/main/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} 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..b93cf3ffd --- /dev/null +++ b/crates/cpp/tests/symmetric_future/source/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: u64, right: u64) -> u64 { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} 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; +} + +interface future-test { + create: func() -> future; +} + +world future-world { + import future-source; + export future-test; +} diff --git a/crates/cpp/tests/symmetric_stream/main/Cargo.toml b/crates/cpp/tests/symmetric_stream/main/Cargo.toml index ea7feeeea..0bdf33fa3 100644 --- a/crates/cpp/tests/symmetric_stream/main/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/main/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -stream = { version = "0.1.0", path = "../stream" } -symmetric_executor = { version = "0.1.0", path = "../../../../symmetric_executor", features = ["trace"]} +stream = { path = "../stream" } +symmetric_executor = { path = "../../../../symmetric_executor", features = ["trace"]} symmetric_stream = { path = "../../../../symmetric_executor/symmetric_stream" } -wit-bindgen-symmetric-rt = { version = "0.36.0", path = "../../../../symmetric_executor/rust-client" } +wit-bindgen-symmetric-rt = { path = "../../../../symmetric_executor/rust-client" } diff --git a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs index b5335abfa..d7333ca0e 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs @@ -21,6 +21,6 @@ impl stream_world::exports::test::test::stream_test::Guest for MyStruct { } } }); - return reader; + reader } } diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 5a2bcbf5f..40eacef0e 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -141,7 +141,7 @@ impl symmetric_executor::Guest for Guest { let mut maxfd = change_event + 1; let now = SystemTime::now(); let mut rfds = core::mem::MaybeUninit::::uninit(); - let rfd_ptr = unsafe { core::ptr::from_mut(rfds.assume_init_mut()) }; + let rfd_ptr = rfds.as_mut_ptr(); unsafe { libc::FD_ZERO(rfd_ptr) }; unsafe { libc::FD_SET(change_event, rfd_ptr); @@ -209,16 +209,16 @@ impl symmetric_executor::Guest for Guest { break; } } - if tvptr.is_null() && maxfd == 0 { - // probably only active tasks, all returned pending, try again - if DEBUGGING { - println!( - "Relooping with {} tasks", - EXECUTOR.lock().unwrap().active_tasks.len() - ); - } - continue; - } + // if tvptr.is_null() && maxfd == 0 { + // // 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 { From 985ebf9fb814f2a762b0e1c9cdd5fe958a61bb35 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 23 Jan 2025 00:16:26 +0100 Subject: [PATCH 487/672] small correction --- crates/cpp/tests/symmetric_future/future/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cpp/tests/symmetric_future/future/src/lib.rs b/crates/cpp/tests/symmetric_future/future/src/lib.rs index 3abe91a6b..68070ba6d 100644 --- a/crates/cpp/tests/symmetric_future/future/src/lib.rs +++ b/crates/cpp/tests/symmetric_future/future/src/lib.rs @@ -10,8 +10,8 @@ impl future_world::exports::test::test::future_source::Guest for MyStruct { async fn create() -> async_support::FutureReader { let (mut write, read) = async_support::future_support::new_future(); async_support::spawn(async move { - let input = create().await; - write.assign(input * 2); + let input = create().await.await; + write.write(input * 2); }); read } From a8555cb66237c286ccf847b7993387c9b13cb17f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 31 Jan 2025 21:50:25 +0100 Subject: [PATCH 488/672] update wasm-tools --- Cargo.lock | 113 +++++++++++++++++++++++++++-------------------------- 1 file changed, 58 insertions(+), 55 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 541964c9c..ecc4cd58c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -107,9 +107,6 @@ name = "anyhow" version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" -dependencies = [ - "backtrace", -] [[package]] name = "arbitrary" @@ -1002,6 +999,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" version = "0.2.159" @@ -1509,10 +1512,10 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.223.0", + "wasm-encoder 0.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-core", "wit-component", - "wit-parser 0.223.0", + "wit-parser 0.224.0", ] [[package]] @@ -1790,27 +1793,27 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#759d5fb921723503277b00e2cbcf903b42cbfaaa" +version = "0.224.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7249cf8cb0c6b9cb42bce90c0a5feb276fbf963fa385ff3d818ab3d90818ed6" dependencies = [ "leb128", - "wasmparser 0.223.0", + "wasmparser 0.224.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-encoder" version = "0.224.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7249cf8cb0c6b9cb42bce90c0a5feb276fbf963fa385ff3d818ab3d90818ed6" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#945be3cd638c2f90fee715dd69a7c802f1b65fbc" dependencies = [ - "leb128", - "wasmparser 0.224.0", + "leb128fmt", + "wasmparser 0.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] name = "wasm-metadata" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#759d5fb921723503277b00e2cbcf903b42cbfaaa" +version = "0.224.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#945be3cd638c2f90fee715dd69a7c802f1b65fbc" dependencies = [ "anyhow", "indexmap", @@ -1819,8 +1822,8 @@ dependencies = [ "serde_json", "spdx", "url", - "wasm-encoder 0.223.0", - "wasmparser 0.223.0", + "wasm-encoder 0.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -1839,25 +1842,25 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#759d5fb921723503277b00e2cbcf903b42cbfaaa" +version = "0.224.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65881a664fdd43646b647bb27bf186ab09c05bf56779d40aed4c6dce47d423f5" dependencies = [ "bitflags", - "hashbrown 0.15.2", "indexmap", "semver", - "serde", ] [[package]] name = "wasmparser" version = "0.224.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65881a664fdd43646b647bb27bf186ab09c05bf56779d40aed4c6dce47d423f5" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#945be3cd638c2f90fee715dd69a7c802f1b65fbc" dependencies = [ "bitflags", + "hashbrown 0.15.2", "indexmap", "semver", + "serde", ] [[package]] @@ -1923,7 +1926,7 @@ dependencies = [ "wasmtime-slab", "wasmtime-versioned-export-macros", "wasmtime-winch", - "wat 1.224.0", + "wat 1.224.0 (registry+https://github.com/rust-lang/crates.io-index)", "windows-sys 0.52.0", ] @@ -2170,44 +2173,44 @@ dependencies = [ [[package]] name = "wast" -version = "223.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#759d5fb921723503277b00e2cbcf903b42cbfaaa" +version = "224.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d722a51e62b669d17e5a9f6bc8ec210178b37d869114355aa46989686c5c6391" dependencies = [ "bumpalo", "leb128", "memchr", "unicode-width", - "wasm-encoder 0.223.0", + "wasm-encoder 0.224.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wast" version = "224.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d722a51e62b669d17e5a9f6bc8ec210178b37d869114355aa46989686c5c6391" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#945be3cd638c2f90fee715dd69a7c802f1b65fbc" dependencies = [ "bumpalo", - "leb128", + "leb128fmt", "memchr", "unicode-width", - "wasm-encoder 0.224.0", + "wasm-encoder 0.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] name = "wat" -version = "1.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#759d5fb921723503277b00e2cbcf903b42cbfaaa" +version = "1.224.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71dece6a7dd5bcbcf8d256606c7fb3faa36286d46bf3f98185407719a5ceede2" dependencies = [ - "wast 223.0.0", + "wast 224.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wat" version = "1.224.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71dece6a7dd5bcbcf8d256606c7fb3faa36286d46bf3f98185407719a5ceede2" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#945be3cd638c2f90fee715dd69a7c802f1b65fbc" dependencies = [ - "wast 224.0.0", + "wast 224.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2437,11 +2440,11 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.223.0", + "wasm-encoder 0.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wit-bindgen-core", "wit-component", - "wit-parser 0.223.0", + "wit-parser 0.224.0", ] [[package]] @@ -2452,8 +2455,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.223.0", - "wasmparser 0.223.0", + "wasm-encoder 0.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasmtime", "wasmtime-wasi", "wit-bindgen-bridge", @@ -2467,7 +2470,7 @@ dependencies = [ "wit-bindgen-rust", "wit-bindgen-teavm-java", "wit-component", - "wit-parser 0.223.0", + "wit-parser 0.224.0", ] [[package]] @@ -2476,7 +2479,7 @@ version = "0.38.0" dependencies = [ "anyhow", "heck 0.5.0", - "wit-parser 0.223.0", + "wit-parser 0.224.0", ] [[package]] @@ -2487,7 +2490,7 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.223.0", + "wasm-encoder 0.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", "wit-bindgen-c", "wit-bindgen-core", @@ -2503,12 +2506,12 @@ dependencies = [ "heck 0.5.0", "indexmap", "test-helpers", - "wasm-encoder 0.223.0", + "wasm-encoder 0.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", - "wasmparser 0.223.0", + "wasmparser 0.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-core", "wit-component", - "wit-parser 0.223.0", + "wit-parser 0.224.0", ] [[package]] @@ -2603,8 +2606,8 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#759d5fb921723503277b00e2cbcf903b42cbfaaa" +version = "0.224.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#945be3cd638c2f90fee715dd69a7c802f1b65fbc" dependencies = [ "anyhow", "bitflags", @@ -2613,11 +2616,11 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.223.0", + "wasm-encoder 0.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata", - "wasmparser 0.223.0", - "wat 1.223.0", - "wit-parser 0.223.0", + "wasmparser 0.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wat 1.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wit-parser 0.224.0", ] [[package]] @@ -2640,8 +2643,8 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.223.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#759d5fb921723503277b00e2cbcf903b42cbfaaa" +version = "0.224.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#945be3cd638c2f90fee715dd69a7c802f1b65fbc" dependencies = [ "anyhow", "id-arena", @@ -2652,7 +2655,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.223.0", + "wasmparser 0.224.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] From 38cb692568bcfff594eed6cc7cff3bb9579b67b9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 31 Jan 2025 23:07:01 +0100 Subject: [PATCH 489/672] initial future impl, still Send trouble --- .../future/src/future_world.rs | 11 ++- .../tests/symmetric_future/future/src/lib.rs | 5 +- .../rust-client/src/async_support.rs | 5 +- .../src/async_support/future_support.rs | 85 +++++++++++++++++++ .../src/async_support/stream_support.rs | 6 +- 5 files changed, 101 insertions(+), 11 deletions(-) diff --git a/crates/cpp/tests/symmetric_future/future/src/future_world.rs b/crates/cpp/tests/symmetric_future/future/src/future_world.rs index 5eb71a70e..be622046d 100644 --- a/crates/cpp/tests/symmetric_future/future/src/future_world.rs +++ b/crates/cpp/tests/symmetric_future/future/src/future_world.rs @@ -11,7 +11,9 @@ pub mod test { static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; - use super::super::super::_rt; + use wit_bindgen_symmetric_rt::async_support::Stream; + + use super::super::super::_rt; #[allow(unused_unsafe, clippy::all)] pub async fn create() -> wit_bindgen_symmetric_rt::async_support::FutureReader{ unsafe { @@ -29,7 +31,7 @@ pub mod test { // let layout2 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); wit_bindgen_symmetric_rt::async_support::await_result(move || unsafe { testX3AtestX2Ffuture_sourceX00X5BasyncX5Dcreate(ptr1)}).await; let l3 = *ptr1.add(0).cast::<*mut u8>(); - let result4 = wit_bindgen_symmetric_rt::async_support::FutureReader::new(l3 as usize); + let result4 = wit_bindgen_symmetric_rt::async_support::FutureReader::new(Stream::from_handle(l3 as usize)); _rt::cabi_dealloc(ptr1, core::mem::size_of::<*const u8>(), core::mem::size_of::<*const u8>()); result4 } @@ -313,7 +315,7 @@ pub mod wit_future { // write, read, cancel_write, cancel_read, close_writable, close_readable // }; - impl super::FuturePayload for u32 { + impl FuturePayload for u32 { // fn new() -> (u32, &'static wit_bindgen_symmetric_rt::async_support::FutureVtable) { // #[cfg(not(target_arch = "wasm32"))] // { @@ -333,7 +335,7 @@ pub mod wit_future { } } /// Creates a new Component Model `future` with the specified payload type. -pub fn new() -> (wit_bindgen_symmetric_rt::async_support::FutureWriter, wit_bindgen_symmetric_rt::async_support::FutureReader) { +pub fn new() -> (wit_bindgen_symmetric_rt::async_support::FutureWriter, wit_bindgen_symmetric_rt::async_support::FutureReader) { new_future() // let (handle, vtable) = T::new(); // wit_bindgen_symmetric_rt::async_support::with_entry(handle, |entry| match entry { @@ -376,6 +378,7 @@ macro_rules! __export_future_world_impl { } #[doc(inline)] pub(crate) use __export_future_world_impl as export; +use wit_bindgen_symmetric_rt::async_support::future_support::new_future; #[cfg(target_arch = "wasm32")] #[unsafe(link_section = "component-type:wit-bindgen:0.38.0:test:test:future-world:encoded world")] diff --git a/crates/cpp/tests/symmetric_future/future/src/lib.rs b/crates/cpp/tests/symmetric_future/future/src/lib.rs index 68070ba6d..024ea000d 100644 --- a/crates/cpp/tests/symmetric_future/future/src/lib.rs +++ b/crates/cpp/tests/symmetric_future/future/src/lib.rs @@ -1,16 +1,17 @@ mod future_world; use future_world::test::test::future_source::create; +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_source::Guest for MyStruct { +impl future_world::exports::test::test::future_test::Guest for MyStruct { async fn create() -> async_support::FutureReader { let (mut write, read) = async_support::future_support::new_future(); async_support::spawn(async move { - let input = create().await.await; + let input = create().await.await.unwrap(); write.write(input * 2); }); read diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 563df635a..5f1f77cfb 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -9,9 +9,10 @@ use crate::module::symmetric::runtime::symmetric_executor::{ self, CallbackState, EventGenerator, EventSubscription, }; -pub use stream_support::{results, Stream, StreamHandle2, StreamReader, StreamWriter}; +pub use future_support::{FutureReader, FutureWriter}; +pub use stream_support::{results, Stream, StreamReader, StreamWriter}; -mod future_support; +pub mod future_support; // later make it non-pub pub mod stream_support; 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 index 8b1378917..4a821c6dc 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/future_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs @@ -1 +1,86 @@ +use std::{ + future::{Future, IntoFuture}, + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, +}; +use super::Stream; + +//use super::Future; + +pub struct FutureWriter { + handle: Stream, + future: Option + 'static + Send>>>, + _phantom: PhantomData, +} + +impl FutureWriter { + pub fn new(handle: Stream) -> Self { + Self { + handle, + future: None, + _phantom: PhantomData, + } + } + + pub fn write(self, _v: T) { + todo!() + } +} + +/// Represents a read operation which may be canceled prior to completion. +pub struct CancelableRead { + reader: Option>, + future: Pin> + 'static + Send>>, +} + +pub struct FutureReader { + handle: Stream, + // future: Option>> + 'static + Send>>>, + _phantom: PhantomData, +} + +impl FutureReader { + pub fn new(handle: Stream) -> Self { + Self { + handle, + // future: None, + _phantom: PhantomData, + } + } + + pub fn read(self) -> CancelableRead { + todo!() + } + + pub fn take_handle(&self) -> *mut () { + todo!() + } +} + +impl Future for CancelableRead { + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + todo!() + } +} + +impl IntoFuture for FutureReader { + type Output = Option; + type IntoFuture = CancelableRead; + + /// 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 { + todo!() + } +} + +pub fn new_future() -> (FutureWriter, FutureReader) { + 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 index 5a53538bc..a79e20a55 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -28,7 +28,7 @@ pub mod results { } pub struct StreamWriter { - handle: StreamHandle2, + handle: Stream, future: Option + 'static + Send>>>, _phantom: PhantomData, } @@ -125,7 +125,7 @@ impl Drop for StreamWriter { /// Represents the readable end of a Component Model `stream`. pub struct StreamReader { - handle: StreamHandle2, + handle: Stream, future: Option>> + 'static + Send>>>, _phantom: PhantomData, } @@ -210,7 +210,7 @@ impl Drop for StreamReader { } } -pub type StreamHandle2 = Stream; +// pub type StreamHandle2 = Stream; pub fn new_stream() -> (StreamWriter, StreamReader) { let handle = Stream::new(); From 0511f6a3044f6790a1a2e8b75ec67a6be5de6623 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 1 Feb 2025 21:47:50 +0100 Subject: [PATCH 490/672] this solves the Send problem --- crates/cpp/tests/symmetric_future/Cargo.lock | 226 ++++++++++++++++++ .../tests/symmetric_future/future/src/lib.rs | 5 +- 2 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 crates/cpp/tests/symmetric_future/Cargo.lock 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/future/src/lib.rs b/crates/cpp/tests/symmetric_future/future/src/lib.rs index 024ea000d..70a33ca58 100644 --- a/crates/cpp/tests/symmetric_future/future/src/lib.rs +++ b/crates/cpp/tests/symmetric_future/future/src/lib.rs @@ -9,9 +9,10 @@ struct MyStruct; impl future_world::exports::test::test::future_test::Guest for MyStruct { async fn create() -> async_support::FutureReader { - let (mut write, read) = async_support::future_support::new_future(); + let (write, read) = async_support::future_support::new_future(); + let input = create().await; async_support::spawn(async move { - let input = create().await.await.unwrap(); + let input = input.await.unwrap(); write.write(input * 2); }); read From cb6ba7ed861131ff6749c28289b5d5625244761a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 1 Feb 2025 22:20:10 +0100 Subject: [PATCH 491/672] clean up stream example, initial future impl --- .../future/src/future_world.rs | 609 +++++++++--------- .../tests/symmetric_future/main/src/main.rs | 54 +- .../tests/symmetric_future/source/src/lib.rs | 38 +- .../tests/symmetric_stream/main/src/main.rs | 20 +- .../tests/symmetric_stream/source/src/lib.rs | 4 +- 5 files changed, 395 insertions(+), 330 deletions(-) diff --git a/crates/cpp/tests/symmetric_future/future/src/future_world.rs b/crates/cpp/tests/symmetric_future/future/src/future_world.rs index be622046d..2fdc04477 100644 --- a/crates/cpp/tests/symmetric_future/future/src/future_world.rs +++ b/crates/cpp/tests/symmetric_future/future/src/future_world.rs @@ -2,87 +2,102 @@ // 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 wit_bindgen_symmetric_rt::async_support::Stream; - - use super::super::super::_rt; - #[allow(unused_unsafe, clippy::all)] - pub async fn create() -> wit_bindgen_symmetric_rt::async_support::FutureReader{ - unsafe { - // let layout0 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); - // let ptr0 = _rt::alloc::alloc(layout0); - let layout1 = _rt::alloc::Layout::from_size_align_unchecked(core::mem::size_of::<*const u8>(), core::mem::size_of::<*const u8>()); - let ptr1 = _rt::alloc::alloc(layout1); - - #[link(wasm_import_module = "test:test/future-source")] - #[link(name = "source")] - extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "[async]create")] - fn testX3AtestX2Ffuture_sourceX00X5BasyncX5Dcreate(_: *mut u8) -> *mut u8; - } - // let layout2 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); - wit_bindgen_symmetric_rt::async_support::await_result(move || unsafe { testX3AtestX2Ffuture_sourceX00X5BasyncX5Dcreate(ptr1)}).await; - let l3 = *ptr1.add(0).cast::<*mut u8>(); - let result4 = wit_bindgen_symmetric_rt::async_support::FutureReader::new(Stream::from_handle(l3 as usize)); - _rt::cabi_dealloc(ptr1, core::mem::size_of::<*const u8>(), core::mem::size_of::<*const u8>()); - result4 - } - } + 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 wit_bindgen_symmetric_rt::async_support::Stream; + + use super::super::super::_rt; + #[allow(unused_unsafe, clippy::all)] + pub async fn create() -> wit_bindgen_symmetric_rt::async_support::FutureReader { + unsafe { + // let layout0 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); + // let ptr0 = _rt::alloc::alloc(layout0); + let layout1 = _rt::alloc::Layout::from_size_align_unchecked( + core::mem::size_of::<*const u8>(), + core::mem::size_of::<*const u8>(), + ); + let ptr1 = _rt::alloc::alloc(layout1); + + #[link(wasm_import_module = "test:test/future-source")] + #[link(name = "source")] + extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "[async]create")] + fn testX3AtestX2Ffuture_sourceX00X5BasyncX5Dcreate(_: *mut u8) -> *mut u8; + } + // let layout2 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); + wit_bindgen_symmetric_rt::async_support::await_result(move || unsafe { + testX3AtestX2Ffuture_sourceX00X5BasyncX5Dcreate(ptr1) + }) + .await; + let l3 = *ptr1.add(0).cast::<*mut u8>(); + let result4 = wit_bindgen_symmetric_rt::async_support::FutureReader::new( + Stream::from_handle(l3 as usize), + ); + _rt::cabi_dealloc( + ptr1, + core::mem::size_of::<*const u8>(), + core::mem::size_of::<*const u8>(), + ); + result4 + } + } + } } - - } } #[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(results: *mut u8) -> *mut u8 {#[cfg(target_arch="wasm32")] - _rt::run_ctors_once();let result = async move { - let result = T::create().await; - result - }; - let result = wit_bindgen_symmetric_rt::async_support::first_poll(result, move |result0| { - let outptr = results.cast::<*mut ()>(); + 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(results: *mut u8) -> *mut u8 { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + let result = async move { + let result = T::create().await; + result + }; + let result = wit_bindgen_symmetric_rt::async_support::first_poll( + result, + move |result0| { + let outptr = results.cast::<*mut ()>(); *(unsafe { &mut *outptr }) = result0.take_handle().cast(); - }); - result.cast() - } - // #[doc(hidden)] - // #[allow(non_snake_case)] - // pub unsafe fn __callback_create(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { - // wit_bindgen_symmetric_rt::async_support::callback(ctx, event0, event1, event2) - // } - pub trait Guest { - async fn create() -> wit_bindgen_symmetric_rt::async_support::FutureReader; - } - #[doc(hidden)] - - macro_rules! __export_test_test_future_test_cabi{ + }, + ); + result.cast() + } + // #[doc(hidden)] + // #[allow(non_snake_case)] + // pub unsafe fn __callback_create(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { + // wit_bindgen_symmetric_rt::async_support::callback(ctx, event0, event1, event2) + // } + pub trait Guest { + async fn create() -> wit_bindgen_symmetric_rt::async_support::FutureReader; + } + #[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(results: *mut u8) -> *mut u8 { + unsafe extern "C" fn testX3AtestX2Ffuture_testX00X5BasyncX5Dcreate(results: *mut u8) -> *mut u8 { $($path_to_types)*::_export_create_cabi::<$ty>(results) } // #[unsafe(export_name = "[callback]create")] @@ -91,232 +106,157 @@ pub mod exports { // } };); } - #[doc(hidden)] - pub(crate) use __export_test_test_future_test_cabi; - + #[doc(hidden)] + pub(crate) use __export_test_test_future_test_cabi; + } + } } - - } -} } mod _rt { - #![allow(dead_code, clippy::all)] + #![allow(dead_code, clippy::all)] + + pub fn as_i32(t: T) -> i32 { + t.as_i32() + } - pub fn as_i32(t: T) -> i32 { - t.as_i32() - } + pub trait AsI32 { + fn as_i32(self) -> 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<'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 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 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 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 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 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 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 char { - #[inline] - fn as_i32(self) -> i32 { - self as i32 + + impl AsI32 for usize { + #[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::alloc; + pub use alloc_crate::boxed::Box; + 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); } - } - pub use alloc_crate::boxed::Box; - pub use alloc_crate::alloc; - pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { - if size == 0 { - return; + + #[cfg(target_arch = "wasm32")] + pub fn run_ctors_once() { + wit_bindgen_symmetric_rt::run_ctors_once(); } - let layout = alloc::Layout::from_size_align_unchecked(size, align); - alloc::dealloc(ptr, layout); - } - - #[cfg(target_arch = "wasm32")] - pub fn run_ctors_once() { - wit_bindgen_symmetric_rt::run_ctors_once(); - } - extern crate alloc as alloc_crate; + 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 { - // fn new() -> u32; - } - // #[doc(hidden)] - // pub mod vtable0 { - // fn write(future: u32, value: u32) -> ::core::pin::Pin>> { - // super::super::_rt::Box::pin(async move { - // #[repr(align(4))] - // struct Buffer([::core::mem::MaybeUninit::; 4]); - // let mut buffer = Buffer([::core::mem::MaybeUninit::uninit(); 4]); - // let address = buffer.0.as_mut_ptr() as *mut u8; - // unsafe { *address.add(0).cast::() = super::super::_rt::as_i32(&value); - // } - // #[link(wasm_import_module = "[import-payload]test:test/future-source")] - // extern "C" { - // #[link_name = "[async][future-write-0]create"] - // fn wit_import(_: u32, _: *mut u8) -> u32; - // } - - // unsafe { wit_bindgen_symmetric_rt::async_support::await_future_result(wit_import, future, address).await } - // }) - // } - - // fn read(future: u32) -> ::core::pin::Pin>>> { - // super::super::_rt::Box::pin(async move { - // struct Buffer([::core::mem::MaybeUninit::; 4]); - // let mut buffer = Buffer([::core::mem::MaybeUninit::uninit(); 4]); - // let address = buffer.0.as_mut_ptr() as *mut u8; - - // #[cfg(not(target_arch = "wasm32"))] - // unsafe extern "C" fn wit_import(_: u32, _: *mut u8) -> u32 { - // unreachable!() - // } - - // #[cfg(target_arch = "wasm32")] - // #[link(wasm_import_module = "[import-payload]test:test/future-source")] - // extern "C" { - // #[link_name = "[async][future-read-0]create"] - // fn wit_import(_: u32, _: *mut u8) -> u32; - // } - - // if unsafe { wit_bindgen_symmetric_rt::async_support::await_future_result(wit_import, future, address).await } { - // let value = unsafe { let l0 = *address.add(0).cast::(); - - // l0 as u32 }; - // Some(value) - // } else { - // None - // } - // }) - // } - - // fn cancel_write(writer: u32) { - // #[cfg(not(target_arch = "wasm32"))] - // { - // unreachable!(); - // } - - // #[cfg(target_arch = "wasm32")] - // { - // #[link(wasm_import_module = "[import-payload]test:test/future-source")] - // extern "C" { - // #[link_name = "[future-cancel-write-0]create"] - // fn cancel(_: u32) -> u32; - // } - // unsafe { cancel(writer) }; - // } - // } - - // fn cancel_read(reader: u32) { - // #[cfg(not(target_arch = "wasm32"))] - // { - // unreachable!(); - // } - - // #[cfg(target_arch = "wasm32")] - // { - // #[link(wasm_import_module = "[import-payload]test:test/future-source")] - // extern "C" { - // #[link_name = "[future-cancel-read-0]create"] - // fn cancel(_: u32) -> u32; - // } - // unsafe { cancel(reader) }; - // } - // } - - // fn close_writable(writer: u32) { - // #[cfg(not(target_arch = "wasm32"))] - // { - // unreachable!(); - // } - - // #[cfg(target_arch = "wasm32")] - // { - // #[link(wasm_import_module = "[import-payload]test:test/future-source")] - // extern "C" { - // #[link_name = "[future-close-writable-0]create"] - // fn drop(_: u32, _: u32); - // } - // unsafe { drop(writer, 0) } - // } - // } - - // fn close_readable(reader: u32) { - // #[cfg(not(target_arch = "wasm32"))] - // { - // unreachable!(); - // } - - // #[cfg(target_arch = "wasm32")] - // { - // #[link(wasm_import_module = "[import-payload]test:test/future-source")] - // extern "C" { - // #[link_name = "[future-close-readable-0]create"] - // fn drop(_: u32); - // } - // unsafe { drop(reader) } - // } - // } - - // pub static VTABLE: wit_bindgen_symmetric_rt::async_support::FutureVtable = wit_bindgen_symmetric_rt::async_support::FutureVtable:: { - // write, read, cancel_write, cancel_read, close_writable, close_readable - // }; - - impl FuturePayload for u32 { - // fn new() -> (u32, &'static wit_bindgen_symmetric_rt::async_support::FutureVtable) { + #![allow(dead_code, unused_variables, clippy::all)] + + #[doc(hidden)] + pub trait FuturePayload: Unpin + Sized + 'static { + // fn new() -> u32; + } + // #[doc(hidden)] + // pub mod vtable0 { + // fn write(future: u32, value: u32) -> ::core::pin::Pin>> { + // super::super::_rt::Box::pin(async move { + // #[repr(align(4))] + // struct Buffer([::core::mem::MaybeUninit::; 4]); + // let mut buffer = Buffer([::core::mem::MaybeUninit::uninit(); 4]); + // let address = buffer.0.as_mut_ptr() as *mut u8; + // unsafe { *address.add(0).cast::() = super::super::_rt::as_i32(&value); + // } + // #[link(wasm_import_module = "[import-payload]test:test/future-source")] + // extern "C" { + // #[link_name = "[async][future-write-0]create"] + // fn wit_import(_: u32, _: *mut u8) -> u32; + // } + + // unsafe { wit_bindgen_symmetric_rt::async_support::await_future_result(wit_import, future, address).await } + // }) + // } + + // fn read(future: u32) -> ::core::pin::Pin>>> { + // super::super::_rt::Box::pin(async move { + // struct Buffer([::core::mem::MaybeUninit::; 4]); + // let mut buffer = Buffer([::core::mem::MaybeUninit::uninit(); 4]); + // let address = buffer.0.as_mut_ptr() as *mut u8; + + // #[cfg(not(target_arch = "wasm32"))] + // unsafe extern "C" fn wit_import(_: u32, _: *mut u8) -> u32 { + // unreachable!() + // } + + // #[cfg(target_arch = "wasm32")] + // #[link(wasm_import_module = "[import-payload]test:test/future-source")] + // extern "C" { + // #[link_name = "[async][future-read-0]create"] + // fn wit_import(_: u32, _: *mut u8) -> u32; + // } + + // if unsafe { wit_bindgen_symmetric_rt::async_support::await_future_result(wit_import, future, address).await } { + // let value = unsafe { let l0 = *address.add(0).cast::(); + + // l0 as u32 }; + // Some(value) + // } else { + // None + // } + // }) + // } + + // fn cancel_write(writer: u32) { // #[cfg(not(target_arch = "wasm32"))] // { // unreachable!(); @@ -326,28 +266,104 @@ pub mod wit_future { // { // #[link(wasm_import_module = "[import-payload]test:test/future-source")] // extern "C" { - // #[link_name = "[future-new-0]create"] - // fn new() -> u32; + // #[link_name = "[future-cancel-write-0]create"] + // fn cancel(_: u32) -> u32; // } - // (unsafe { new() }, &VTABLE) + // unsafe { cancel(writer) }; // } // } - } + + // fn cancel_read(reader: u32) { + // #[cfg(not(target_arch = "wasm32"))] + // { + // unreachable!(); + // } + + // #[cfg(target_arch = "wasm32")] + // { + // #[link(wasm_import_module = "[import-payload]test:test/future-source")] + // extern "C" { + // #[link_name = "[future-cancel-read-0]create"] + // fn cancel(_: u32) -> u32; + // } + // unsafe { cancel(reader) }; + // } + // } + + // fn close_writable(writer: u32) { + // #[cfg(not(target_arch = "wasm32"))] + // { + // unreachable!(); + // } + + // #[cfg(target_arch = "wasm32")] + // { + // #[link(wasm_import_module = "[import-payload]test:test/future-source")] + // extern "C" { + // #[link_name = "[future-close-writable-0]create"] + // fn drop(_: u32, _: u32); + // } + // unsafe { drop(writer, 0) } + // } + // } + + // fn close_readable(reader: u32) { + // #[cfg(not(target_arch = "wasm32"))] + // { + // unreachable!(); + // } + + // #[cfg(target_arch = "wasm32")] + // { + // #[link(wasm_import_module = "[import-payload]test:test/future-source")] + // extern "C" { + // #[link_name = "[future-close-readable-0]create"] + // fn drop(_: u32); + // } + // unsafe { drop(reader) } + // } + // } + + // pub static VTABLE: wit_bindgen_symmetric_rt::async_support::FutureVtable = wit_bindgen_symmetric_rt::async_support::FutureVtable:: { + // write, read, cancel_write, cancel_read, close_writable, close_readable + // }; + + impl FuturePayload for u32 { + // fn new() -> (u32, &'static wit_bindgen_symmetric_rt::async_support::FutureVtable) { + // #[cfg(not(target_arch = "wasm32"))] + // { + // unreachable!(); + // } + + // #[cfg(target_arch = "wasm32")] + // { + // #[link(wasm_import_module = "[import-payload]test:test/future-source")] + // extern "C" { + // #[link_name = "[future-new-0]create"] + // fn new() -> u32; + // } + // (unsafe { new() }, &VTABLE) + // } + // } + } } /// Creates a new Component Model `future` with the specified payload type. -pub fn new() -> (wit_bindgen_symmetric_rt::async_support::FutureWriter, wit_bindgen_symmetric_rt::async_support::FutureReader) { - new_future() - // let (handle, vtable) = T::new(); - // wit_bindgen_symmetric_rt::async_support::with_entry(handle, |entry| match entry { - // ::std::collections::hash_map::Entry::Vacant(entry) => { - // entry.insert(wit_bindgen_symmetric_rt::async_support::Handle::LocalOpen); - // } - // ::std::collections::hash_map::Entry::Occupied(_) => unreachable!(), - // }); - // ( - // wit_bindgen_symmetric_rt::async_support::FutureWriter::new(handle, vtable), - // wit_bindgen_symmetric_rt::async_support::FutureReader::new(handle, vtable), - // ) +pub fn new() -> ( + wit_bindgen_symmetric_rt::async_support::FutureWriter, + wit_bindgen_symmetric_rt::async_support::FutureReader, +) { + new_future() + // let (handle, vtable) = T::new(); + // wit_bindgen_symmetric_rt::async_support::with_entry(handle, |entry| match entry { + // ::std::collections::hash_map::Entry::Vacant(entry) => { + // entry.insert(wit_bindgen_symmetric_rt::async_support::Handle::LocalOpen); + // } + // ::std::collections::hash_map::Entry::Occupied(_) => unreachable!(), + // }); + // ( + // wit_bindgen_symmetric_rt::async_support::FutureWriter::new(handle, vtable), + // wit_bindgen_symmetric_rt::async_support::FutureReader::new(handle, vtable), + // ) } // } @@ -395,6 +411,5 @@ t\x070.223.0\x10wit-bindgen-rust\x060.38.0"; #[inline(never)] #[doc(hidden)] pub fn __link_custom_section_describing_imports() { - wit_bindgen::rt::maybe_link_cabi_realloc(); + wit_bindgen::rt::maybe_link_cabi_realloc(); } - diff --git a/crates/cpp/tests/symmetric_future/main/src/main.rs b/crates/cpp/tests/symmetric_future/main/src/main.rs index e7a11a969..678e45e04 100644 --- a/crates/cpp/tests/symmetric_future/main/src/main.rs +++ b/crates/cpp/tests/symmetric_future/main/src/main.rs @@ -1,3 +1,55 @@ +use wit_bindgen_symmetric_rt::{ + async_support::Stream, + symmetric_stream::{Address, Buffer}, + CallbackState, +}; + +#[link(name = "future")] +extern "C" { + pub fn testX3AtestX2Ffuture_testX00X5BasyncX5Dcreate(results: *mut ()) -> *mut (); +} + +struct CallbackInfo { + stream: Stream, + data: u32, +} + +extern "C" fn ready(arg: *mut ()) -> CallbackState { + let info = unsafe { &*arg.cast::() }; + 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() { - println!("Hello, world!"); + let mut result_future: *mut () = core::ptr::null_mut(); + let continuation = unsafe { + testX3AtestX2Ffuture_testX00X5BasyncX5Dcreate((&mut result_future as *mut *mut ()).cast()) + }; + // function should have completed (not async) + assert!(continuation.is_null()); + let stream = unsafe { Stream::from_handle(result_future as usize) }; + 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/src/lib.rs b/crates/cpp/tests/symmetric_future/source/src/lib.rs index b93cf3ffd..90f162e33 100644 --- a/crates/cpp/tests/symmetric_future/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_future/source/src/lib.rs @@ -1,14 +1,32 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right +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 } -#[cfg(test)] -mod tests { - use super::*; +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 +} - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } +#[allow(non_snake_case)] +#[no_mangle] +pub fn testX3AtestX2Ffuture_sourceX00X5BasyncX5Dcreate(results: *mut u8) -> *mut u8 { + 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 ()); + *unsafe { &mut *results.cast::() } = stream.take_handle(); + std::ptr::null_mut() } diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index e8996bdc3..b2586c4a5 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -19,20 +19,12 @@ struct CallbackInfo { extern "C" fn ready(arg: *mut ()) -> CallbackState { let info = unsafe { &*arg.cast::() }; let buffer = info.stream.read_result(); - // unsafe { stream_support::read_amount(info.stream) }; 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); - // unsafe { - // stream_support::start_reading( - // info.stream, - // info.data.as_ptr().cast_mut().cast(), - // DATALEN, - // ); - // }; // call again CallbackState::Pending } else { @@ -44,10 +36,7 @@ extern "C" fn ready(arg: *mut ()) -> CallbackState { fn main() { let mut result_stream: *mut () = core::ptr::null_mut(); let continuation = unsafe { - testX3AtestX2Fstream_testX00X5BasyncX5Dcreate( - // core::ptr::null_mut(), - (&mut result_stream as *mut *mut ()).cast(), - ) + testX3AtestX2Fstream_testX00X5BasyncX5Dcreate((&mut result_stream as *mut *mut ()).cast()) }; // function should have completed (not async) assert!(continuation.is_null()); @@ -61,13 +50,7 @@ fn main() { DATALEN as u64, ); stream.start_reading(buffer); - // unsafe { - // stream_support::start_reading(stream, info.data.as_mut_ptr().cast(), DATALEN); - // }; let subscription = stream.read_ready_subscribe(); - // unsafe { - // wit_bindgen_symmetric_rt::subscribe_event_send_ptr(stream_support::read_ready_event(stream)) - // }; println!("Register read in main"); wit_bindgen_symmetric_rt::register( subscription, @@ -75,5 +58,4 @@ fn main() { (&*info as *const CallbackInfo).cast_mut().cast(), ); wit_bindgen_symmetric_rt::run(); - // unsafe { stream_support::close_read(stream) }; } diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index d177b9f92..3f756aa11 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -1,8 +1,6 @@ use std::sync::atomic::{AtomicU32, Ordering}; -use wit_bindgen_symmetric_rt::{ - async_support::Stream, register, CallbackState, EventSubscription, -}; +use wit_bindgen_symmetric_rt::{async_support::Stream, register, CallbackState, EventSubscription}; static COUNT: AtomicU32 = AtomicU32::new(1); From da20a27f07696f4773aaa5f04a2482206e4366f7 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 1 Feb 2025 22:32:51 +0100 Subject: [PATCH 492/672] more implementation --- .../rust-client/src/async_support/future_support.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 index 4a821c6dc..a4d3b170d 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/future_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs @@ -51,11 +51,11 @@ impl FutureReader { } pub fn read(self) -> CancelableRead { - todo!() + CancelableRead{ reader: Some(self), future: todo!() } } pub fn take_handle(&self) -> *mut () { - todo!() + self.handle.take_handle() as *mut () } } @@ -75,7 +75,7 @@ impl IntoFuture for FutureReader { /// 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 { - todo!() + self.read() } } From 50f9ad8beabf512ffabe654af36640adc8cc608f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 2 Feb 2025 00:20:11 +0100 Subject: [PATCH 493/672] future impl --- .../tests/symmetric_future/future/src/lib.rs | 2 +- .../src/async_support/future_support.rs | 105 ++++++++++++++++-- 2 files changed, 98 insertions(+), 9 deletions(-) diff --git a/crates/cpp/tests/symmetric_future/future/src/lib.rs b/crates/cpp/tests/symmetric_future/future/src/lib.rs index 70a33ca58..22d26ff83 100644 --- a/crates/cpp/tests/symmetric_future/future/src/lib.rs +++ b/crates/cpp/tests/symmetric_future/future/src/lib.rs @@ -13,7 +13,7 @@ impl future_world::exports::test::test::future_test::Guest for MyStruct { let input = create().await; async_support::spawn(async move { let input = input.await.unwrap(); - write.write(input * 2); + write.write(input * 2).await; }); read } 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 index a4d3b170d..0c9497f62 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/future_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs @@ -1,11 +1,16 @@ use std::{ future::{Future, IntoFuture}, marker::PhantomData, + mem::MaybeUninit, pin::Pin, task::{Context, Poll}, }; -use super::Stream; +use futures::FutureExt; + +use crate::symmetric_stream::{Address, Buffer}; + +use super::{wait_on, Stream}; //use super::Future; @@ -24,15 +29,54 @@ impl FutureWriter { } } - pub fn write(self, _v: T) { - todo!() + pub fn write(self, data: T) -> CancelableWrite { + CancelableWrite{ writer: self, future: None, data: Some(data) } + } +} + +/// Represents a write operation which may be canceled prior to completion. +pub struct CancelableWrite { + writer: FutureWriter, + future: Option + 'static + Send>>>, + data: Option, +} + +impl Future for CancelableWrite { + 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; + unsafe { (*addr).write(data) }; + //let size = buffer.capacity() as usize; + }) as Pin + 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 { - reader: Option>, - future: Pin> + 'static + Send>>, + reader: FutureReader, + future: Option> + 'static + Send>>>, } pub struct FutureReader { @@ -51,7 +95,10 @@ impl FutureReader { } pub fn read(self) -> CancelableRead { - CancelableRead{ reader: Some(self), future: todo!() } + CancelableRead { + reader: self, + future: None, + } } pub fn take_handle(&self) -> *mut () { @@ -59,15 +106,57 @@ impl FutureReader { } } -impl Future for CancelableRead { +impl Future for CancelableRead { type Output = Option; fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + 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::::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 + 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 CancelableRead { + pub fn cancel(mut self) -> FutureReader { + self.cancel_mut() + } + + fn cancel_mut(&mut self) -> FutureReader { todo!() } } -impl IntoFuture for FutureReader { +impl IntoFuture for FutureReader { type Output = Option; type IntoFuture = CancelableRead; From bf26bbc8dc2098edc5085cd562e20f0fdba71a86 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 2 Feb 2025 00:25:29 +0100 Subject: [PATCH 494/672] future works fine --- .../rust-client/src/async_support/future_support.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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 index 0c9497f62..6be83b49a 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/future_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs @@ -30,7 +30,11 @@ impl FutureWriter { } pub fn write(self, data: T) -> CancelableWrite { - CancelableWrite{ writer: self, future: None, data: Some(data) } + CancelableWrite { + writer: self, + future: None, + data: Some(data), + } } } @@ -41,7 +45,7 @@ pub struct CancelableWrite { data: Option, } -impl Future for CancelableWrite { +impl Future for CancelableWrite { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> { @@ -60,7 +64,8 @@ impl Future for CancelableWrite { let buffer = handle.start_writing(); let addr = buffer.get_address().take_handle() as *mut MaybeUninit; unsafe { (*addr).write(data) }; - //let size = buffer.capacity() as usize; + buffer.set_size(1); + handle.finish_writing(Some(buffer)); }) as Pin + Send>>); } match me.future.as_mut().unwrap().poll_unpin(cx) { From d7cddb70b154a16d806f400bd9b7a97b8da4efc9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 2 Feb 2025 22:47:48 +0100 Subject: [PATCH 495/672] fix the endless wait in the stream example --- crates/symmetric_executor/src/lib.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 40eacef0e..67defa149 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -209,16 +209,19 @@ impl symmetric_executor::Guest for Guest { break; } } - // if tvptr.is_null() && maxfd == 0 { - // // probably only active tasks, all returned pending, try again - // if DEBUGGING { - // println!( - // "Relooping with {} tasks", - // EXECUTOR.lock().unwrap().active_tasks.len() - // ); - // } - // continue; - // } + 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 { From 94ca086cf359f77d37d520527fd47d3f7d69f587 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 5 Feb 2025 23:14:19 +0100 Subject: [PATCH 496/672] fix compilation with wamr --- crates/cpp/helper-types/wit-host.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/helper-types/wit-host.h b/crates/cpp/helper-types/wit-host.h index 3ccbef813..bd9ae704f 100644 --- a/crates/cpp/helper-types/wit-host.h +++ b/crates/cpp/helper-types/wit-host.h @@ -159,7 +159,7 @@ template class guest_owned : public T { exec_env(b.exec_env) #endif { - b.data_ = nullptr; + b.data_ = INVALID_GUEST_ADDRESS; } guest_owned(T &&t, guest_address a, #ifdef WIT_HOST_WAMR From 253ad8d4d8310a03b6c434fc4fad554d47f228dc Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 9 Mar 2025 14:14:54 +0100 Subject: [PATCH 497/672] fix compilation after large merge --- crates/c/src/lib.rs | 2 +- crates/core/src/abi.rs | 36 +++-------- crates/core/src/lib.rs | 10 ++-- crates/core/src/symmetric.rs | 8 ++- crates/cpp/src/lib.rs | 109 ++++++++++++++-------------------- crates/cpp/src/wamr.rs | 17 ++---- crates/csharp/src/function.rs | 3 +- crates/moonbit/src/lib.rs | 24 +++++--- crates/rust/src/bindgen.rs | 10 +--- 9 files changed, 88 insertions(+), 131 deletions(-) diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index d5ecb2213..3bd1c9f2d 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -556,7 +556,7 @@ impl C { fn push_type_name(&mut self, ty: &Type, dst: &mut String) { match ty { Type::Bool => dst.push_str("bool"), - Type::Char => dst.push_str("uint32_t"), // TODO: better type? + Type::Char => dst.push_str("uint32_t"), Type::U8 => dst.push_str("uint8_t"), Type::S8 => dst.push_str("int8_t"), Type::U16 => dst.push_str("uint16_t"), diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 27ea9f8fc..826ec188c 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -1,8 +1,7 @@ pub use wit_parser::abi::{AbiVariant, WasmSignature, WasmType}; use wit_parser::{ align_to_arch, Alignment, ArchitectureSize, ElementInfo, Enum, Flags, FlagsRepr, Function, - Handle, Int, Record, Resolve, Result_, Results, SizeAlign, Tuple, Type, TypeDefKind, TypeId, - Variant, + Handle, Int, Record, Resolve, Result_, SizeAlign, Tuple, Type, TypeDefKind, TypeId, Variant, }; // Helper macro for defining instructions without having to have tons of @@ -840,7 +839,6 @@ fn needs_post_return(resolve: &Resolve, ty: &Type) -> bool { TypeDefKind::Future(_) | TypeDefKind::Stream(_) => false, TypeDefKind::Unknown => unreachable!(), }, - Type::Bool | Type::U8 | Type::S8 @@ -924,9 +922,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { .bindgen .sizes() .record(func.params.iter().map(|(_, ty)| ty)); - let ptr = self - .bindgen - .return_pointer(size, align); + let ptr = self.bindgen.return_pointer(size, align); lower_to_memory(self, ptr); } else { if !sig.indirect_params { @@ -972,9 +968,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { if self.async_ { let ElementInfo { size, align } = self.bindgen.sizes().record(func.result.iter()); - let ptr = self - .bindgen - .return_pointer(size, align); + let ptr = self.bindgen.return_pointer(size, align); self.return_pointer = Some(ptr.clone()); self.stack.push(ptr); @@ -987,9 +981,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { // this ABI. if self.variant == AbiVariant::GuestImport && sig.retptr { let info = self.bindgen.sizes().params(&func.result); - let ptr = self - .bindgen - .return_pointer(info.size, info.align); + let ptr = self.bindgen.return_pointer(info.size, info.align); self.return_pointer = Some(ptr.clone()); self.stack.push(ptr); } @@ -1005,11 +997,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { if matches!(self.lift_lower, LiftLower::Symmetric) && sig.retptr { // probably wrong? let ptr = self.stack.pop().unwrap(); - self.read_results_from_memory( - &func.result, - ptr.clone(), - Default::default(), - ); + 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 @@ -1663,8 +1651,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 }) } @@ -1874,9 +1860,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::List(_) => self.read_list_from_memory(ty, addr, offset), - TypeDefKind::Future(_) - | TypeDefKind::Stream(_) - | TypeDefKind::Handle(_) => { + TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Handle(_) => { if matches!(self.lift_lower, LiftLower::Symmetric) { self.emit_and_lift(ty, addr, &PointerLoad { offset }) } else { @@ -2065,7 +2049,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); self.emit(&Instruction::GuestDeallocateString); } - Type::Bool | Type::U8 | Type::S8 @@ -2077,9 +2060,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { | Type::U64 | Type::S64 | Type::F32 - | Type::F64 - | Type::ErrorContext => {} - + | Type::F64 => {} Type::Id(id) => match &self.resolve.types[id].kind { TypeDefKind::Type(t) => self.deallocate(t, addr, offset), @@ -2150,6 +2131,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::Stream(_) => todo!("read stream from memory"), TypeDefKind::Unknown => unreachable!(), }, + Type::ErrorContext => todo!(), } } @@ -2277,7 +2259,7 @@ pub fn wasm_signature_symmetric( } let mut results = Vec::new(); - for ty in func.results.iter_types() { + for ty in func.result.iter() { push_flat_symmetric(resolve, ty, &mut results) } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 3ba905669..638441956 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -243,12 +243,10 @@ pub trait WorldGenerator { // TODO: Should we refine this test to inspect only types reachable from // the specified world? if !cfg!(feature = "async") - && resolve.types.iter().any(|(_, ty)| { - matches!( - ty.kind, - TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext - ) - }) + && resolve + .types + .iter() + .any(|(_, ty)| matches!(ty.kind, TypeDefKind::Future(_) | TypeDefKind::Stream(_))) { anyhow::bail!( "must enable `async` feature when using WIT files \ diff --git a/crates/core/src/symmetric.rs b/crates/core/src/symmetric.rs index bf9e710e2..6d73fbca2 100644 --- a/crates/core/src/symmetric.rs +++ b/crates/core/src/symmetric.rs @@ -37,10 +37,10 @@ fn needs_dealloc2(resolve: &Resolve, tp: &Type) -> bool { TypeDefKind::List(_l) => true, TypeDefKind::Future(_) => todo!(), TypeDefKind::Stream(_) => todo!(), - TypeDefKind::ErrorContext => false, TypeDefKind::Type(tp) => needs_dealloc2(resolve, tp), TypeDefKind::Unknown => false, }, + Type::ErrorContext => todo!(), } } @@ -101,10 +101,11 @@ fn has_non_canonical_list2(resolve: &Resolve, ty: &Type, maybe: bool) -> bool { has_non_canonical_list2(resolve, ty, true) } } - TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext => false, + TypeDefKind::Future(_) | TypeDefKind::Stream(_) => false, TypeDefKind::Type(ty) => has_non_canonical_list2(resolve, ty, maybe), TypeDefKind::Unknown => false, }, + Type::ErrorContext => todo!(), } } @@ -164,10 +165,11 @@ fn has_non_canonical_list_rust2(resolve: &Resolve, ty: &Type) -> bool { .map_or(false, |ty| has_non_canonical_list_rust2(resolve, ty)) } TypeDefKind::List(_ty) => true, - TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::ErrorContext => false, + TypeDefKind::Future(_) | TypeDefKind::Stream(_) => false, TypeDefKind::Type(ty) => has_non_canonical_list_rust2(resolve, ty), TypeDefKind::Unknown => false, }, + Type::ErrorContext => todo!(), } } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index afafbb820..e4d1f8836 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -12,8 +12,7 @@ use wit_bindgen_core::{ make_external_component, make_external_symbol, symmetric, uwrite, uwriteln, wit_parser::{ Alignment, ArchitectureSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, - Resolve, Results, SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, - WorldKey, + Resolve, SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, }, Files, InterfaceGenerator, Source, WorldGenerator, }; @@ -956,7 +955,6 @@ impl CppInterfaceGenerator<'_> { TypeDefKind::Stream(_) => todo!("generate for stream"), TypeDefKind::Handle(_) => todo!("generate for handle"), TypeDefKind::Unknown => unreachable!(), - TypeDefKind::ErrorContext => todo!(), } } @@ -972,6 +970,9 @@ impl CppInterfaceGenerator<'_> { 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]; @@ -1202,7 +1203,7 @@ impl CppInterfaceGenerator<'_> { func: &Function, abi_variant: AbiVariant, // import: bool, - from_namespace: &Vec, + _from_namespace: &Vec, ) -> HighlevelSignature { let mut res = HighlevelSignature::default(); // let abi_variant = if import ^ self.gen.opts.host_side() { @@ -1230,39 +1231,15 @@ impl CppInterfaceGenerator<'_> { && matches!(abi_variant, AbiVariant::GuestExport) && self.gen.opts.host_side()) { - match &func.results { - wit_bindgen_core::wit_parser::Results::Named(n) => { - if n.is_empty() { - res.result = "void".into(); - } else { - res.result = "std::tuple<".into(); - for (i, (_name, ty)) in n.iter().enumerate() { - if i > 0 { - res.result.push_str(", "); - } - res.result.push_str(&self.type_name( - ty, - &res.namespace, - Flavor::Result(abi_variant), - )); - } - res.result.push('>'); - } - } - wit_bindgen_core::wit_parser::Results::Anon(ty) => { - if matches!(is_drop, SpecialMethod::Allocate) { - res.result = OWNED_CLASS_NAME.into(); - } else { - res.result = - self.type_name(ty, from_namespace, Flavor::Result(abi_variant)); - if matches!( - is_drop, - SpecialMethod::Allocate | SpecialMethod::ResourceRep - ) { - res.result.push('*'); - } - } - } + 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) @@ -1731,6 +1708,9 @@ impl CppInterfaceGenerator<'_> { 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( @@ -2059,8 +2039,8 @@ impl CppInterfaceGenerator<'_> { TypeDefKind::Stream(_) => todo!(), TypeDefKind::Type(ty) => self.type_name(ty, from_namespace, flavor), TypeDefKind::Unknown => todo!(), - TypeDefKind::ErrorContext => todo!(), }, + Type::ErrorContext => todo!(), } } @@ -2236,7 +2216,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> name: name, kind: FunctionKind::Static(id), params: vec![("self".into(), Type::Id(id))], - results: Results::Named(vec![]), + result: None, docs: Docs::default(), stability: Stability::Unknown, }; @@ -2253,6 +2233,9 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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(_)) @@ -2264,7 +2247,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> kind: FunctionKind::Static(id), // same params as constructor params: func.params.clone(), - results: Results::Anon(Type::Id(id)), + result: Some(Type::Id(id)), docs: Docs::default(), stability: Stability::Unknown, }; @@ -2297,7 +2280,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> name: "[resource-new]".to_string() + &name, kind: FunctionKind::Static(id), params: vec![("self".into(), Type::Id(id))], - results: Results::Anon(id_type), + result: Some(id_type), docs: Docs::default(), stability: Stability::Unknown, }; @@ -2307,7 +2290,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> name: "[resource-rep]".to_string() + &name, kind: FunctionKind::Static(id), params: vec![("id".into(), id_type)], - results: Results::Anon(Type::Id(id)), + result: Some(Type::Id(id)), docs: Docs::default(), stability: Stability::Unknown, }; @@ -2317,7 +2300,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> name: "[resource-drop]".to_string() + &name, kind: FunctionKind::Static(id), params: vec![("id".into(), id_type)], - results: Results::Named(vec![]), + result: None, docs: Docs::default(), stability: Stability::Unknown, }; @@ -2523,10 +2506,6 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> fn type_stream(&mut self, _id: TypeId, _name: &str, _ty: &Option, _docs: &Docs) { todo!() } - - fn type_error_context(&mut self, _id: TypeId, _name: &str, _docs: &Docs) { - todo!() - } } struct CabiPostInformation { @@ -2678,8 +2657,9 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { | Type::F32 | Type::F64 | Type::Char => false, - Type::String => false, // correct? + Type::String => false, Type::Id(id) => self.has_resources(id), + Type::ErrorContext => todo!(), } } fn has_resources(&self, id: &TypeId) -> bool { @@ -2701,7 +2681,6 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { _ => false, }, TypeDefKind::Unknown => todo!(), - TypeDefKind::ErrorContext => todo!(), } } } @@ -3652,7 +3631,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } abi::Instruction::CallInterface { func, .. } => { // dbg!(func); - self.let_results(func.results.len(), results); + 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); @@ -3748,20 +3727,21 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src.push_str(&operands[0]); } } else { - 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(")"); + 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, @@ -3872,7 +3852,6 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::StreamLift { .. } => todo!(), abi::Instruction::ErrorContextLower { .. } => todo!(), abi::Instruction::ErrorContextLift { .. } => todo!(), - abi::Instruction::AsyncMalloc { .. } => todo!(), abi::Instruction::AsyncCallWasm { .. } => todo!(), abi::Instruction::AsyncPostCallInterface { .. } => todo!(), abi::Instruction::AsyncCallReturn { .. } => todo!(), diff --git a/crates/cpp/src/wamr.rs b/crates/cpp/src/wamr.rs index e82529b5a..ebdf5af12 100644 --- a/crates/cpp/src/wamr.rs +++ b/crates/cpp/src/wamr.rs @@ -1,4 +1,4 @@ -use wit_bindgen_core::wit_parser::{Function, Resolve, Results, Type, TypeDefKind}; +use wit_bindgen_core::wit_parser::{Function, Resolve, Type, TypeDefKind}; #[derive(Debug, Default)] pub struct WamrSig { @@ -61,8 +61,8 @@ fn push_wamr(ty: &Type, resolve: &Resolve, params_str: &mut String) { TypeDefKind::Handle(_h) => { params_str.push('i'); } - TypeDefKind::ErrorContext => todo!(), }, + Type::ErrorContext => todo!(), } } @@ -124,8 +124,8 @@ fn wamr_add_result(sig: &mut WamrSig, resolve: &Resolve, ty: &Type) { TypeDefKind::Handle(_h) => { sig.wamr_result = "i".into(); } - TypeDefKind::ErrorContext => todo!(), }, + Type::ErrorContext => todo!(), } } @@ -134,14 +134,9 @@ pub fn wamr_signature(resolve: &Resolve, func: &Function) -> WamrSig { for (_name, param) in func.params.iter() { push_wamr(param, resolve, &mut result.wamr_types); } - match &func.results { - Results::Named(p) => { - if !p.is_empty() { - // assume a pointer - result.wamr_types.push('*'); - } - } - Results::Anon(e) => wamr_add_result(&mut result, resolve, e), + match &func.result { + Some(ty) => wamr_add_result(&mut result, resolve, ty), + None => {} } result } diff --git a/crates/csharp/src/function.rs b/crates/csharp/src/function.rs index 95645533d..698d19ba9 100644 --- a/crates/csharp/src/function.rs +++ b/crates/csharp/src/function.rs @@ -1293,7 +1293,8 @@ impl Bindgen for FunctionBindgen<'_, '_> { " var {ret_area} = stackalloc {element_type}[{array_size}+1]; var {ptr} = ((int){ret_area}) + ({align} - 1) & -{align}; - " + ", + align = align.align_wasm32() ); format!("{ptr}") } diff --git a/crates/moonbit/src/lib.rs b/crates/moonbit/src/lib.rs index 4c5575576..970ffd4e7 100644 --- a/crates/moonbit/src/lib.rs +++ b/crates/moonbit/src/lib.rs @@ -2468,49 +2468,57 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::I32Load { offset } | Instruction::PointerLoad { offset } | Instruction::LengthLoad { offset } => results.push(format!( - "{ffi_qualifier}load32(({}) + {offset})", + "{}load32(({}) + {offset})", + self.gen.qualify_package(FFI_DIR), operands[0], offset = offset.size_wasm32() )), Instruction::I32Load8U { offset } => results.push(format!( - "{ffi_qualifier}load8_u(({}) + {offset})", + "{}load8_u(({}) + {offset})", + self.gen.qualify_package(FFI_DIR), operands[0], offset = offset.size_wasm32() )), Instruction::I32Load8S { offset } => results.push(format!( - "{ffi_qualifier}load8(({}) + {offset})", + "{}load8(({}) + {offset})", + self.gen.qualify_package(FFI_DIR), operands[0], offset = offset.size_wasm32() )), Instruction::I32Load16U { offset } => results.push(format!( - "{ffi_qualifier}load16_u(({}) + {offset})", + "{}load16_u(({}) + {offset})", + self.gen.qualify_package(FFI_DIR), operands[0], offset = offset.size_wasm32() )), Instruction::I32Load16S { offset } => results.push(format!( - "{ffi_qualifier}load16(({}) + {offset})", + "{}load16(({}) + {offset})", + self.gen.qualify_package(FFI_DIR), operands[0], offset = offset.size_wasm32() )), Instruction::I64Load { offset } => results.push(format!( - "{ffi_qualifier}load64(({}) + {offset})", + "{}load64(({}) + {offset})", + self.gen.qualify_package(FFI_DIR), operands[0], offset = offset.size_wasm32() )), Instruction::F32Load { offset } => results.push(format!( - "{ffi_qualifier}loadf32(({}) + {offset})", + "{}loadf32(({}) + {offset})", + self.gen.qualify_package(FFI_DIR), operands[0], offset = offset.size_wasm32() )), Instruction::F64Load { offset } => results.push(format!( - "{ffi_qualifier}loadf64(({}) + {offset})", + "{}loadf64(({}) + {offset})", + self.gen.qualify_package(FFI_DIR), operands[0], offset = offset.size_wasm32() )), diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 26e3fe1a7..50b677c8f 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -285,7 +285,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { // stack whereas exports use a per-module return area to cut down on // stack usage. Note that for imports this also facilitates "adapter // modules" for components to not have data segments. - if size == 0 { + if size.is_empty() { // If the size requested is 0 then we know it won't be written to so // hand out a null pointer. This can happen with async for example // when the params or results are zero-sized. @@ -985,14 +985,6 @@ impl Bindgen for FunctionBindgen<'_, '_> { let func = self.declare_import("", name, &[WasmType::Pointer; 3], &[WasmType::I32]); let async_support = self.gen.gen.async_support_path(); - let tmp = self.tmp(); - let layout = format!("layout{tmp}"); - let alloc = self.gen.path_to_std_alloc_module(); - self.push_str(&format!( - "let {layout} = {alloc}::Layout::from_size_align_unchecked({size}, {align});\n", - size = size.format(POINTER_SIZE_EXPRESSION), - align = align.format(POINTER_SIZE_EXPRESSION) - )); let operands = operands.join(", "); uwriteln!( self.src, From a833dc910d120ca02996ecbd823393ef3789eca1 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 9 Mar 2025 16:12:38 +0100 Subject: [PATCH 498/672] regenerate --- .../async_module/src/async_module.rs | 866 +----------------- crates/cpp/tests/symmetric_async/generate.sh | 0 .../symmetric_async/wit/async_module.wit | 4 +- 3 files changed, 34 insertions(+), 836 deletions(-) mode change 100644 => 100755 crates/cpp/tests/symmetric_async/generate.sh 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 index 96b509285..d115b3091 100644 --- a/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs +++ b/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs @@ -1,29 +1,28 @@ -// Generated by `wit-bindgen` 0.36.0. DO NOT EDIT! +// 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, clippy::all)] + #[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 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; + #[cfg_attr(target_arch = "wasm32", link_name = "[async-lower]sleep")] + fn testX3AtestX2FwaitX00X5Basync_lowerX5Dsleep(_: u64) -> *mut u8; } ::wit_bindgen_symmetric_rt::async_support::await_result( - move || unsafe { testX3AtestX2FwaitX00X5BasyncX5Dsleep(nanoseconds) }, // layout2, - // ptr0.cast_mut().cast(), - // ptr1, + move || unsafe { testX3AtestX2FwaitX00X5Basync_lowerX5Dsleep(nanoseconds) }, ) .await; } @@ -36,7 +35,7 @@ pub mod exports { pub mod test { pub mod test { - #[allow(dead_code, clippy::all)] + #[allow(dead_code, unused_imports, clippy::all)] pub mod string_delay { #[used] #[doc(hidden)] @@ -53,31 +52,27 @@ pub mod exports { ) -> *mut u8 { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); - let len0 = arg1; - let addr0 = arg0; - let string0 = String::from( - std::str::from_utf8(std::slice::from_raw_parts(addr0, len0)).unwrap(), - ); - let result1 = T::forward(string0); - let result = ::wit_bindgen_symmetric_rt::async_support::first_poll( - result1, - move |result2| { - let vec3 = (result2.into_bytes()).into_boxed_slice(); - let ptr3 = vec3.as_ptr().cast::(); - let len3 = vec3.len(); - ::core::mem::forget(vec3); - let output = arg2.cast::(); - *unsafe { &mut *output } = ptr3 as usize; - *unsafe { &mut *output.add(1) } = len3; - }, - ); - + 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::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::(); + let len2 = vec2.len(); + ::core::mem::forget(vec2); + let output = arg2.cast::(); + *unsafe { &mut *output } = ptr2 as usize; + *unsafe { &mut *output.add(1) } = len2; + }); result.cast() } pub trait Guest { - fn forward( - s: _rt::String, - ) -> impl ::core::future::Future + 'static; + async fn forward(s: _rt::String) -> _rt::String; } #[doc(hidden)] @@ -86,8 +81,8 @@ pub mod exports { #[cfg_attr(target_arch = "wasm32", export_name = "forward")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn X5BasyncX5DtestX3AtestX2Fstring_delayX00forward(arg0: *mut u8,arg1:usize,arg2: *mut u8,) -> *mut u8 { - $($path_to_types)*::_export_forward_cabi::<$ty>(arg0, arg1,arg2) + unsafe extern "C" fn testX3AtestX2Fstring_delayX00forward(arg0: *mut u8,arg1: usize,arg2: *mut u8,) -> *mut u8 { + $($path_to_types)*::_export_forward_cabi::<$ty>(arg0, arg1, arg2) } };); } @@ -99,7 +94,6 @@ pub mod exports { } mod _rt { #![allow(dead_code, clippy::all)] - pub use alloc_crate::alloc; pub fn as_i64(t: T) -> i64 { t.as_i64() @@ -128,814 +122,18 @@ mod _rt { self as i64 } } - 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); - } #[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; - #[cfg(target_arch = "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_symmetric_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; - 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 { - handle: u32, - _phantom: PhantomData, - } - - impl fmt::Debug for FutureWriter { - 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 { - writer: Option>, - future: Pin>>, - } - - impl Future for CancelableWrite { - 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 CancelableWrite { - /// 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 { - self.cancel_mut() - } - - fn cancel_mut(&mut self) -> FutureWriter { - 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 Drop for CancelableWrite { - fn drop(&mut self) { - if self.writer.is_some() { - self.cancel_mut(); - } - } - } - - impl FutureWriter { - /// Write the specified value to this `future`. - pub fn write(self, v: T) -> CancelableWrite { - 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>> - } - 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 Drop for FutureWriter { - 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 { - reader: Option>, - future: Pin>>>, - } - - impl Future for CancelableRead { - type Output = Option; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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 CancelableRead { - /// 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 { - self.cancel_mut() - } - - fn cancel_mut(&mut self) -> FutureReader { - 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 Drop for CancelableRead { - fn drop(&mut self) { - if self.reader.is_some() { - self.cancel_mut(); - } - } - } - - /// Represents the readable end of a Component Model `future`. - pub struct FutureReader { - handle: u32, - _phantom: PhantomData, - } - - impl fmt::Debug for FutureReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FutureReader") - .field("handle", &self.handle) - .finish() - } - } - - impl FutureReader { - #[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 IntoFuture for FutureReader { - type Output = Option; - type IntoFuture = CancelableRead; - - /// 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>>, - 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 Drop for FutureReader { - 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; - async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; - fn cancel_write(stream: u32); - fn cancel_read(stream: u32); - fn close_writable(stream: u32); - fn close_readable(stream: u32); - } - - struct CancelWriteOnDrop { - handle: Option, - _phantom: PhantomData, - } - - impl Drop for CancelWriteOnDrop { - 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 { - handle: u32, - future: Option + 'static>>>, - _phantom: PhantomData, - } - - impl StreamWriter { - /// 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 fmt::Debug for StreamWriter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StreamWriter") - .field("handle", &self.handle) - .finish() - } - } - - impl Sink> for StreamWriter { - type Error = Infallible; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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) -> 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:: { - 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:: { - 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> { - self.poll_ready(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.poll_ready(cx) - } - } - - impl Drop for StreamWriter { - 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 { - handle: Option, - _phantom: PhantomData, - } - - impl Drop for CancelReadOnDrop { - 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 { - handle: u32, - future: Option>> + 'static>>>, - _phantom: PhantomData, - } - - impl StreamReader { - /// 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 fmt::Debug for StreamReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StreamReader") - .field("handle", &self.handle) - .finish() - } - } - - impl StreamReader { - #[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 Stream for StreamReader { - type Item = Vec; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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:: { - handle: Some(handle), - _phantom: PhantomData, - }; - Box::pin(async move { - let mut buffer = iter::repeat_with(MaybeUninit::uninit) - .take(ceiling(64 * 1024, mem::size_of::())) - .collect::>(); - - let result = if let Some(count) = - T::read(handle, &mut buffer).await - { - buffer.truncate(count); - Some(unsafe { - mem::transmute::>, Vec>(buffer) - }) - } else { - None - }; - cancel_on_drop.handle = None; - drop(cancel_on_drop); - result - }) - as Pin>> - } - Handle::LocalOpen => { - let (tx, rx) = oneshot::channel(); - entry.insert(Handle::LocalWaiting(tx)); - let mut cancel_on_drop = CancelReadOnDrop:: { - 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 Drop for StreamReader { - 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() -> (FutureWriter, FutureReader) { - 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() -> (StreamWriter, StreamReader) { - 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 } - } - } + pub use alloc_crate::vec::Vec; 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. +/// 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!`. /// @@ -963,7 +161,7 @@ macro_rules! __export_async_module_impl { pub(crate) use __export_async_module_impl as export; #[cfg(target_arch = "wasm32")] -#[link_section = "component-type:wit-bindgen:0.36.0:test:test:async-module:encoded world"] +#[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; 264] = *b"\ @@ -972,7 +170,7 @@ A\x04\x01B\x02\x01@\x01\x0bnanosecondsw\x01\0\x04\0\x05sleep\x01\0\x03\0\x0etest :test/wait\x05\0\x01B\x02\x01@\x01\x01ss\0s\x04\0\x07forward\x01\0\x04\0\x16test\ :test/string-delay\x05\x01\x04\0\x16test:test/async-module\x04\0\x0b\x12\x01\0\x0c\ async-module\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x07\ -0.221.2\x10wit-bindgen-rust\x060.36.0"; +0.227.1\x10wit-bindgen-rust\x060.40.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/cpp/tests/symmetric_async/generate.sh b/crates/cpp/tests/symmetric_async/generate.sh old mode 100644 new mode 100755 diff --git a/crates/cpp/tests/symmetric_async/wit/async_module.wit b/crates/cpp/tests/symmetric_async/wit/async_module.wit index 3436d87a3..76d826324 100644 --- a/crates/cpp/tests/symmetric_async/wit/async_module.wit +++ b/crates/cpp/tests/symmetric_async/wit/async_module.wit @@ -1,11 +1,11 @@ package test:test; interface string-delay { - forward: func(s: string) -> string; + forward: async func(s: string) -> string; } interface wait { - sleep: func(nanoseconds: u64); + sleep: async func(nanoseconds: u64); } world async-module { From 8f12fd809f831e57f37bd95c5eb4e5cb31e93703 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 9 Mar 2025 16:28:21 +0100 Subject: [PATCH 499/672] better alignment with latest codegen --- .../async_module/src/async_module.rs | 38 +++++++++---------- .../symmetric_async/async_module/src/lib.rs | 6 +-- .../tests/symmetric_async/main/src/main.rs | 8 ++-- 3 files changed, 26 insertions(+), 26 deletions(-) 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 index d115b3091..d856d5e44 100644 --- a/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs +++ b/crates/cpp/tests/symmetric_async/async_module/src/async_module.rs @@ -13,17 +13,17 @@ pub mod test { use super::super::super::_rt; #[allow(unused_unsafe, clippy::all)] - pub async fn sleep(nanoseconds: u64) -> () { + 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-lower]sleep")] - fn testX3AtestX2FwaitX00X5Basync_lowerX5Dsleep(_: u64) -> *mut u8; + #[cfg_attr(target_arch = "wasm32", link_name = "[async]sleep")] + fn testX3AtestX2FwaitX00X5BasyncX5Dsleep(_: u64) -> *mut u8; } - ::wit_bindgen_symmetric_rt::async_support::await_result( - move || unsafe { testX3AtestX2FwaitX00X5Basync_lowerX5Dsleep(nanoseconds) }, - ) + ::wit_bindgen_symmetric_rt::async_support::await_result(move || unsafe { + testX3AtestX2FwaitX00X5BasyncX5Dsleep(nanoseconds) + }) .await; } } @@ -45,7 +45,7 @@ pub mod exports { use super::super::super::super::_rt; #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_forward_cabi( + pub unsafe fn _export_async_forward_cabi( arg0: *mut u8, arg1: usize, arg2: *mut u8, @@ -57,7 +57,7 @@ pub mod exports { let string0 = String::from( std::str::from_utf8(std::slice::from_raw_parts(arg0, len0)).unwrap(), ); - let result = T::forward(string0).await; + let result = T::async_forward(string0).await; result }; let result = wit_bindgen_symmetric_rt::async_support::first_poll(result, move |result1| { @@ -72,17 +72,17 @@ pub mod exports { result.cast() } pub trait Guest { - async fn forward(s: _rt::String) -> _rt::String; + 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 = "forward")] + #[cfg_attr(target_arch = "wasm32", export_name = "[async]forward")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn testX3AtestX2Fstring_delayX00forward(arg0: *mut u8,arg1: usize,arg2: *mut u8,) -> *mut u8 { - $($path_to_types)*::_export_forward_cabi::<$ty>(arg0, arg1, arg2) + 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) } };); } @@ -164,13 +164,13 @@ pub(crate) use __export_async_module_impl as export; #[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; 264] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x85\x01\x01A\x02\x01\ -A\x04\x01B\x02\x01@\x01\x0bnanosecondsw\x01\0\x04\0\x05sleep\x01\0\x03\0\x0etest\ -:test/wait\x05\0\x01B\x02\x01@\x01\x01ss\0s\x04\0\x07forward\x01\0\x04\0\x16test\ -:test/string-delay\x05\x01\x04\0\x16test:test/async-module\x04\0\x0b\x12\x01\0\x0c\ -async-module\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x07\ -0.227.1\x10wit-bindgen-rust\x060.40.0"; +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)] diff --git a/crates/cpp/tests/symmetric_async/async_module/src/lib.rs b/crates/cpp/tests/symmetric_async/async_module/src/lib.rs index d0e8b5fdb..9973e2af8 100644 --- a/crates/cpp/tests/symmetric_async/async_module/src/lib.rs +++ b/crates/cpp/tests/symmetric_async/async_module/src/lib.rs @@ -5,15 +5,15 @@ async_module::export!(Guest with_types_in async_module); struct Guest; impl async_module::exports::test::test::string_delay::Guest for Guest { - async fn forward(s: String) -> String { + async fn async_forward(s: String) -> String { match s.as_str() { "A" => "directly returned".into(), "B" => { - async_module::test::test::wait::sleep(5_000_000_000).await; + async_module::test::test::wait::async_sleep(5_000_000_000).await; "after five seconds".into() } _ => { - async_module::test::test::wait::sleep(1_000_000_000).await; + async_module::test::test::wait::async_sleep(1_000_000_000).await; "after one second".into() } } diff --git a/crates/cpp/tests/symmetric_async/main/src/main.rs b/crates/cpp/tests/symmetric_async/main/src/main.rs index 72c326d20..8b1dd8e61 100644 --- a/crates/cpp/tests/symmetric_async/main/src/main.rs +++ b/crates/cpp/tests/symmetric_async/main/src/main.rs @@ -2,7 +2,7 @@ use wit_bindgen_symmetric_rt::{CallbackState, EventSubscription}; #[link(name = "async_module")] extern "C" { - pub fn X5BasyncX5DtestX3AtestX2Fstring_delayX00forward( + pub fn testX3AtestX2Fstring_delayX00X5BasyncX5Dforward( addr: *const u8, len: usize, results: *mut (), @@ -22,7 +22,7 @@ extern "C" fn print_result(obj: *mut ()) -> CallbackState { fn main() { let mut result1: [usize; 2] = [0, 0]; let handle1 = unsafe { - X5BasyncX5DtestX3AtestX2Fstring_delayX00forward( + testX3AtestX2Fstring_delayX00X5BasyncX5Dforward( "A".as_ptr(), 1, result1.as_mut_ptr().cast(), @@ -35,7 +35,7 @@ fn main() { let mut result2: [usize; 2] = [0, 0]; let handle2 = unsafe { - X5BasyncX5DtestX3AtestX2Fstring_delayX00forward( + testX3AtestX2Fstring_delayX00X5BasyncX5Dforward( "B".as_ptr(), 1, result2.as_mut_ptr().cast(), @@ -50,7 +50,7 @@ fn main() { let mut result3: [usize; 2] = [0, 0]; let handle3 = unsafe { - X5BasyncX5DtestX3AtestX2Fstring_delayX00forward( + testX3AtestX2Fstring_delayX00X5BasyncX5Dforward( "C".as_ptr(), 1, result3.as_mut_ptr().cast(), From 97db7e31174103a514521e6fc9e3cd6502537442 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 9 Mar 2025 17:17:26 +0100 Subject: [PATCH 500/672] a function returning a stream is likely not async --- crates/cpp/tests/symmetric_async/generate.sh | 1 + crates/cpp/tests/symmetric_stream/generate.sh | 3 +- .../tests/symmetric_stream/main/src/main.rs | 12 +- .../tests/symmetric_stream/source/src/lib.rs | 5 +- .../tests/symmetric_stream/stream/src/lib.rs | 4 +- .../stream/src/stream_world.rs | 103 +++++++----------- .../src/async_support/stream_support.rs | 4 + 7 files changed, 55 insertions(+), 77 deletions(-) diff --git a/crates/cpp/tests/symmetric_async/generate.sh b/crates/cpp/tests/symmetric_async/generate.sh index 575b0c87b..ac4e4230c 100755 --- a/crates/cpp/tests/symmetric_async/generate.sh +++ b/crates/cpp/tests/symmetric_async/generate.sh @@ -1,2 +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_stream/generate.sh b/crates/cpp/tests/symmetric_stream/generate.sh index 583a3096d..21107d649 100755 --- a/crates/cpp/tests/symmetric_stream/generate.sh +++ b/crates/cpp/tests/symmetric_stream/generate.sh @@ -1,2 +1,3 @@ #!/bin/sh -(cd stream/src ; ../../../../../../target/debug/wit-bindgen rust ../../wit/async_stream.wit --async all --symmetric) +(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/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index b2586c4a5..47836dd89 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -6,7 +6,7 @@ use wit_bindgen_symmetric_rt::{ #[link(name = "stream")] extern "C" { - pub fn testX3AtestX2Fstream_testX00X5BasyncX5Dcreate(results: *mut ()) -> *mut (); + pub fn testX3AtestX2Fstream_testX00create() -> usize; } const DATALEN: usize = 2; @@ -34,13 +34,13 @@ extern "C" fn ready(arg: *mut ()) -> CallbackState { } fn main() { - let mut result_stream: *mut () = core::ptr::null_mut(); - let continuation = unsafe { - testX3AtestX2Fstream_testX00X5BasyncX5Dcreate((&mut result_stream as *mut *mut ()).cast()) + // 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 as usize) }; + // assert!(continuation.is_null()); + let stream = unsafe { Stream::from_handle(result_stream) }; let mut info = Box::pin(CallbackInfo { stream: stream.clone(), data: [0, 0], diff --git a/crates/cpp/tests/symmetric_stream/source/src/lib.rs b/crates/cpp/tests/symmetric_stream/source/src/lib.rs index 3f756aa11..b837dd7c6 100644 --- a/crates/cpp/tests/symmetric_stream/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/source/src/lib.rs @@ -39,11 +39,10 @@ extern "C" fn write_ready(data: *mut ()) -> CallbackState { #[allow(non_snake_case)] #[no_mangle] -pub fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(results: *mut u8) -> *mut u8 { +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 ()); - *unsafe { &mut *results.cast::() } = stream.take_handle(); - std::ptr::null_mut() + stream.take_handle() } diff --git a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs index d7333ca0e..88a10e84b 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs @@ -9,9 +9,9 @@ stream_world::export!(MyStruct with_types_in stream_world); struct MyStruct; impl stream_world::exports::test::test::stream_test::Guest for MyStruct { - async fn create() -> async_support::StreamReader { + fn create() -> async_support::StreamReader { let (mut writer, reader) = async_support::stream_support::new_stream(); - let mut input = create().await; + let mut input = create(); async_support::spawn(async move { while let Some(values) = input.next().await { diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 8dc04ef5d..1c1b23a0b 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -1,42 +1,30 @@ -// Generated by `wit-bindgen` 0.36.0. DO NOT EDIT! +// 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, clippy::all)] + #[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 wit_bindgen_symmetric_rt::async_support::Stream; - use super::super::super::_rt; - #[allow(unused_unsafe, clippy::all)] - pub async fn create() -> wit_bindgen_symmetric_rt::async_support::StreamReader { + pub fn create() -> wit_bindgen_symmetric_rt::async_support::StreamReader { unsafe { - let layout1 = _rt::alloc::Layout::from_size_align_unchecked(8, 8); - let ptr1 = _rt::alloc::alloc(layout1); - #[link(wasm_import_module = "test:test/stream-source")] #[link(name = "source")] extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "[async]create")] - fn testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(_: *mut u8) -> *mut u8; + #[cfg_attr(target_arch = "wasm32", link_name = "create")] + fn testX3AtestX2Fstream_sourceX00create() -> *mut u8; } - ::wit_bindgen_symmetric_rt::async_support::await_result(move || unsafe { - testX3AtestX2Fstream_sourceX00X5BasyncX5Dcreate(ptr1) - }) - .await; - let l3 = *ptr1.add(0).cast::<*mut u8>(); - let result4 = wit_bindgen_symmetric_rt::async_support::StreamReader::new( - Stream::from_handle(l3 as usize), - ); - _rt::cabi_dealloc(ptr1, 8, 8); - result4 + let ret = testX3AtestX2Fstream_sourceX00create(); + wit_bindgen_symmetric_rt::async_support::StreamReader::new( + wit_bindgen_symmetric_rt::async_support::stream_support::Stream::from_handle(ret as usize), + ) } } } @@ -47,33 +35,24 @@ pub mod exports { pub mod test { pub mod test { - #[allow(dead_code, clippy::all)] + #[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(results: *mut u8) -> *mut u8 { + pub unsafe fn _export_create_cabi() -> usize { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); let result0 = T::create(); - let result = ::wit_bindgen_symmetric_rt::async_support::first_poll( - result0, - move |result1| { - let outptr = results.cast::<*mut ()>(); - *(unsafe { &mut *outptr }) = result1.into_handle().cast(); - }, - ); - - result.cast() + (result0).take_handle() } pub trait Guest { - fn create() -> impl ::core::future::Future< - Output = ::wit_bindgen_symmetric_rt::async_support::StreamReader, - > + 'static; + fn create() -> wit_bindgen_symmetric_rt::async_support::StreamReader; } #[doc(hidden)] @@ -82,8 +61,8 @@ pub mod exports { #[cfg_attr(target_arch = "wasm32", export_name = "create")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn testX3AtestX2Fstream_testX00X5BasyncX5Dcreate(results: *mut u8) -> *mut u8 { - $($path_to_types)*::_export_create_cabi::<$ty>(results) + unsafe extern "C" fn testX3AtestX2Fstream_testX00create() -> usize { + $($path_to_types)*::_export_create_cabi::<$ty>() } };); } @@ -94,40 +73,34 @@ pub mod exports { } } mod _rt { + #![allow(dead_code, clippy::all)] pub use alloc_crate::alloc; - 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); - } + pub use alloc_crate::boxed::Box; #[cfg(target_arch = "wasm32")] pub fn run_ctors_once() { - wit_bindgen::rt::run_ctors_once(); + wit_bindgen_symmetric_rt::run_ctors_once(); } extern crate alloc as alloc_crate; } pub mod wit_stream { - #![allow(dead_code, clippy::all)] - - use wit_bindgen_symmetric_rt::async_support::stream_support::new_stream; - - pub trait StreamPayload: Unpin + Sized + 'static {} - - impl StreamPayload for u32 {} + #![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() -> ( - ::wit_bindgen_symmetric_rt::async_support::StreamWriter, - ::wit_bindgen_symmetric_rt::async_support::StreamReader, + wit_bindgen_symmetric_rt::async_support::StreamWriter, + wit_bindgen_symmetric_rt::async_support::StreamReader, ) { - new_stream() + wit_bindgen_symmetric_rt::async_support::stream_support::new_stream() } } -/// Generates `#[no_mangle]` functions to export the specified type as the -/// root implementation of all generated traits. +/// 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!`. /// @@ -155,16 +128,16 @@ macro_rules! __export_stream_world_impl { pub(crate) use __export_stream_world_impl as export; #[cfg(target_arch = "wasm32")] -#[link_section = "component-type:wit-bindgen:0.36.0:test:test:stream-world:encoded world"] +#[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; 262] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x83\x01\x01A\x02\x01\ -A\x04\x01B\x03\x01fy\x01@\0\0\0\x04\0\x06create\x01\x01\x03\0\x17test:test/strea\ -m-source\x05\0\x01B\x03\x01fy\x01@\0\0\0\x04\0\x06create\x01\x01\x04\0\x15test:t\ -est/stream-test\x05\x01\x04\0\x16test:test/stream-world\x04\0\x0b\x12\x01\0\x0cs\ -tream-world\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x07\ -0.221.2\x10wit-bindgen-rust\x060.36.0"; +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)] 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 index a79e20a55..467289ff6 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -138,6 +138,10 @@ impl StreamReader { assert!(self.future.is_some()); self.future = None; } + + pub fn take_handle(&self) -> usize { + self.handle.take_handle() + } } impl fmt::Debug for StreamReader { From 93b91e1e85cfd6b841f368e0d8ed8900ace22352 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 9 Mar 2025 17:55:36 +0100 Subject: [PATCH 501/672] make create non-async --- .../future/src/future_world.rs | 253 ++---------------- .../tests/symmetric_future/future/src/lib.rs | 6 +- crates/cpp/tests/symmetric_future/generate.sh | 3 +- .../tests/symmetric_future/main/src/main.rs | 11 +- .../tests/symmetric_future/source/src/lib.rs | 5 +- 5 files changed, 37 insertions(+), 241 deletions(-) diff --git a/crates/cpp/tests/symmetric_future/future/src/future_world.rs b/crates/cpp/tests/symmetric_future/future/src/future_world.rs index 2fdc04477..4d075cb46 100644 --- a/crates/cpp/tests/symmetric_future/future/src/future_world.rs +++ b/crates/cpp/tests/symmetric_future/future/src/future_world.rs @@ -1,4 +1,4 @@ -// Generated by `wit-bindgen` 0.38.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.40.0. DO NOT EDIT! // Options used: #[allow(dead_code, clippy::all)] pub mod test { @@ -11,41 +11,20 @@ pub mod test { static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; - use wit_bindgen_symmetric_rt::async_support::Stream; - use super::super::super::_rt; #[allow(unused_unsafe, clippy::all)] - pub async fn create() -> wit_bindgen_symmetric_rt::async_support::FutureReader { + pub fn create() -> wit_bindgen_symmetric_rt::async_support::FutureReader { unsafe { - // let layout0 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); - // let ptr0 = _rt::alloc::alloc(layout0); - let layout1 = _rt::alloc::Layout::from_size_align_unchecked( - core::mem::size_of::<*const u8>(), - core::mem::size_of::<*const u8>(), - ); - let ptr1 = _rt::alloc::alloc(layout1); - #[link(wasm_import_module = "test:test/future-source")] #[link(name = "source")] extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "[async]create")] - fn testX3AtestX2Ffuture_sourceX00X5BasyncX5Dcreate(_: *mut u8) -> *mut u8; + #[cfg_attr(target_arch = "wasm32", link_name = "create")] + fn testX3AtestX2Ffuture_sourceX00create() -> usize; } - // let layout2 = _rt::alloc::Layout::from_size_align_unchecked(0, 1); - wit_bindgen_symmetric_rt::async_support::await_result(move || unsafe { - testX3AtestX2Ffuture_sourceX00X5BasyncX5Dcreate(ptr1) - }) - .await; - let l3 = *ptr1.add(0).cast::<*mut u8>(); - let result4 = wit_bindgen_symmetric_rt::async_support::FutureReader::new( - Stream::from_handle(l3 as usize), - ); - _rt::cabi_dealloc( - ptr1, - core::mem::size_of::<*const u8>(), - core::mem::size_of::<*const u8>(), - ); - result4 + let ret = testX3AtestX2Ffuture_sourceX00create(); + wit_bindgen_symmetric_rt::async_support::FutureReader::new( + wit_bindgen_symmetric_rt::async_support::Stream::from_handle(ret), + ) } } } @@ -66,29 +45,14 @@ pub mod exports { use super::super::super::super::_rt; #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_create_cabi(results: *mut u8) -> *mut u8 { + pub unsafe fn _export_create_cabi() -> usize { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); - let result = async move { - let result = T::create().await; - result - }; - let result = wit_bindgen_symmetric_rt::async_support::first_poll( - result, - move |result0| { - let outptr = results.cast::<*mut ()>(); - *(unsafe { &mut *outptr }) = result0.take_handle().cast(); - }, - ); - result.cast() + let result0 = T::create(); + (result0).take_handle() as usize } - // #[doc(hidden)] - // #[allow(non_snake_case)] - // pub unsafe fn __callback_create(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { - // wit_bindgen_symmetric_rt::async_support::callback(ctx, event0, event1, event2) - // } pub trait Guest { - async fn create() -> wit_bindgen_symmetric_rt::async_support::FutureReader; + fn create() -> wit_bindgen_symmetric_rt::async_support::FutureReader; } #[doc(hidden)] @@ -97,13 +61,9 @@ pub mod exports { #[cfg_attr(target_arch = "wasm32", export_name = "create")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn testX3AtestX2Ffuture_testX00X5BasyncX5Dcreate(results: *mut u8) -> *mut u8 { - $($path_to_types)*::_export_create_cabi::<$ty>(results) + unsafe extern "C" fn testX3AtestX2Ffuture_testX00create() -> usize { + $($path_to_types)*::_export_create_cabi::<$ty>() } - // #[unsafe(export_name = "[callback]create")] - // unsafe extern "C" fn _callback_create(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { - // $($path_to_types)*::__callback_create(ctx, event0, event1, event2) - // } };); } #[doc(hidden)] @@ -184,15 +144,7 @@ mod _rt { self as i32 } } - pub use alloc_crate::alloc; pub use alloc_crate::boxed::Box; - 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); - } #[cfg(target_arch = "wasm32")] pub fn run_ctors_once() { @@ -204,168 +156,16 @@ pub mod wit_future { #![allow(dead_code, unused_variables, clippy::all)] #[doc(hidden)] - pub trait FuturePayload: Unpin + Sized + 'static { - // fn new() -> u32; + pub trait FuturePayload: Unpin + Sized + 'static {} + impl FuturePayload for u32 {} + /// Creates a new Component Model `future` with the specified payload type. + pub fn new() -> ( + wit_bindgen_symmetric_rt::async_support::FutureWriter, + wit_bindgen_symmetric_rt::async_support::FutureReader, + ) { + wit_bindgen_symmetric_rt::async_support::future_support::new_future() } - // #[doc(hidden)] - // pub mod vtable0 { - // fn write(future: u32, value: u32) -> ::core::pin::Pin>> { - // super::super::_rt::Box::pin(async move { - // #[repr(align(4))] - // struct Buffer([::core::mem::MaybeUninit::; 4]); - // let mut buffer = Buffer([::core::mem::MaybeUninit::uninit(); 4]); - // let address = buffer.0.as_mut_ptr() as *mut u8; - // unsafe { *address.add(0).cast::() = super::super::_rt::as_i32(&value); - // } - // #[link(wasm_import_module = "[import-payload]test:test/future-source")] - // extern "C" { - // #[link_name = "[async][future-write-0]create"] - // fn wit_import(_: u32, _: *mut u8) -> u32; - // } - - // unsafe { wit_bindgen_symmetric_rt::async_support::await_future_result(wit_import, future, address).await } - // }) - // } - - // fn read(future: u32) -> ::core::pin::Pin>>> { - // super::super::_rt::Box::pin(async move { - // struct Buffer([::core::mem::MaybeUninit::; 4]); - // let mut buffer = Buffer([::core::mem::MaybeUninit::uninit(); 4]); - // let address = buffer.0.as_mut_ptr() as *mut u8; - - // #[cfg(not(target_arch = "wasm32"))] - // unsafe extern "C" fn wit_import(_: u32, _: *mut u8) -> u32 { - // unreachable!() - // } - - // #[cfg(target_arch = "wasm32")] - // #[link(wasm_import_module = "[import-payload]test:test/future-source")] - // extern "C" { - // #[link_name = "[async][future-read-0]create"] - // fn wit_import(_: u32, _: *mut u8) -> u32; - // } - - // if unsafe { wit_bindgen_symmetric_rt::async_support::await_future_result(wit_import, future, address).await } { - // let value = unsafe { let l0 = *address.add(0).cast::(); - - // l0 as u32 }; - // Some(value) - // } else { - // None - // } - // }) - // } - - // fn cancel_write(writer: u32) { - // #[cfg(not(target_arch = "wasm32"))] - // { - // unreachable!(); - // } - - // #[cfg(target_arch = "wasm32")] - // { - // #[link(wasm_import_module = "[import-payload]test:test/future-source")] - // extern "C" { - // #[link_name = "[future-cancel-write-0]create"] - // fn cancel(_: u32) -> u32; - // } - // unsafe { cancel(writer) }; - // } - // } - - // fn cancel_read(reader: u32) { - // #[cfg(not(target_arch = "wasm32"))] - // { - // unreachable!(); - // } - - // #[cfg(target_arch = "wasm32")] - // { - // #[link(wasm_import_module = "[import-payload]test:test/future-source")] - // extern "C" { - // #[link_name = "[future-cancel-read-0]create"] - // fn cancel(_: u32) -> u32; - // } - // unsafe { cancel(reader) }; - // } - // } - - // fn close_writable(writer: u32) { - // #[cfg(not(target_arch = "wasm32"))] - // { - // unreachable!(); - // } - - // #[cfg(target_arch = "wasm32")] - // { - // #[link(wasm_import_module = "[import-payload]test:test/future-source")] - // extern "C" { - // #[link_name = "[future-close-writable-0]create"] - // fn drop(_: u32, _: u32); - // } - // unsafe { drop(writer, 0) } - // } - // } - - // fn close_readable(reader: u32) { - // #[cfg(not(target_arch = "wasm32"))] - // { - // unreachable!(); - // } - - // #[cfg(target_arch = "wasm32")] - // { - // #[link(wasm_import_module = "[import-payload]test:test/future-source")] - // extern "C" { - // #[link_name = "[future-close-readable-0]create"] - // fn drop(_: u32); - // } - // unsafe { drop(reader) } - // } - // } - - // pub static VTABLE: wit_bindgen_symmetric_rt::async_support::FutureVtable = wit_bindgen_symmetric_rt::async_support::FutureVtable:: { - // write, read, cancel_write, cancel_read, close_writable, close_readable - // }; - - impl FuturePayload for u32 { - // fn new() -> (u32, &'static wit_bindgen_symmetric_rt::async_support::FutureVtable) { - // #[cfg(not(target_arch = "wasm32"))] - // { - // unreachable!(); - // } - - // #[cfg(target_arch = "wasm32")] - // { - // #[link(wasm_import_module = "[import-payload]test:test/future-source")] - // extern "C" { - // #[link_name = "[future-new-0]create"] - // fn new() -> u32; - // } - // (unsafe { new() }, &VTABLE) - // } - // } - } -} -/// Creates a new Component Model `future` with the specified payload type. -pub fn new() -> ( - wit_bindgen_symmetric_rt::async_support::FutureWriter, - wit_bindgen_symmetric_rt::async_support::FutureReader, -) { - new_future() - // let (handle, vtable) = T::new(); - // wit_bindgen_symmetric_rt::async_support::with_entry(handle, |entry| match entry { - // ::std::collections::hash_map::Entry::Vacant(entry) => { - // entry.insert(wit_bindgen_symmetric_rt::async_support::Handle::LocalOpen); - // } - // ::std::collections::hash_map::Entry::Occupied(_) => unreachable!(), - // }); - // ( - // wit_bindgen_symmetric_rt::async_support::FutureWriter::new(handle, vtable), - // wit_bindgen_symmetric_rt::async_support::FutureReader::new(handle, vtable), - // ) } -// } /// Generates `#[unsafe(no_mangle)]` functions to export the specified type as /// the root implementation of all generated traits. @@ -394,19 +194,18 @@ macro_rules! __export_future_world_impl { } #[doc(inline)] pub(crate) use __export_future_world_impl as export; -use wit_bindgen_symmetric_rt::async_support::future_support::new_future; #[cfg(target_arch = "wasm32")] -#[unsafe(link_section = "component-type:wit-bindgen:0.38.0:test:test:future-world:encoded world")] +#[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\x01g\x01y\x01@\0\0\0\x04\0\x06create\x01\x01\x03\0\x17test:test/f\ -uture-source\x05\0\x01B\x03\x01g\x01y\x01@\0\0\0\x04\0\x06create\x01\x01\x04\0\x15\ +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.223.0\x10wit-bindgen-rust\x060.38.0"; +t\x070.227.1\x10wit-bindgen-rust\x060.40.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/cpp/tests/symmetric_future/future/src/lib.rs b/crates/cpp/tests/symmetric_future/future/src/lib.rs index 22d26ff83..1ca28dcfd 100644 --- a/crates/cpp/tests/symmetric_future/future/src/lib.rs +++ b/crates/cpp/tests/symmetric_future/future/src/lib.rs @@ -1,6 +1,6 @@ mod future_world; -use future_world::test::test::future_source::create; +use future_world::test::test::future_source; use wit_bindgen_symmetric_rt::async_support; future_world::export!(MyStruct with_types_in future_world); @@ -8,9 +8,9 @@ future_world::export!(MyStruct with_types_in future_world); struct MyStruct; impl future_world::exports::test::test::future_test::Guest for MyStruct { - async fn create() -> async_support::FutureReader { + fn create() -> async_support::FutureReader { let (write, read) = async_support::future_support::new_future(); - let input = create().await; + let input = future_source::create(); async_support::spawn(async move { let input = input.await.unwrap(); write.write(input * 2).await; diff --git a/crates/cpp/tests/symmetric_future/generate.sh b/crates/cpp/tests/symmetric_future/generate.sh index db380eb5a..43a301f69 100755 --- a/crates/cpp/tests/symmetric_future/generate.sh +++ b/crates/cpp/tests/symmetric_future/generate.sh @@ -1,2 +1,3 @@ #!/bin/sh -(cd future/src ; ../../../../../../target/debug/wit-bindgen rust ../../wit/future.wit --async all --symmetric) +(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/src/main.rs b/crates/cpp/tests/symmetric_future/main/src/main.rs index 678e45e04..55846d1f7 100644 --- a/crates/cpp/tests/symmetric_future/main/src/main.rs +++ b/crates/cpp/tests/symmetric_future/main/src/main.rs @@ -6,7 +6,7 @@ use wit_bindgen_symmetric_rt::{ #[link(name = "future")] extern "C" { - pub fn testX3AtestX2Ffuture_testX00X5BasyncX5Dcreate(results: *mut ()) -> *mut (); + pub fn testX3AtestX2Ffuture_testX00create() -> usize; } struct CallbackInfo { @@ -28,13 +28,10 @@ extern "C" fn ready(arg: *mut ()) -> CallbackState { } fn main() { - let mut result_future: *mut () = core::ptr::null_mut(); - let continuation = unsafe { - testX3AtestX2Ffuture_testX00X5BasyncX5Dcreate((&mut result_future as *mut *mut ()).cast()) - }; + let result_future = unsafe { testX3AtestX2Ffuture_testX00create() }; // function should have completed (not async) - assert!(continuation.is_null()); - let stream = unsafe { Stream::from_handle(result_future as usize) }; + // assert!(continuation.is_null()); + let stream = unsafe { Stream::from_handle(result_future) }; let mut info = Box::pin(CallbackInfo { stream: stream.clone(), data: 0, diff --git a/crates/cpp/tests/symmetric_future/source/src/lib.rs b/crates/cpp/tests/symmetric_future/source/src/lib.rs index 90f162e33..832dcb40f 100644 --- a/crates/cpp/tests/symmetric_future/source/src/lib.rs +++ b/crates/cpp/tests/symmetric_future/source/src/lib.rs @@ -22,11 +22,10 @@ extern "C" fn write_ready(data: *mut ()) -> CallbackState { #[allow(non_snake_case)] #[no_mangle] -pub fn testX3AtestX2Ffuture_sourceX00X5BasyncX5Dcreate(results: *mut u8) -> *mut u8 { +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 ()); - *unsafe { &mut *results.cast::() } = stream.take_handle(); - std::ptr::null_mut() + stream.take_handle() } From 0a3d251994cd95e2b5bb5e2972f83b3a69e23e02 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 9 Mar 2025 18:01:55 +0100 Subject: [PATCH 502/672] fix c++ name --- crates/cpp/tests/symmetric_async/async_cpp/src/async_module.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 88c74dfe7..c10c0d327 100644 --- a/crates/cpp/tests/symmetric_async/async_cpp/src/async_module.cpp +++ b/crates/cpp/tests/symmetric_async/async_cpp/src/async_module.cpp @@ -52,7 +52,7 @@ static symmetric::runtime::symmetric_executor::CallbackState wait_on_future(std: } extern "C" -void* X5BasyncX5DtestX3AtestX2Fstring_delayX00forward(uint8_t* arg0, size_t arg1, uint8_t* arg2) +void* testX3AtestX2Fstring_delayX00X5BasyncX5Dforward(uint8_t* arg0, size_t arg1, uint8_t* arg2) { auto len0 = arg1; From b3bba171e137e7953ff764599c85467316d46a4e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 9 Mar 2025 18:32:17 +0100 Subject: [PATCH 503/672] introduce from_handle (closer to canonical code) --- .../tests/symmetric_stream/main/src/main.rs | 4 +-- .../stream/src/stream_world.rs | 12 +++---- .../src/async_support/stream_support.rs | 33 +++++++++++-------- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index 47836dd89..ac43fe073 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -35,9 +35,7 @@ extern "C" fn ready(arg: *mut ()) -> CallbackState { fn main() { // let mut result_stream: *mut () = core::ptr::null_mut(); - let result_stream = unsafe { - testX3AtestX2Fstream_testX00create() - }; + let result_stream = unsafe { testX3AtestX2Fstream_testX00create() }; // function should have completed (not async) // assert!(continuation.is_null()); let stream = unsafe { Stream::from_handle(result_stream) }; diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 1c1b23a0b..f51a715f6 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -19,12 +19,10 @@ pub mod test { #[link(name = "source")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "create")] - fn testX3AtestX2Fstream_sourceX00create() -> *mut u8; + fn testX3AtestX2Fstream_sourceX00create() -> usize; } let ret = testX3AtestX2Fstream_sourceX00create(); - wit_bindgen_symmetric_rt::async_support::StreamReader::new( - wit_bindgen_symmetric_rt::async_support::stream_support::Stream::from_handle(ret as usize), - ) + wit_bindgen_symmetric_rt::async_support::StreamReader::from_handle(ret) } } } @@ -86,10 +84,8 @@ mod _rt { pub mod wit_stream { #![allow(dead_code, unused_variables, clippy::all)] - pub trait StreamPayload: Unpin + Sized + 'static { - } - impl StreamPayload for u32 { - } + pub trait StreamPayload: Unpin + Sized + 'static {} + impl StreamPayload for u32 {} /// Creates a new Component Model `stream` with the specified payload type. pub fn new() -> ( wit_bindgen_symmetric_rt::async_support::StreamWriter, 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 index 467289ff6..e405d72cc 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -130,20 +130,6 @@ pub struct StreamReader { _phantom: PhantomData, } -impl StreamReader { - /// 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; - } - - pub fn take_handle(&self) -> usize { - self.handle.take_handle() - } -} - impl fmt::Debug for StreamReader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("StreamReader") @@ -161,7 +147,26 @@ impl StreamReader { _phantom: PhantomData, } } + + pub unsafe fn from_handle(handle: usize) -> Self { + Self::new(unsafe { Stream::from_handle(handle) }) + } + + /// 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 () } From 9704aa149145457179dd60184edf56b204fc7939 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 9 Mar 2025 23:08:35 +0100 Subject: [PATCH 504/672] stream code generation for symmetric --- crates/core/src/abi.rs | 5 +- .../stream/src/stream_world.rs | 8 ++-- crates/rust/src/bindgen.rs | 38 ++++++++++----- crates/rust/src/interface.rs | 20 +++++--- crates/rust/src/lib.rs | 46 +++++++++++++++---- .../src/async_support/stream_support.rs | 4 +- 6 files changed, 88 insertions(+), 33 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 826ec188c..4ff8a9c09 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -2223,7 +2223,10 @@ fn cast(from: WasmType, to: WasmType) -> Bitcast { fn push_flat_symmetric(resolve: &Resolve, ty: &Type, vec: &mut Vec) { if let Type::Id(id) = ty { - if matches!(&resolve.types[*id].kind, TypeDefKind::Handle(_)) { + if matches!( + &resolve.types[*id].kind, + TypeDefKind::Handle(_) | TypeDefKind::Stream(_) | TypeDefKind::Future(_) + ) { vec.push(WasmType::Pointer); } else { resolve.push_flat(ty, vec); diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index f51a715f6..62b91b0ab 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -19,7 +19,7 @@ pub mod test { #[link(name = "source")] extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "create")] - fn testX3AtestX2Fstream_sourceX00create() -> usize; + fn testX3AtestX2Fstream_sourceX00create() -> *mut u8; } let ret = testX3AtestX2Fstream_sourceX00create(); wit_bindgen_symmetric_rt::async_support::StreamReader::from_handle(ret) @@ -43,7 +43,7 @@ pub mod exports { use super::super::super::super::_rt; #[doc(hidden)] #[allow(non_snake_case)] - pub unsafe fn _export_create_cabi() -> usize { + pub unsafe fn _export_create_cabi() -> *mut u8 { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); let result0 = T::create(); @@ -59,7 +59,7 @@ pub mod exports { #[cfg_attr(target_arch = "wasm32", export_name = "create")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn testX3AtestX2Fstream_testX00create() -> usize { + unsafe extern "C" fn testX3AtestX2Fstream_testX00create() -> *mut u8 { $($path_to_types)*::_export_create_cabi::<$ty>() } };); @@ -77,7 +77,7 @@ mod _rt { #[cfg(target_arch = "wasm32")] pub fn run_ctors_once() { - wit_bindgen_symmetric_rt::run_ctors_once(); + wit_bindgen::rt::run_ctors_once(); } extern crate alloc as alloc_crate; } diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 50b677c8f..b20d6d15a 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -521,7 +521,11 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::FutureLower { .. } => { let op = &operands[0]; - results.push(format!("({op}).take_handle() as i32")) + if self.gen.gen.opts.symmetric { + results.push(format!("({op}).take_handle()")) + } else { + results.push(format!("({op}).take_handle() as i32")) + } } Instruction::FutureLift { payload, .. } => { @@ -535,16 +539,24 @@ impl Bindgen for FunctionBindgen<'_, '_> { }) .unwrap_or_else(|| "()".into()); let ordinal = self.gen.gen.future_payloads.get_index_of(&name).unwrap(); - let path = self.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.gen.gen.opts.symmetric { + results.push(format!("{async_support}::FutureReader::from_handle({op})")) + } else { + let path = self.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.gen.gen.opts.symmetric { + results.push(format!("({op}).take_handle()")) + } else { + results.push(format!("({op}).take_handle() as i32")) + } } Instruction::StreamLift { payload, .. } => { @@ -558,11 +570,15 @@ impl Bindgen for FunctionBindgen<'_, '_> { }) .unwrap_or_else(|| "()".into()); let ordinal = self.gen.gen.stream_payloads.get_index_of(&name).unwrap(); - let path = self.gen.path_to_root(); - results.push(format!( - "{async_support}::StreamReader::from_handle_and_vtable\ + if self.gen.gen.opts.symmetric { + results.push(format!("{async_support}::StreamReader::from_handle({op})")) + } else { + let path = self.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 { .. } => { diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index fa3054a5e..887d4962c 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -600,8 +600,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_}>> {{ @@ -688,7 +691,8 @@ pub mod vtable{ordinal} {{ }} }} "#, - ); + ) + }; self.gen.future_payloads.insert(name, code); } @@ -772,8 +776,11 @@ 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_} + '_>> {{ @@ -879,7 +886,8 @@ pub mod vtable{ordinal} {{ }} }} "#, - ); + ) + }; self.gen.stream_payloads.insert(name, code); } diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index c3d4db368..2ab50f37b 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -435,7 +435,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( @@ -500,15 +504,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; + " + ) + }; + let construct = if self.opts.symmetric { + format!("{async_support}::future_support::new_future()") + } else { + format!("unsafe {{ {async_support}::future_new::(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; - }}" + pub trait FuturePayload: Unpin + Sized + 'static {{{vtable_def}}}" )); for code in self.future_payloads.values() { self.src.push_str(code); @@ -517,7 +533,7 @@ pub mod wit_future {{ "\ /// Creates a new Component Model `future` with the specified payload type. pub fn new() -> ({async_support}::FutureWriter, {async_support}::FutureReader) {{ - unsafe {{ {async_support}::future_new::(T::VTABLE) }} + {construct} }} }} ", @@ -526,14 +542,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; + " + ) + }; + let construct = if self.opts.symmetric { + format!("{async_support}::stream_support::new_stream()") + } else { + format!("unsafe {{ {async_support}::stream_new::(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; - }}" + pub trait StreamPayload: Unpin + Sized + 'static {{{vtable_def}}}" )); for code in self.stream_payloads.values() { self.src.push_str(code); @@ -542,7 +570,7 @@ pub mod wit_stream {{ &format!("\ /// Creates a new Component Model `stream` with the specified payload type. pub fn new() -> ({async_support}::StreamWriter, {async_support}::StreamReader) {{ - unsafe {{ {async_support}::stream_new::(T::VTABLE) }} + {construct} }} }} "), 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 index e405d72cc..a4c15d786 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -148,8 +148,8 @@ impl StreamReader { } } - pub unsafe fn from_handle(handle: usize) -> Self { - Self::new(unsafe { Stream::from_handle(handle) }) + pub unsafe fn from_handle(handle: *mut u8) -> Self { + Self::new(unsafe { Stream::from_handle(handle as usize) }) } /// Cancel the current pending read operation. From c2edf55adc888d4967c9e8866111c1bd8df2c98e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 9 Mar 2025 23:09:45 +0100 Subject: [PATCH 505/672] fix compilation --- crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 62b91b0ab..705ebcecd 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -47,7 +47,7 @@ pub mod exports { #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); let result0 = T::create(); - (result0).take_handle() + (result0).take_handle() as *mut u8 } pub trait Guest { fn create() -> wit_bindgen_symmetric_rt::async_support::StreamReader; From 00e8ed7a6d6bc4a946ae65ed50b24b287d6a4e3a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 9 Mar 2025 23:15:04 +0100 Subject: [PATCH 506/672] fully operational generator --- crates/rust/src/bindgen.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index b20d6d15a..04987291c 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -522,7 +522,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::FutureLower { .. } => { let op = &operands[0]; if self.gen.gen.opts.symmetric { - results.push(format!("({op}).take_handle()")) + results.push(format!("({op}).take_handle() as *mut u8")) } else { results.push(format!("({op}).take_handle() as i32")) } @@ -553,7 +553,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { Instruction::StreamLower { .. } => { let op = &operands[0]; if self.gen.gen.opts.symmetric { - results.push(format!("({op}).take_handle()")) + results.push(format!("({op}).take_handle() as *mut u8")) } else { results.push(format!("({op}).take_handle() as i32")) } From 68ce1b738f2031f86fc295acedf931e77c13b552 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 16 Mar 2025 16:27:52 +0100 Subject: [PATCH 507/672] fix return pointer usage --- crates/core/src/abi.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index a9eef3bd9..da8f5b8b8 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -965,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); @@ -995,9 +997,9 @@ impl<'a, B: Bindgen> Generator<'a, B> { }; if matches!(self.lift_lower, LiftLower::Symmetric) && sig.retptr { - // probably wrong? - let ptr = self.stack.pop().unwrap(); - self.read_results_from_memory(&func.result, ptr.clone(), Default::default()); + 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 From 827a35df5f907be6d09347ba2e405c196eea1b39 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 16 Mar 2025 17:03:12 +0100 Subject: [PATCH 508/672] remove merge artifacts to shrink the difference to main --- crates/c/src/lib.rs | 2 +- crates/core/Cargo.toml | 4 - crates/core/src/abi.rs | 31 +- crates/core/src/lib.rs | 231 ----- crates/guest-rust/rt/src/lib.rs | 2 +- crates/rust/src/async_support.rs | 918 ------------------- crates/rust/src/stream_and_future_support.rs | 765 ---------------- crates/test-helpers/codegen-macro/Cargo.toml | 2 +- 8 files changed, 15 insertions(+), 1940 deletions(-) delete mode 100644 crates/rust/src/async_support.rs delete mode 100644 crates/rust/src/stream_and_future_support.rs diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 22545be97..ac9fdccf3 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -556,7 +556,7 @@ impl C { fn push_type_name(&mut self, ty: &Type, dst: &mut String) { match ty { Type::Bool => dst.push_str("bool"), - Type::Char => dst.push_str("uint32_t"), + Type::Char => dst.push_str("uint32_t"), // TODO: better type? Type::U8 => dst.push_str("uint8_t"), Type::S8 => dst.push_str("int8_t"), Type::U16 => dst.push_str("uint16_t"), diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index bc476ea80..60935ef8d 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -18,7 +18,3 @@ doctest = false wit-parser = { workspace = true } anyhow = { workspace = true } heck = { workspace = true } - -[features] -default = ["async"] -async = [] diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index da8f5b8b8..5d596a79b 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -839,6 +839,7 @@ fn needs_post_return(resolve: &Resolve, ty: &Type) -> bool { TypeDefKind::Future(_) | TypeDefKind::Stream(_) => false, TypeDefKind::Unknown => unreachable!(), }, + Type::Bool | Type::U8 | Type::S8 @@ -894,7 +895,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { fn call_with_signature(&mut self, func: &Function, sig: WasmSignature) { const MAX_FLAT_PARAMS: usize = 16; - // const MAX_FLAT_RESULTS: usize = 1; let language_to_abi = matches!(self.lift_lower, LiftLower::LowerArgsLiftResults) || (matches!(self.lift_lower, LiftLower::Symmetric) @@ -994,11 +994,15 @@ impl<'a, B: Bindgen> Generator<'a, B> { sig: &sig, module_prefix: Default::default(), }); - }; + } 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()); + 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 @@ -1041,24 +1045,10 @@ impl<'a, B: Bindgen> Generator<'a, B> { }); } - // if let Some(results) = async_results { - // let name = &format!("[task-return]{}", func.name); - - // self.emit(&Instruction::AsyncCallReturn { - // name, - // params: &if results.len() > MAX_FLAT_PARAMS { - // vec![WasmType::Pointer] - // } else { - // results - // }, - // }); - // self.emit(&Instruction::Return { func, amt: 1 }); - // } else { self.emit(&Instruction::Return { func, amt: usize::from(func.result.is_some()), }); - // } } false => { if let (AbiVariant::GuestImport, true) = (self.variant, self.async_) { @@ -1074,6 +1064,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { offset += self_.bindgen.sizes().size(ty); } }; + if !sig.indirect_params { // If parameters are not passed indirectly then we lift each // argument in succession from the component wasm types that @@ -1484,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), @@ -2062,7 +2055,8 @@ impl<'a, B: Bindgen> Generator<'a, B> { | Type::U64 | Type::S64 | Type::F32 - | Type::F64 => {} + | Type::F64 + | Type::ErrorContext => {} Type::Id(id) => match &self.resolve.types[id].kind { TypeDefKind::Type(t) => self.deallocate(t, addr, offset), @@ -2133,7 +2127,6 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::Stream(_) => todo!("read stream from memory"), TypeDefKind::Unknown => unreachable!(), }, - Type::ErrorContext => todo!(), } } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 638441956..6752aae34 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -21,239 +21,8 @@ pub enum Direction { Export, } -// #[derive(Default)] -// pub struct Types { -// type_info: HashMap, -// } - -// #[derive(Default, Clone, Copy, Debug)] -// pub struct TypeInfo { -// /// Whether or not this type is ever used (transitively) within the -// /// parameter of an imported function. -// /// -// /// This means that it's used in a context where ownership isn't -// /// relinquished. -// pub borrowed: bool, - -// /// Whether or not this type is ever used (transitively) within the -// /// parameter or result of an export, or the result of an import. -// /// -// /// This means that it's used in a context where ownership is required and -// /// memory management is necessary. -// pub owned: bool, - -// /// Whether or not this type is ever used (transitively) within the -// /// error case in the result of a function. -// pub error: bool, - -// /// Whether or not this type (transitively) has a list (or string). -// pub has_list: bool, - -// /// Whether or not this type (transitively) has a resource (or handle). -// pub has_resource: bool, - -// /// Whether or not this type (transitively) has a borrow handle. -// pub has_borrow_handle: bool, - -// /// Whether or not this type (transitively) has an own handle. -// pub has_own_handle: bool, -// } - -// impl std::ops::BitOrAssign for TypeInfo { -// fn bitor_assign(&mut self, rhs: Self) { -// self.borrowed |= rhs.borrowed; -// self.owned |= rhs.owned; -// self.error |= rhs.error; -// self.has_list |= rhs.has_list; -// self.has_resource |= rhs.has_resource; -// self.has_borrow_handle |= rhs.has_borrow_handle; -// self.has_own_handle |= rhs.has_own_handle; -// } -// } - -// impl TypeInfo { -// pub fn is_clone(&self) -> bool { -// !self.has_resource -// } -// pub fn is_copy(&self) -> bool { -// !self.has_list && !self.has_resource -// } -// } - -// impl Types { -// pub fn analyze(&mut self, resolve: &Resolve) { -// for (t, _) in resolve.types.iter() { -// self.type_id_info(resolve, t); -// } -// for (_, world) in resolve.worlds.iter() { -// for (import, (_, item)) in world -// .imports -// .iter() -// .map(|i| (true, i)) -// .chain(world.exports.iter().map(|i| (false, i))) -// { -// match item { -// WorldItem::Function(f) => { -// self.type_info_func(resolve, f, import); -// } -// WorldItem::Interface(id) => { -// for (_, f) in resolve.interfaces[*id].functions.iter() { -// self.type_info_func(resolve, f, import); -// } -// } -// WorldItem::Type(_) => {} -// } -// } -// } -// } - -// fn type_info_func(&mut self, resolve: &Resolve, func: &Function, import: bool) { -// let mut live = LiveTypes::default(); -// for (_, ty) in func.params.iter() { -// self.type_info(resolve, ty); -// live.add_type(resolve, ty); -// } -// for id in live.iter() { -// if resolve.types[id].name.is_some() { -// let info = self.type_info.get_mut(&id).unwrap(); -// if import { -// info.borrowed = true; -// } else { -// info.owned = true; -// } -// } -// } -// let mut live = LiveTypes::default(); -// for ty in func.results.iter_types() { -// self.type_info(resolve, ty); -// live.add_type(resolve, ty); -// } -// for id in live.iter() { -// if resolve.types[id].name.is_some() { -// self.type_info.get_mut(&id).unwrap().owned = true; -// } -// } - -// for ty in func.results.iter_types() { -// let id = match ty { -// Type::Id(id) => *id, -// _ => continue, -// }; -// let err = match &resolve.types[id].kind { -// TypeDefKind::Result(Result_ { err, .. }) => err, -// _ => continue, -// }; -// if let Some(Type::Id(id)) = err { -// // When an interface `use`s a type from another interface, it creates a new typeid -// // referring to the definition typeid. Chase any chain of references down to the -// // typeid of the definition. -// fn resolve_type_definition_id(resolve: &Resolve, mut id: TypeId) -> TypeId { -// loop { -// match resolve.types[id].kind { -// TypeDefKind::Type(Type::Id(def_id)) => id = def_id, -// _ => return id, -// } -// } -// } -// let id = resolve_type_definition_id(resolve, *id); -// self.type_info.get_mut(&id).unwrap().error = true; -// } -// } -// } - -// pub fn get(&self, id: TypeId) -> TypeInfo { -// self.type_info[&id] -// } - -// pub fn type_id_info(&mut self, resolve: &Resolve, ty: TypeId) -> TypeInfo { -// if let Some(info) = self.type_info.get(&ty) { -// return *info; -// } -// let mut info = TypeInfo::default(); -// match &resolve.types[ty].kind { -// TypeDefKind::Record(r) => { -// for field in r.fields.iter() { -// info |= self.type_info(resolve, &field.ty); -// } -// } -// TypeDefKind::Resource => { -// info.has_resource = true; -// } -// TypeDefKind::Handle(handle) => { -// match handle { -// Handle::Borrow(_) => info.has_borrow_handle = true, -// Handle::Own(_) => info.has_own_handle = true, -// } -// info.has_resource = true; -// } -// TypeDefKind::Tuple(t) => { -// for ty in t.types.iter() { -// info |= self.type_info(resolve, ty); -// } -// } -// TypeDefKind::Flags(_) => {} -// TypeDefKind::Enum(_) => {} -// TypeDefKind::Variant(v) => { -// for case in v.cases.iter() { -// info |= self.optional_type_info(resolve, case.ty.as_ref()); -// } -// } -// TypeDefKind::List(ty) => { -// info = self.type_info(resolve, ty); -// info.has_list = true; -// } -// TypeDefKind::Type(ty) => { -// info = self.type_info(resolve, ty); -// } -// TypeDefKind::Option(ty) => { -// info = self.type_info(resolve, ty); -// } -// TypeDefKind::Result(r) => { -// info = self.optional_type_info(resolve, r.ok.as_ref()); -// info |= self.optional_type_info(resolve, r.err.as_ref()); -// } -// TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Error => {} -// TypeDefKind::Unknown => unreachable!(), -// } -// let prev = self.type_info.insert(ty, info); -// assert!(prev.is_none()); -// info -// } - -// pub fn type_info(&mut self, resolve: &Resolve, ty: &Type) -> TypeInfo { -// let mut info = TypeInfo::default(); -// match ty { -// Type::String => info.has_list = true, -// Type::Id(id) => return self.type_id_info(resolve, *id), -// _ => {} -// } -// info -// } - -// fn optional_type_info(&mut self, resolve: &Resolve, ty: Option<&Type>) -> TypeInfo { -// match ty { -// Some(ty) => self.type_info(resolve, ty), -// None => TypeInfo::default(), -// } -// } -// } - pub trait WorldGenerator { fn generate(&mut self, resolve: &Resolve, id: WorldId, files: &mut Files) -> Result<()> { - // TODO: Should we refine this test to inspect only types reachable from - // the specified world? - if !cfg!(feature = "async") - && resolve - .types - .iter() - .any(|(_, ty)| matches!(ty.kind, TypeDefKind::Future(_) | TypeDefKind::Stream(_))) - { - anyhow::bail!( - "must enable `async` feature when using WIT files \ - containing future, stream, or error types" - ); - } - let world = &resolve.worlds[id]; self.preprocess(resolve, id); diff --git a/crates/guest-rust/rt/src/lib.rs b/crates/guest-rust/rt/src/lib.rs index 3ba3ce088..0f2858362 100644 --- a/crates/guest-rust/rt/src/lib.rs +++ b/crates/guest-rust/rt/src/lib.rs @@ -1,4 +1,4 @@ -#![cfg_attr(not(feature = "async"), no_std)] +#![no_std] extern crate alloc; diff --git a/crates/rust/src/async_support.rs b/crates/rust/src/async_support.rs deleted file mode 100644 index f9bf9ed7c..000000000 --- a/crates/rust/src/async_support.rs +++ /dev/null @@ -1,918 +0,0 @@ -use { - futures::{ - channel::oneshot, - future::{self, FutureExt}, - sink::Sink, - stream::{FuturesUnordered, Stream, StreamExt}, - }, - once_cell::sync::Lazy, - std::{ - alloc::{self, Layout}, - any::Any, - collections::hash_map::Entry, - collections::HashMap, - convert::Infallible, - fmt::{self, Debug, Display}, - future::{Future, IntoFuture}, - iter, - marker::PhantomData, - mem::{self, ManuallyDrop, MaybeUninit}, - pin::{pin, Pin}, - ptr, - sync::Arc, - task::{Context, Poll, Wake, Waker}, - }, -}; - -type BoxFuture = Pin + 'static>>; - -struct FutureState { - todo: usize, - tasks: Option>, -} - -static mut CURRENT: *mut FutureState = ptr::null_mut(); - -static mut CALLS: Lazy>> = Lazy::new(HashMap::new); - -static mut SPAWNED: Vec = Vec::new(); - -enum Handle { - LocalOpen, - LocalReady(Box, Waker), - LocalWaiting(oneshot::Sender>), - LocalClosed, - Read, - Write, -} - -static mut HANDLES: Lazy> = Lazy::new(HashMap::new); - -fn dummy_waker() -> Waker { - struct DummyWaker; - - impl Wake for DummyWaker { - fn wake(self: Arc) {} - } - - static WAKER: Lazy> = Lazy::new(|| Arc::new(DummyWaker)); - - WAKER.clone().into() -} - -unsafe fn poll(state: *mut FutureState) -> Poll<()> { - loop { - if let Some(futures) = (*state).tasks.as_mut() { - CURRENT = state; - let poll = futures.poll_next_unpin(&mut Context::from_waker(&dummy_waker())); - CURRENT = ptr::null_mut(); - - if SPAWNED.is_empty() { - match poll { - Poll::Ready(Some(())) => (), - Poll::Ready(None) => { - (*state).tasks = None; - break Poll::Ready(()); - } - Poll::Pending => break Poll::Pending, - } - } else { - futures.extend(SPAWNED.drain(..)); - } - } else { - break Poll::Ready(()); - } - } -} - -pub fn first_poll( - future: impl Future + 'static, - fun: impl FnOnce(T) + 'static, -) -> *mut u8 { - let state = Box::into_raw(Box::new(FutureState { - todo: 0, - tasks: Some( - [Box::pin(future.map(fun)) as BoxFuture] - .into_iter() - .collect(), - ), - })); - match unsafe { poll(state) } { - Poll::Ready(()) => ptr::null_mut(), - Poll::Pending => state as _, - } -} - -pub async unsafe fn await_result( - import: unsafe extern "C" fn(*mut u8, *mut u8) -> i32, - params_layout: Layout, - params: *mut u8, - results: *mut u8, -) { - const STATUS_STARTING: u32 = 0; - const STATUS_STARTED: u32 = 1; - const STATUS_RETURNED: u32 = 2; - const STATUS_DONE: u32 = 3; - - let result = import(params, results) as u32; - let status = result >> 30; - let call = (result & !(0b11 << 30)) as i32; - - if status != STATUS_DONE { - assert!(!CURRENT.is_null()); - (*CURRENT).todo += 1; - } - - match status { - STATUS_STARTING => { - let (tx, rx) = oneshot::channel(); - CALLS.insert(call, tx); - rx.await.unwrap(); - alloc::dealloc(params, params_layout); - } - STATUS_STARTED => { - alloc::dealloc(params, params_layout); - let (tx, rx) = oneshot::channel(); - CALLS.insert(call, tx); - rx.await.unwrap(); - } - STATUS_RETURNED | STATUS_DONE => { - alloc::dealloc(params, params_layout); - } - _ => unreachable!(), - } -} - -mod results { - pub const BLOCKED: u32 = 0xffff_ffff; - pub const CLOSED: u32 = 0x8000_0000; - pub const CANCELED: u32 = 0; -} - -pub async unsafe fn await_future_result( - import: unsafe extern "C" fn(u32, *mut u8) -> u32, - future: u32, - address: *mut u8, -) -> bool { - let result = import(future, address); - match result { - results::BLOCKED => { - assert!(!CURRENT.is_null()); - (*CURRENT).todo += 1; - let (tx, rx) = oneshot::channel(); - CALLS.insert(future as _, tx); - let v = rx.await.unwrap(); - v == 1 - } - results::CLOSED | results::CANCELED => false, - 1 => true, - _ => unreachable!(), - } -} - -pub async unsafe fn await_stream_result( - import: unsafe extern "C" fn(u32, *mut u8, u32) -> u32, - stream: u32, - address: *mut u8, - count: u32, -) -> Option { - let result = import(stream, address, count); - match result { - results::BLOCKED => { - assert!(!CURRENT.is_null()); - (*CURRENT).todo += 1; - let (tx, rx) = oneshot::channel(); - CALLS.insert(stream as _, tx); - let v = rx.await.unwrap(); - if let results::CLOSED | results::CANCELED = v { - None - } else { - Some(usize::try_from(v).unwrap()) - } - } - results::CLOSED | results::CANCELED => None, - v => Some(usize::try_from(v).unwrap()), - } -} - -fn subtask_drop(subtask: u32) { - #[cfg(not(target_arch = "wasm32"))] - { - unreachable!(); - } - - #[cfg(target_arch = "wasm32")] - { - #[link(wasm_import_module = "$root")] - extern "C" { - #[link_name = "[subtask-drop]"] - fn subtask_drop(_: u32); - } - unsafe { - subtask_drop(subtask); - } - } -} - -pub unsafe fn callback(ctx: *mut u8, event0: i32, event1: i32, event2: i32) -> i32 { - const EVENT_CALL_STARTING: i32 = 0; - const EVENT_CALL_STARTED: i32 = 1; - const EVENT_CALL_RETURNED: i32 = 2; - const EVENT_CALL_DONE: i32 = 3; - const EVENT_YIELDED: i32 = 4; - const EVENT_STREAM_READ: i32 = 5; - const EVENT_STREAM_WRITE: i32 = 6; - const EVENT_FUTURE_READ: i32 = 7; - const EVENT_FUTURE_WRITE: i32 = 8; - - match event0 { - EVENT_CALL_STARTED => { - // TODO: could dealloc params here if we attached the pointer to the call - 0 - } - EVENT_CALL_RETURNED | EVENT_CALL_DONE | EVENT_STREAM_READ | EVENT_STREAM_WRITE - | EVENT_FUTURE_READ | EVENT_FUTURE_WRITE => { - if let Some(call) = CALLS.remove(&event1) { - call.send(event2 as _); - } - - let state = ctx as *mut FutureState; - let done = poll(state).is_ready(); - - if event0 == EVENT_CALL_DONE { - subtask_drop(event1 as u32); - } - - if matches!( - event0, - EVENT_CALL_DONE - | EVENT_STREAM_READ - | EVENT_STREAM_WRITE - | EVENT_FUTURE_READ - | EVENT_FUTURE_WRITE - ) { - (*state).todo -= 1; - } - - if done && (*state).todo == 0 { - drop(Box::from_raw(state)); - 1 - } else { - 0 - } - } - _ => unreachable!(), - } -} - -#[doc(hidden)] -pub trait FuturePayload: Sized + 'static { - fn new() -> u32; - async fn write(future: u32, value: Self) -> bool; - async fn read(future: u32) -> Option; - fn drop_writer(future: u32); - fn drop_reader(future: u32); -} - -pub struct FutureWriter { - handle: u32, - _phantom: PhantomData, -} - -impl FutureWriter { - pub async fn write(self, v: T) { - match unsafe { HANDLES.entry(self.handle) } { - Entry::Vacant(_) => unreachable!(), - Entry::Occupied(mut entry) => match entry.get() { - Handle::LocalOpen => { - let mut v = Some(v); - future::poll_fn(move |cx| match unsafe { HANDLES.entry(self.handle) } { - 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!() - } - }, - }) - .await - } - Handle::LocalWaiting(_) => { - let Handle::LocalWaiting(tx) = entry.insert(Handle::LocalClosed) else { - unreachable!() - }; - tx.send(Box::new(v)); - } - Handle::LocalClosed => (), - Handle::Read | Handle::LocalReady(..) => unreachable!(), - Handle::Write => { - T::write(self.handle, v).await; - } - }, - } - } -} - -impl Drop for FutureWriter { - fn drop(&mut self) { - match unsafe { HANDLES.entry(self.handle) } { - 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::drop_writer(self.handle); - } - }, - } - } -} - -pub struct FutureReader { - handle: u32, - _phantom: PhantomData, -} - -impl FutureReader { - #[doc(hidden)] - pub fn from_handle(handle: u32) -> Self { - match unsafe { HANDLES.entry(handle) } { - 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 { - match unsafe { HANDLES.entry(self.handle) } { - 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 IntoFuture for FutureReader { - type Output = Option; - type IntoFuture = Pin + 'static>>; - - fn into_future(self) -> Self::IntoFuture { - match unsafe { HANDLES.entry(self.handle) } { - Entry::Vacant(_) => unreachable!(), - Entry::Occupied(mut entry) => match entry.get() { - Handle::Write | Handle::LocalWaiting(_) => unreachable!(), - Handle::Read => Box::pin(async move { T::read(self.handle).await }), - 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 Drop for FutureReader { - fn drop(&mut self) { - match unsafe { HANDLES.entry(self.handle) } { - 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::drop_reader(self.handle); - } - Handle::Write => unreachable!(), - }, - } - } -} - -#[doc(hidden)] -pub trait StreamPayload: Unpin + Sized + 'static { - fn new() -> u32; - async fn write(stream: u32, values: &[Self]) -> Option; - async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; - fn drop_writer(future: u32); - fn drop_reader(future: u32); -} - -pub struct StreamWriter { - handle: u32, - future: Option + 'static>>>, - _phantom: PhantomData, -} - -impl Sink> for StreamWriter { - type Error = Infallible; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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) -> Result<(), Self::Error> { - assert!(self.future.is_none()); - match unsafe { HANDLES.entry(self.handle) } { - Entry::Vacant(_) => unreachable!(), - Entry::Occupied(mut entry) => match entry.get() { - Handle::LocalOpen => { - let handle = self.handle; - let mut item = Some(item); - self.get_mut().future = Some(Box::pin(future::poll_fn(move |cx| { - match unsafe { HANDLES.entry(handle) } { - 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 { - Poll::Ready(()) - } - } - Handle::LocalReady(..) => Poll::Pending, - Handle::LocalClosed => 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; - 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; - } - } - })); - } - }, - } - Ok(()) - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.poll_ready(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.poll_ready(cx) - } -} - -impl Drop for StreamWriter { - fn drop(&mut self) { - if self.future.is_some() { - todo!("gracefully handle `StreamWriter::drop` when a write is in progress"); - } - - match unsafe { HANDLES.entry(self.handle) } { - 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::drop_writer(self.handle); - } - }, - } - } -} - -pub struct StreamReader { - handle: u32, - future: Option>> + 'static>>>, - _phantom: PhantomData, -} - -impl StreamReader { - #[doc(hidden)] - pub fn from_handle(handle: u32) -> Self { - match unsafe { HANDLES.entry(handle) } { - Entry::Vacant(mut 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 { - match unsafe { HANDLES.entry(self.handle) } { - 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 Stream for StreamReader { - type Item = Vec; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let me = self.get_mut(); - - if me.future.is_none() { - me.future = Some(match unsafe { HANDLES.entry(me.handle) } { - Entry::Vacant(_) => unreachable!(), - Entry::Occupied(mut entry) => match entry.get() { - Handle::Write | Handle::LocalWaiting(_) => unreachable!(), - Handle::Read => { - let handle = me.handle; - Box::pin(async move { - let mut buffer = iter::repeat_with(MaybeUninit::uninit) - .take(ceiling(64 * 1024, mem::size_of::())) - .collect::>(); - - if let Some(count) = T::read(handle, &mut buffer).await { - buffer.truncate(count); - Some(unsafe { - mem::transmute::>, Vec>(buffer) - }) - } else { - None - } - }) - } - Handle::LocalOpen => { - let (tx, rx) = oneshot::channel(); - entry.insert(Handle::LocalWaiting(tx)); - Box::pin(rx.map(|v| v.ok().map(|v| *v.downcast().unwrap()))) - } - Handle::LocalClosed => return Poll::Ready(None), - Handle::LocalReady(..) => { - let Handle::LocalReady(v, waker) = entry.insert(Handle::LocalOpen) else { - unreachable!() - }; - waker.wake(); - return Poll::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 Drop for StreamReader { - fn drop(&mut self) { - if self.future.is_some() { - todo!("gracefully handle `StreamReader::drop` when a read is in progress"); - } - - match unsafe { HANDLES.entry(self.handle) } { - 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::drop_reader(self.handle); - } - Handle::Write => unreachable!(), - }, - } - } -} - -pub struct Error { - handle: u32, -} - -impl Error { - #[doc(hidden)] - pub fn from_handle(handle: u32) -> Self { - Self { handle } - } - - #[doc(hidden)] - pub fn handle(&self) -> u32 { - self.handle - } -} - -impl Debug for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Error").finish() - } -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Error") - } -} - -impl std::error::Error for Error {} - -impl Drop for Error { - fn drop(&mut self) { - #[cfg(not(target_arch = "wasm32"))] - { - unreachable!(); - } - - #[cfg(target_arch = "wasm32")] - { - #[link(wasm_import_module = "$root")] - extern "C" { - #[link_name = "[error-drop]"] - fn error_drop(_: u32); - } - if self.handle != 0 { - unsafe { error_drop(self.handle) } - } - } - } -} - -pub fn new_future() -> (FutureWriter, FutureReader) { - let handle = T::new(); - unsafe { HANDLES.insert(handle, Handle::LocalOpen) }; - ( - FutureWriter { - handle, - _phantom: PhantomData, - }, - FutureReader { - handle, - _phantom: PhantomData, - }, - ) -} - -pub fn new_stream() -> (StreamWriter, StreamReader) { - let handle = T::new(); - unsafe { HANDLES.insert(handle, Handle::LocalOpen) }; - ( - StreamWriter { - handle, - future: None, - _phantom: PhantomData, - }, - StreamReader { - handle, - future: None, - _phantom: PhantomData, - }, - ) -} - -pub fn spawn(future: impl Future + 'static) { - unsafe { SPAWNED.push(Box::pin(future)) } -} - -fn task_wait(state: &mut FutureState) { - #[cfg(not(target_arch = "wasm32"))] - { - unreachable!(); - } - - #[cfg(target_arch = "wasm32")] - { - #[link(wasm_import_module = "$root")] - extern "C" { - #[link_name = "[task-wait]"] - fn wait(_: *mut i32) -> i32; - } - let mut payload = [0i32; 2]; - unsafe { - let event0 = wait(payload.as_mut_ptr()); - callback(state as *mut _ as _, event0, payload[0], payload[1]); - } - } -} - -// TODO: refactor so `'static` bounds aren't necessary -pub fn block_on(future: impl Future + 'static) -> T { - let (mut tx, mut rx) = oneshot::channel(); - let state = &mut FutureState { - todo: 0, - tasks: Some( - [Box::pin(future.map(move |v| drop(tx.send(v)))) as BoxFuture] - .into_iter() - .collect(), - ), - }; - loop { - match unsafe { poll(state) } { - Poll::Ready(()) => break rx.try_recv().unwrap().unwrap(), - Poll::Pending => task_wait(state), - } - } -} - -fn task_poll(state: &mut FutureState) -> bool { - #[cfg(not(target_arch = "wasm32"))] - { - unreachable!(); - } - - #[cfg(target_arch = "wasm32")] - { - #[link(wasm_import_module = "$root")] - extern "C" { - #[link_name = "[task-poll]"] - fn poll(_: *mut i32) -> i32; - } - let mut payload = [0i32; 3]; - unsafe { - let got_event = poll(payload.as_mut_ptr()) != 0; - if got_event { - callback(state as *mut _ as _, payload[0], payload[1], payload[2]); - } - got_event - } - } -} - -// TODO: refactor so `'static` bounds aren't necessary -pub fn poll_future(future: impl Future + 'static) -> Option { - let (mut tx, mut rx) = oneshot::channel(); - let state = &mut FutureState { - todo: 0, - tasks: Some( - [Box::pin(future.map(move |v| drop(tx.send(v)))) as BoxFuture] - .into_iter() - .collect(), - ), - }; - loop { - match unsafe { poll(state) } { - Poll::Ready(()) => break Some(rx.try_recv().unwrap().unwrap()), - Poll::Pending => { - if !task_poll(state) { - break None; - } - } - } - } -} - -pub fn task_yield() { - #[cfg(not(target_arch = "wasm32"))] - { - unreachable!(); - } - - #[cfg(target_arch = "wasm32")] - { - #[link(wasm_import_module = "$root")] - extern "C" { - #[link_name = "[task-yield]"] - fn yield_(); - } - unsafe { - yield_(); - } - } -} - -pub fn task_backpressure(enabled: bool) { - #[cfg(not(target_arch = "wasm32"))] - { - unreachable!(); - } - - #[cfg(target_arch = "wasm32")] - { - #[link(wasm_import_module = "$root")] - extern "C" { - #[link_name = "[task-backpressure]"] - fn backpressure(_: i32); - } - unsafe { - backpressure(if enabled { 1 } else { 0 }); - } - } -} - -fn ceiling(x: usize, y: usize) -> usize { - (x / y) + if x % y == 0 { 0 } else { 1 } -} diff --git a/crates/rust/src/stream_and_future_support.rs b/crates/rust/src/stream_and_future_support.rs deleted file mode 100644 index 2540b22b5..000000000 --- a/crates/rust/src/stream_and_future_support.rs +++ /dev/null @@ -1,765 +0,0 @@ -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; - 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 { - handle: u32, - _phantom: PhantomData, -} - -impl fmt::Debug for FutureWriter { - 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 { - writer: Option>, - future: Pin>>, -} - -impl Future for CancelableWrite { - 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 CancelableWrite { - /// 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 { - self.cancel_mut() - } - - fn cancel_mut(&mut self) -> FutureWriter { - 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 Drop for CancelableWrite { - fn drop(&mut self) { - if self.writer.is_some() { - self.cancel_mut(); - } - } -} - -impl FutureWriter { - /// Write the specified value to this `future`. - pub fn write(self, v: T) -> CancelableWrite { - 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>> - } - 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 Drop for FutureWriter { - 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 { - reader: Option>, - future: Pin>>>, -} - -impl Future for CancelableRead { - type Output = Option; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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 CancelableRead { - /// 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 { - self.cancel_mut() - } - - fn cancel_mut(&mut self) -> FutureReader { - 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 Drop for CancelableRead { - fn drop(&mut self) { - if self.reader.is_some() { - self.cancel_mut(); - } - } -} - -/// Represents the readable end of a Component Model `future`. -pub struct FutureReader { - handle: u32, - _phantom: PhantomData, -} - -impl fmt::Debug for FutureReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FutureReader") - .field("handle", &self.handle) - .finish() - } -} - -impl FutureReader { - #[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 IntoFuture for FutureReader { - type Output = Option; - type IntoFuture = CancelableRead; - - /// 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>>, - 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 Drop for FutureReader { - 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; - async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; - fn cancel_write(stream: u32); - fn cancel_read(stream: u32); - fn close_writable(stream: u32); - fn close_readable(stream: u32); -} - -struct CancelWriteOnDrop { - handle: Option, - _phantom: PhantomData, -} - -impl Drop for CancelWriteOnDrop { - 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 { - handle: u32, - future: Option + 'static>>>, - _phantom: PhantomData, -} - -impl StreamWriter { - /// 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 fmt::Debug for StreamWriter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StreamWriter") - .field("handle", &self.handle) - .finish() - } -} - -impl Sink> for StreamWriter { - type Error = Infallible; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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) -> 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:: { - 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:: { - 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> { - self.poll_ready(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.poll_ready(cx) - } -} - -impl Drop for StreamWriter { - 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 { - handle: Option, - _phantom: PhantomData, -} - -impl Drop for CancelReadOnDrop { - 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 { - handle: u32, - future: Option>> + 'static>>>, - _phantom: PhantomData, -} - -impl StreamReader { - /// 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 fmt::Debug for StreamReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StreamReader") - .field("handle", &self.handle) - .finish() - } -} - -impl StreamReader { - #[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 Stream for StreamReader { - type Item = Vec; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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:: { - handle: Some(handle), - _phantom: PhantomData, - }; - Box::pin(async move { - let mut buffer = iter::repeat_with(MaybeUninit::uninit) - .take(ceiling(64 * 1024, mem::size_of::())) - .collect::>(); - - let result = if let Some(count) = T::read(handle, &mut buffer).await { - buffer.truncate(count); - Some(unsafe { - mem::transmute::>, Vec>(buffer) - }) - } else { - None - }; - cancel_on_drop.handle = None; - drop(cancel_on_drop); - result - }) as Pin>> - } - Handle::LocalOpen => { - let (tx, rx) = oneshot::channel(); - entry.insert(Handle::LocalWaiting(tx)); - let mut cancel_on_drop = CancelReadOnDrop:: { - 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 Drop for StreamReader { - 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() -> (FutureWriter, FutureReader) { - 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() -> (StreamWriter, StreamReader) { - 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 } -} diff --git a/crates/test-helpers/codegen-macro/Cargo.toml b/crates/test-helpers/codegen-macro/Cargo.toml index e0d888ef6..ba1b684e1 100644 --- a/crates/test-helpers/codegen-macro/Cargo.toml +++ b/crates/test-helpers/codegen-macro/Cargo.toml @@ -11,4 +11,4 @@ test = false [dependencies] heck = { workspace = true } -quote = "1.0.33" +quote = "1.0.32" From e0d009add440e40942754d372831b416ea841610 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 20 Mar 2025 23:36:37 +0100 Subject: [PATCH 509/672] update --- Cargo.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 86c5a6efc..abf102d05 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2095,7 +2095,7 @@ dependencies = [ [[package]] name = "wasm-compose" version = "0.227.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" dependencies = [ "anyhow", "heck 0.4.1", @@ -2134,7 +2134,7 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.227.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" dependencies = [ "leb128fmt", "wasmparser 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", @@ -2143,7 +2143,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.227.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" dependencies = [ "anyhow", "auditable-serde", @@ -2186,7 +2186,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.227.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" dependencies = [ "bitflags", "hashbrown 0.15.2", @@ -2519,7 +2519,7 @@ dependencies = [ [[package]] name = "wast" version = "227.0.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" dependencies = [ "bumpalo", "leb128fmt", @@ -2540,7 +2540,7 @@ dependencies = [ [[package]] name = "wat" version = "1.227.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" dependencies = [ "wast 227.0.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] @@ -2930,7 +2930,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.227.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" dependencies = [ "anyhow", "bitflags", @@ -2967,7 +2967,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.227.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#27632f4746fa022e6a1b3e4b04a8561df29820b8" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" dependencies = [ "anyhow", "id-arena", From 99caafa901f43bab3f29175ccdd158545673440e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 21 Mar 2025 00:01:28 +0100 Subject: [PATCH 510/672] spawn unchecked (for shorter futures) --- .../rust-client/src/async_support.rs | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 5f1f77cfb..1ee743c72 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -92,19 +92,11 @@ extern "C" fn symmetric_callback(obj: *mut ()) -> symmetric_executor::CallbackSt } } -/// 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( - future: impl Future + 'static, - fun: impl FnOnce(T) + 'static, +pub fn first_poll_sub( + future: BoxFuture, ) -> *mut () { let state = Box::into_raw(Box::new(FutureState { - future: Box::pin(future.map(fun)), + future, completion_event: None, waiting_for: None, })); @@ -123,6 +115,20 @@ pub fn first_poll( } } +/// 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( + future: impl Future + 'static, + fun: impl FnOnce(T) + 'static, +) -> *mut () { + first_poll_sub(Box::pin(future.map(fun))) +} + /// Await the completion of a call to an async-lowered import. #[doc(hidden)] pub async unsafe fn await_result(function: impl Fn() -> *mut u8) { @@ -138,3 +144,10 @@ pub fn spawn(future: impl Future + 'static + Send) { let wait_for = unsafe { EventSubscription::from_handle(wait_for as usize) }; drop(wait_for); } + +pub unsafe fn spawn_unchecked(future: impl Future) { + let future1: Pin>> = Box::pin(future); + let wait_for = first_poll_sub(unsafe { std::mem::transmute(future1) }); + let wait_for = unsafe { EventSubscription::from_handle(wait_for as usize) }; + drop(wait_for); +} From 75ec928387b7da13bfb988d0f703a2bc76ed8242 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 21 Mar 2025 00:14:53 +0100 Subject: [PATCH 511/672] turn on tracing --- crates/cpp/tests/symmetric_stream/stream/Cargo.toml | 2 +- crates/symmetric_executor/symmetric_stream/Cargo.toml | 3 +++ crates/symmetric_executor/symmetric_stream/src/lib.rs | 10 ++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml index 43a957e03..053228ad6 100644 --- a/crates/cpp/tests/symmetric_stream/stream/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream/stream/Cargo.toml @@ -8,7 +8,7 @@ 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" } +symmetric_stream = { path = "../../../../symmetric_executor/symmetric_stream", features=["trace"] } [dependencies.wit-bindgen] package = "dummy-rt" diff --git a/crates/symmetric_executor/symmetric_stream/Cargo.toml b/crates/symmetric_executor/symmetric_stream/Cargo.toml index 6f5d50aa2..fb3b6bbbf 100644 --- a/crates/symmetric_executor/symmetric_stream/Cargo.toml +++ b/crates/symmetric_executor/symmetric_stream/Cargo.toml @@ -13,3 +13,6 @@ path = "../dummy-rt" [lib] crate-type = ["cdylib"] + +[features] +trace = [] diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index b4592d757..a9f0a04f6 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -80,6 +80,8 @@ impl GuestStreamObj for StreamObj { ready_size: AtomicIsize::new(results::BLOCKED), ready_capacity: AtomicUsize::new(0), }; + #[cfg(feature = "trace")] + println!("Stream::new {}", inner.read_ready_event_send.handle()); Self(Arc::new(inner)) } @@ -90,6 +92,8 @@ impl GuestStreamObj for StreamObj { fn start_reading(&self, buffer: symmetric_stream::Buffer) { let buf = buffer.get::().get_address().take_handle() as *mut (); let size = buffer.get::().capacity(); + #[cfg(feature = "trace")] + println!("Stream::start_read {} {buf} {size}", self.read_ready_event_send.handle()); 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 { @@ -107,6 +111,8 @@ impl GuestStreamObj for StreamObj { 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); + #[cfg(feature = "trace")] + println!("Stream::read_result {} {addr} {size}", self.read_ready_event_send.handle()); if addr as usize == EOF_MARKER { None } else { @@ -128,6 +134,8 @@ impl GuestStreamObj for StreamObj { .0 .read_addr .swap(core::ptr::null_mut(), Ordering::Relaxed); + #[cfg(feature = "trace")] + println!("Stream::start_write {} {addr} {size}", self.read_ready_event_send.handle()); self.0.ready_capacity.store(size, Ordering::Release); symmetric_stream::Buffer::new(Buffer { addr, @@ -144,6 +152,8 @@ impl GuestStreamObj for StreamObj { } else { (0, EOF_MARKER as usize as *mut ()) }; + #[cfg(feature = "trace")] + println!("Stream::finish_write {} {addr} {elements}", self.read_ready_event_send.handle()); 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); From 8a126e22fa3fe6fd841de8b5c91ac0e9267bc04f Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 21 Mar 2025 08:34:26 +0100 Subject: [PATCH 512/672] prototype for stream tracing --- crates/symmetric_executor/symmetric_stream/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index a9f0a04f6..f53573991 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -93,7 +93,7 @@ impl GuestStreamObj for StreamObj { let buf = buffer.get::().get_address().take_handle() as *mut (); let size = buffer.get::().capacity(); #[cfg(feature = "trace")] - println!("Stream::start_read {} {buf} {size}", self.read_ready_event_send.handle()); + println!("Stream::start_read {} {buf:?} {size}", self.0.read_ready_event_send.handle()); 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 { @@ -112,7 +112,7 @@ impl GuestStreamObj for StreamObj { let addr = self.0.ready_addr.swap(null_mut(), Ordering::Relaxed); let capacity = self.0.ready_capacity.swap(0, Ordering::Relaxed); #[cfg(feature = "trace")] - println!("Stream::read_result {} {addr} {size}", self.read_ready_event_send.handle()); + println!("Stream::read_result {} {addr:?} {size}", self.0.read_ready_event_send.handle()); if addr as usize == EOF_MARKER { None } else { @@ -135,7 +135,7 @@ impl GuestStreamObj for StreamObj { .read_addr .swap(core::ptr::null_mut(), Ordering::Relaxed); #[cfg(feature = "trace")] - println!("Stream::start_write {} {addr} {size}", self.read_ready_event_send.handle()); + println!("Stream::start_write {} {addr:?} {size}", self.0.read_ready_event_send.handle()); self.0.ready_capacity.store(size, Ordering::Release); symmetric_stream::Buffer::new(Buffer { addr, @@ -153,7 +153,7 @@ impl GuestStreamObj for StreamObj { (0, EOF_MARKER as usize as *mut ()) }; #[cfg(feature = "trace")] - println!("Stream::finish_write {} {addr} {elements}", self.read_ready_event_send.handle()); + println!("Stream::finish_write {} {addr:?} {elements}", self.0.read_ready_event_send.handle()); 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); From 58ea3e8e00c376fe1ec2c4ef7c15f971f8548775 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 21 Mar 2025 08:47:03 +0100 Subject: [PATCH 513/672] point out signal activation in trace --- crates/symmetric_executor/symmetric_stream/src/lib.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index f53573991..0be430f76 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -81,7 +81,7 @@ impl GuestStreamObj for StreamObj { ready_capacity: AtomicUsize::new(0), }; #[cfg(feature = "trace")] - println!("Stream::new {}", inner.read_ready_event_send.handle()); + println!("Stream::new {:x}", inner.read_ready_event_send.handle()); Self(Arc::new(inner)) } @@ -93,7 +93,7 @@ impl GuestStreamObj for StreamObj { let buf = buffer.get::().get_address().take_handle() as *mut (); let size = buffer.get::().capacity(); #[cfg(feature = "trace")] - println!("Stream::start_read {} {buf:?} {size}", self.0.read_ready_event_send.handle()); + println!("Stream::start_read {:x} {buf:x?} {size} =>", self.0.read_ready_event_send.handle()); 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 { @@ -112,7 +112,7 @@ impl GuestStreamObj for StreamObj { let addr = self.0.ready_addr.swap(null_mut(), Ordering::Relaxed); let capacity = self.0.ready_capacity.swap(0, Ordering::Relaxed); #[cfg(feature = "trace")] - println!("Stream::read_result {} {addr:?} {size}", self.0.read_ready_event_send.handle()); + println!("Stream::read_result {:x} {addr:x?} {size}", self.0.read_ready_event_send.handle()); if addr as usize == EOF_MARKER { None } else { @@ -135,7 +135,7 @@ impl GuestStreamObj for StreamObj { .read_addr .swap(core::ptr::null_mut(), Ordering::Relaxed); #[cfg(feature = "trace")] - println!("Stream::start_write {} {addr:?} {size}", self.0.read_ready_event_send.handle()); + println!("Stream::start_write {:x} {addr:x?} {size}", self.0.read_ready_event_send.handle()); self.0.ready_capacity.store(size, Ordering::Release); symmetric_stream::Buffer::new(Buffer { addr, @@ -153,7 +153,7 @@ impl GuestStreamObj for StreamObj { (0, EOF_MARKER as usize as *mut ()) }; #[cfg(feature = "trace")] - println!("Stream::finish_write {} {addr:?} {elements}", self.0.read_ready_event_send.handle()); + println!("Stream::finish_write {:x} {addr:x?} {elements} =>", self.0.read_ready_event_send.handle()); 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); From 4e7d6fb8f6ed5b6ebb55e0b203999838aecc58f6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 30 Mar 2025 21:32:39 +0200 Subject: [PATCH 514/672] link to newest wasm-tools --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3e0c5d3b0..9098198e3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2245,7 +2245,7 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.227.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0a617a9c56fc9e88e9ba7312fce2edd503d4c081" dependencies = [ "leb128fmt", "wasmparser 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", @@ -2270,7 +2270,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.227.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0a617a9c56fc9e88e9ba7312fce2edd503d4c081" dependencies = [ "anyhow", "auditable-serde", @@ -2324,7 +2324,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.227.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0a617a9c56fc9e88e9ba7312fce2edd503d4c081" dependencies = [ "bitflags", "hashbrown 0.15.2", @@ -2657,7 +2657,7 @@ dependencies = [ [[package]] name = "wast" version = "227.0.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0a617a9c56fc9e88e9ba7312fce2edd503d4c081" dependencies = [ "bumpalo", "leb128fmt", @@ -2678,7 +2678,7 @@ dependencies = [ [[package]] name = "wat" version = "1.227.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0a617a9c56fc9e88e9ba7312fce2edd503d4c081" dependencies = [ "wast 227.0.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] @@ -3071,7 +3071,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.227.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0a617a9c56fc9e88e9ba7312fce2edd503d4c081" dependencies = [ "anyhow", "bitflags", @@ -3108,7 +3108,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.227.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#408e05b15e89c22e394433d104ba00b90bd0962f" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0a617a9c56fc9e88e9ba7312fce2edd503d4c081" dependencies = [ "anyhow", "id-arena", From 81a667d8e90150e53f1baea9cc623a4818ee4cc9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 30 Mar 2025 21:34:57 +0200 Subject: [PATCH 515/672] post-merge cleanup --- crates/core/src/abi.rs | 15 ++++++++++----- crates/cpp/src/lib.rs | 2 +- .../symmetric_stream/src/lib.rs | 1 + 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 0a2a524fa..ac913cb16 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -874,7 +874,14 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.call_with_signature(func, sig, variant, lift_lower, async_); } - fn call_with_signature(&mut self, func: &Function, sig: WasmSignature, variant: AbiVariant, lift_lower: LiftLower, async_: bool) { + fn call_with_signature( + &mut self, + func: &Function, + sig: WasmSignature, + variant: AbiVariant, + lift_lower: LiftLower, + async_: bool, + ) { const MAX_FLAT_PARAMS: usize = 16; // Lowering parameters calling a wasm import _or_ returning a result @@ -1121,9 +1128,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.lower(ty); } } else { - match variant == AbiVariant::GuestImport - || lift_lower == LiftLower::Symmetric - { + match variant == AbiVariant::GuestImport || 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 @@ -1852,7 +1857,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { // if matches!(self.lift_lower, LiftLower::Symmetric) { // self.emit_and_lift(ty, addr, &PointerLoad { offset }) // } else { - self.emit_and_lift(ty, addr, &I32Load { offset }) + self.emit_and_lift(ty, addr, &I32Load { offset }) // } } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index e4d1f8836..45e52e122 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1822,7 +1822,7 @@ impl CppInterfaceGenerator<'_> { let mut f = FunctionBindgen::new(self, params.clone()); f.params = params; - abi::post_return(f.gen.resolve, func, &mut f, false); + abi::post_return(f.gen.resolve, func, &mut f); let FunctionBindgen { src, .. } = f; self.gen.c_src.src.push_str(&src); self.gen.c_src.src.push_str("}\n"); diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index 0be430f76..785f0e40a 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -114,6 +114,7 @@ impl GuestStreamObj for StreamObj { #[cfg(feature = "trace")] println!("Stream::read_result {:x} {addr:x?} {size}", self.0.read_ready_event_send.handle()); if addr as usize == EOF_MARKER { + None } else { Some(symmetric_stream::Buffer::new(Buffer { From dd48a91d0ce3be70d1486ebb8e908a3a97cb7d73 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 9 Apr 2025 23:50:53 +0200 Subject: [PATCH 516/672] skeletal future support --- crates/cpp/src/lib.rs | 16 ++++++-- crates/cpp/src/wamr.rs | 4 +- .../symmetric_future/future_cpp/Makefile | 0 .../future_cpp/future_world.cpp | 38 +++++++++++++++++++ .../future_cpp/future_world_cpp.h | 14 +++++++ crates/cpp/tests/symmetric_future/generate.sh | 1 + 6 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 crates/cpp/tests/symmetric_future/future_cpp/Makefile create mode 100644 crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp create mode 100644 crates/cpp/tests/symmetric_future/future_cpp/future_world_cpp.h diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 45e52e122..28e2950e3 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2035,7 +2035,11 @@ impl CppInterfaceGenerator<'_> { } } } - TypeDefKind::Future(_) => todo!(), + TypeDefKind::Future(ty) => { + "std::future<".to_string() + + &self.optional_type_name(ty.as_ref(), from_namespace, flavor) + + ">" + } TypeDefKind::Stream(_) => todo!(), TypeDefKind::Type(ty) => self.type_name(ty, from_namespace, flavor), TypeDefKind::Unknown => todo!(), @@ -3846,8 +3850,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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::FutureLower { .. } => { + self.src.push_str("future_lower()"); + results.push(String::from("future")); + } + abi::Instruction::FutureLift { .. } => { + self.src.push_str("future_lift()"); + results.push(String::from("future")); + } abi::Instruction::StreamLower { .. } => todo!(), abi::Instruction::StreamLift { .. } => todo!(), abi::Instruction::ErrorContextLower { .. } => todo!(), diff --git a/crates/cpp/src/wamr.rs b/crates/cpp/src/wamr.rs index ebdf5af12..6121cbbb3 100644 --- a/crates/cpp/src/wamr.rs +++ b/crates/cpp/src/wamr.rs @@ -112,8 +112,8 @@ fn wamr_add_result(sig: &mut WamrSig, resolve: &Resolve, ty: &Type) { TypeDefKind::List(_) => { sig.wamr_types.push('*'); } - TypeDefKind::Future(_) => todo!(), - TypeDefKind::Stream(_) => todo!(), + TypeDefKind::Future(_) => sig.wamr_types.push('*'), + TypeDefKind::Stream(_) => sig.wamr_types.push('*'), TypeDefKind::Type(ty) => wamr_add_result(sig, resolve, &ty), TypeDefKind::Unknown => todo!(), TypeDefKind::Resource => { diff --git a/crates/cpp/tests/symmetric_future/future_cpp/Makefile b/crates/cpp/tests/symmetric_future/future_cpp/Makefile new file mode 100644 index 000000000..e69de29bb diff --git a/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp b/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp new file mode 100644 index 000000000..f66e65122 --- /dev/null +++ b/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp @@ -0,0 +1,38 @@ +// 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_future_world(void); +void __component_type_object_force_link_future_world_public_use_in_this_compilation_unit(void) { + __component_type_object_force_link_future_world(); +} +#endif +#include "future_world_cpp.h" +#include // 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" uint8_t* testX3AtestX2Ffuture_sourceX00create(); +std::future test::test::future_source::Create() +{ + auto ret = testX3AtestX2Ffuture_sourceX00create(); + future_lift();return future; +} +extern "C" +uint8_t* testX3AtestX2Ffuture_testX00create() +{ + auto result0 = exports::test::test::future_test::Create(); + future_lower();return future; +} + +// Component Adapters diff --git a/crates/cpp/tests/symmetric_future/future_cpp/future_world_cpp.h b/crates/cpp/tests/symmetric_future/future_cpp/future_world_cpp.h new file mode 100644 index 000000000..b0e1b8517 --- /dev/null +++ b/crates/cpp/tests/symmetric_future/future_cpp/future_world_cpp.h @@ -0,0 +1,14 @@ +// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT! +#ifndef __CPP_GUEST_BINDINGS_FUTURE_WORLD_H +#define __CPP_GUEST_BINDINGS_FUTURE_WORLD_H +#define WIT_SYMMETRIC +#include +#include +#include +namespace test {namespace test {namespace future_source {std::future Create(); +// export_interface Interface(Id { idx: 1 }) +}}} +namespace exports {namespace test {namespace test {namespace future_test {std::future Create(); +}}}} + +#endif diff --git a/crates/cpp/tests/symmetric_future/generate.sh b/crates/cpp/tests/symmetric_future/generate.sh index 43a301f69..b89af3940 100755 --- a/crates/cpp/tests/symmetric_future/generate.sh +++ b/crates/cpp/tests/symmetric_future/generate.sh @@ -1,3 +1,4 @@ #!/bin/sh (cd future/src ; ../../../../../../target/debug/wit-bindgen rust ../../wit/future.wit --async none --symmetric) +(cd future_cpp; ../../../../../target/debug/wit-bindgen cpp ../wit/future.wit --symmetric) cargo fmt From a805d3cf793d0a714a56bce829a45f426d572048 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 10 Apr 2025 22:04:04 +0200 Subject: [PATCH 517/672] update wasm-tools --- Cargo.lock | 80 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 51a863233..1e262f1ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1882,7 +1882,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.228.0", + "wasm-encoder 0.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-core", "wit-component", "wit-parser 0.228.0", @@ -2231,7 +2231,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05d30290541f2d4242a162bbda76b8f2d8b1ac59eab3568ed6f2327d52c9b2c4" dependencies = [ "leb128fmt", - "wasmparser 0.228.0", + "wasmparser 0.228.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-encoder" +version = "0.228.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6d761090983165c46c78f8a5d2205e5b1ec17f54" +dependencies = [ + "leb128fmt", + "wasmparser 0.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2253,13 +2262,12 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.228.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc79a7e49646e1591d26649eac7ad2b09488aa02c086f3d076705830eae61031" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6d761090983165c46c78f8a5d2205e5b1ec17f54" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.228.0", - "wasmparser 0.228.0", + "wasm-encoder 0.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2300,8 +2308,8 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.227.1" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#0a617a9c56fc9e88e9ba7312fce2edd503d4c081" +version = "0.228.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6d761090983165c46c78f8a5d2205e5b1ec17f54" dependencies = [ "bitflags", "hashbrown 0.15.2", @@ -2373,7 +2381,7 @@ dependencies = [ "wasmtime-slab", "wasmtime-versioned-export-macros", "wasmtime-winch", - "wat 1.227.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wat 1.228.0 (registry+https://github.com/rust-lang/crates.io-index)", "windows-sys 0.52.0", ] @@ -2628,7 +2636,19 @@ dependencies = [ "leb128fmt", "memchr", "unicode-width 0.2.0", - "wasm-encoder 0.228.0", + "wasm-encoder 0.228.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wast" +version = "228.0.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6d761090983165c46c78f8a5d2205e5b1ec17f54" +dependencies = [ + "bumpalo", + "leb128fmt", + "memchr", + "unicode-width 0.2.0", + "wasm-encoder 0.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2637,7 +2657,15 @@ version = "1.228.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ec29c89a8d055df988de7236483bf569988ac3d6905899f6af5ef920f9385ad" dependencies = [ - "wast 228.0.0", + "wast 228.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wat" +version = "1.228.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6d761090983165c46c78f8a5d2205e5b1ec17f54" +dependencies = [ + "wast 228.0.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] @@ -2866,7 +2894,7 @@ dependencies = [ "anyhow", "clap", "heck 0.5.0", - "wasm-encoder 0.228.0", + "wasm-encoder 0.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata 0.228.0", "wit-bindgen-core", "wit-component", @@ -2881,8 +2909,8 @@ dependencies = [ "env_logger", "heck 0.5.0", "test-artifacts", - "wasm-encoder 0.228.0", - "wasmparser 0.228.0", + "wasm-encoder 0.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasmtime", "wasmtime-wasi", "wit-bindgen-bridge", @@ -2915,8 +2943,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.227.1 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", - "wasm-metadata 0.227.1", + "wasm-encoder 0.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasm-metadata 0.228.0", "wit-bindgen-c", "wit-bindgen-core", "wit-component", @@ -3017,9 +3045,9 @@ dependencies = [ "wac-parser", "wac-types", "wasi-preview1-component-adapter-provider", - "wasm-encoder 0.228.0", - "wasmparser 0.228.0", - "wat", + "wasm-encoder 0.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wasmparser 0.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wat 1.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-bindgen-csharp", "wit-component", "wit-parser 0.228.0", @@ -3028,8 +3056,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.228.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb53295365b9500e17bc41c40229337183244f0d6185a5b028c587837c3370f" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6d761090983165c46c78f8a5d2205e5b1ec17f54" dependencies = [ "anyhow", "bitflags", @@ -3038,10 +3065,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.228.0", + "wasm-encoder 0.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wasm-metadata 0.228.0", - "wasmparser 0.228.0", - "wat", + "wasmparser 0.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", + "wat 1.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", "wit-parser 0.228.0", ] @@ -3066,8 +3093,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.228.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399ce56e28d79fd3abfa03fdc7ceb89ffec4d4b2674fe3a92056b7d845653c38" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6d761090983165c46c78f8a5d2205e5b1ec17f54" dependencies = [ "anyhow", "id-arena", @@ -3078,7 +3104,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.228.0", + "wasmparser 0.228.0 (git+https://github.com/cpetig/wasm-tools?branch=symmetric)", ] [[package]] From 5d177974f347b016dfc88eda487d039ad57fd939 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 10 Apr 2025 22:45:53 +0200 Subject: [PATCH 518/672] fix compilation after merge --- crates/core/src/abi.rs | 40 +- crates/cpp/src/lib.rs | 2030 ++++++++++++++++++------------------ crates/rust/src/bindgen.rs | 2 +- 3 files changed, 1029 insertions(+), 1043 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index fd7157df0..a237a823e 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -967,36 +967,24 @@ impl<'a, B: Bindgen> Generator<'a, B> { self.realloc = None; let mut retptr_oprnd = None; - if 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()); + + // If necessary we may need to prepare a return pointer for + // this ABI. + if variant == AbiVariant::GuestImport && sig.retptr { + let info = self.bindgen.sizes().params(&func.result); + let ptr = self.bindgen.return_pointer(info.size, info.align); self.return_pointer = Some(ptr.clone()); + retptr_oprnd = Some(ptr.clone()); self.stack.push(ptr); - - assert_eq!(self.stack.len(), 2); - self.emit(&Instruction::AsyncCallWasm { - name: &format!("[async-lower]{}", func.name), - }); - } else { - // If necessary we may need to prepare a return pointer for - // this ABI. - if variant == AbiVariant::GuestImport && sig.retptr { - let info = self.bindgen.sizes().params(&func.result); - let ptr = self.bindgen.return_pointer(info.size, info.align); - self.return_pointer = Some(ptr.clone()); - self.stack.push(ptr); - } - - assert_eq!(self.stack.len(), sig.params.len()); - self.emit(&Instruction::CallWasm { - name: &func.name, - sig: &sig, - module_prefix: Default::default(), - }); } + assert_eq!(self.stack.len(), sig.params.len()); + self.emit(&Instruction::CallWasm { + name: &func.name, + sig: &sig, + module_prefix: Default::default(), + }); + if matches!(lift_lower, LiftLower::Symmetric) && sig.retptr { if let Some(ptr) = retptr_oprnd { self.read_results_from_memory( diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 28e2950e3..46d5be53b 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2705,61 +2705,61 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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()); + 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()); + } } - } 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)); - } - } + 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()), + 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}")); - } + 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) - } + self.load_ext("uint8_t", *offset, operands, results) + } abi::Instruction::I32Load8S { offset } => { - self.load_ext("int8_t", *offset, operands, results) - } + self.load_ext("int8_t", *offset, operands, results) + } abi::Instruction::I32Load16U { offset } => { - self.load_ext("uint16_t", *offset, operands, results) - } + self.load_ext("uint16_t", *offset, operands, results) + } abi::Instruction::I32Load16S { offset } => { - self.load_ext("int16_t", *offset, operands, results) - } + self.load_ext("int16_t", *offset, operands, results) + } abi::Instruction::I64Load { offset } => { - self.load("int64_t", *offset, operands, results) - } + 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), @@ -2769,13 +2769,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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::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"), @@ -2792,727 +2792,727 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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]); + 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); } - 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]); + 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); } - 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]); + 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); } - 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] - ) + 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); } - } 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] - ); + 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); } - 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}); + 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" )); + 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})")); + } } - } 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))); - } - } + 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()")); + // 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); } - } 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::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()")); + 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()")); + } } - } 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}); + 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!("{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})")); } - results.push(format!("std::move({var})")) + (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!(), + }, } - 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})")); - } - } + 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::>() - .join(", "), - ); - self.src.push_str(">("); - self.src.push_str(&operands.join(", ")); - self.src.push_str(");\n"); - results.push(format!("std::move({name})")); - } + 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::>() + .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())); + 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)" + )); + } + } } - Int::U64 => { - let name = + abi::Instruction::FlagsLift { flags, ty, .. } => { + let typename = 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)))" - )); + 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); - } + 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::>(); - let payloads = self - .payloads - .drain(self.payloads.len() - variant.cases.len()..) - .collect::>(); - - 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); - } + 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::>(); + let payloads = self + .payloads + .drain(self.payloads.len() - variant.cases.len()..) + .collect::>(); + + 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]); + 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); + 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); + 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"); } - 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::>(); - 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("}"); - } + 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::>(); + let op0 = &operands[0]; - // if named_enum { - // result.push_str("#[cfg(debug_assertions)]"); - // } - let blocks: Vec = 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("}"); + 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("}"); + } - result.push_str("}"); - results.push(result); - } + // if named_enum { + // result.push_str("#[cfg(debug_assertions)]"); + // } + let blocks: Vec = 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])); - } + 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};"); - } + 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();"); + 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, - "\ + 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 (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}; + 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})")); - } + 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};"); - } + 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() - }; + 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, - "\ + 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 (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}; + let tmp = self.tmp(); + let resultname = self.tempname("result", tmp); + uwriteln!( + self.src, + "{full_type} {resultname}; if ({operand}==0) {{ {ok} {resultname}.emplace({ok_result}); @@ -3520,359 +3520,357 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { {err} {resultname}={err_type}{{{err_result}}}; }}" - ); - results.push(resultname); - } + ); + results.push(resultname); + } abi::Instruction::CallWasm { - name, - sig, - module_prefix, - } => { - let module_name = self - .gen - .wasm_import_module - .as_ref() - .map(|e| { - self.gen + name, + sig, + module_prefix, + } => { + let module_name = self .gen - .import_prefix + .wasm_import_module .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\ + .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) - } + 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;"); } - } - 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")) + 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;"); } - Some(WasmType::Length) => { - if self.gen.gen.opts.wasm64 { - (String::from("WASM_I64"), String::from("i64")) - } else { - (String::from("WASM_I32"), String::from("i32")) - } + 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()); } - Some(WasmType::PointerOrI64) => { - (String::from("WASM_I64"), String::from("i64")) + } 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()); } - 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.src.push_str(&func); + self.src.push_str("("); + self.src.push_str(&operands.join(", ")); + self.src.push_str(");\n"); } } - } - 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) => + 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() { - // strange but works - if matches!(self.variant, AbiVariant::GuestExport) { - self.src.push_str("this->index = "); - } else { - self.src.push_str("this->handle = "); - } + let _ = namespace.pop(); } - _ => self.src.push_str("return "), + 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); } - if let Some(CabiPostInformation { - module: _, - name: _cabi_post_name, - ret_type: cabi_post_type, - }) = self.cabi_post.as_ref() + self.src.push_str(&func_name_h); + if matches!(func.kind, FunctionKind::Constructor(_)) + && self.gen.gen.opts.host_side() { - self.src.push_str("wit::guest_owned<"); - self.src.push_str(&cabi_post_type); - self.src.push_str(">("); + self.push_str("::New"); } - 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]); + 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(", "); + } } - } 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() + self.push_str(&operands.join(", ")); + if false + && matches!(func.kind, FunctionKind::Constructor(_)) + && !self.gen.gen.opts.is_only_handle(self.variant) { - 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)); - } + // acquire object from unique_ptr + self.push_str(").release();"); + results[0] = format!("(*({}))", results[0]); + } else { + self.push_str(");\n"); } - 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(); + 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()"); + ); + } else { + self.src.push_str(".into_handle()"); + } + } + self.src.push_str(";\n"); } } - self.src.push_str(";\n"); } - } - } abi::Instruction::Malloc { .. } => todo!(), abi::Instruction::GuestDeallocate { .. } => { - uwriteln!(self.src, "free((void*) ({}));", operands[0]); - } + 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, "}}"); - } + 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, "}}"); - } + 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::>(); - - 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"); - } + let blocks = self + .blocks + .drain(self.blocks.len() - blocks..) + .collect::>(); + + 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) - } + 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) - } + 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) - } + 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 { .. } => { - self.src.push_str("future_lower()"); - results.push(String::from("future")); - } + self.src.push_str("future_lower()"); + results.push(String::from("future")); + } abi::Instruction::FutureLift { .. } => { - self.src.push_str("future_lift()"); - results.push(String::from("future")); - } + self.src.push_str("future_lift()"); + results.push(String::from("future")); + } 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); - } - } + for i in 0..*amt { + let tmp = self.tmp(); + let result = format!("result{}", tmp); + uwriteln!(self.src, "auto {result} = {};", operands[i]); + results.push(result); + } + } + abi::Instruction::AsyncTaskReturn { .. } => todo!(), } } diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 984ae0e7c..dc37fc68d 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -1039,7 +1039,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { } Instruction::AsyncTaskReturn { name, params } => { - let func = self.declare_import(name, params, &[]); + let func = self.declare_import("", name, params, &[]); uwriteln!(self.src, "{func}({});", operands.join(", ")); } From e200698d1c266928e8c381464ce5e95351c30fbd Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 12 Apr 2025 10:12:30 +0200 Subject: [PATCH 519/672] add from handle method (similar to stream) needed for lifting --- crates/symmetric_executor/rust-client/src/async_support.rs | 6 ++---- .../rust-client/src/async_support/future_support.rs | 4 ++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 1ee743c72..84a42a1f4 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -92,9 +92,7 @@ extern "C" fn symmetric_callback(obj: *mut ()) -> symmetric_executor::CallbackSt } } -pub fn first_poll_sub( - future: BoxFuture, -) -> *mut () { +pub fn first_poll_sub(future: BoxFuture) -> *mut () { let state = Box::into_raw(Box::new(FutureState { future, completion_event: None, @@ -146,7 +144,7 @@ pub fn spawn(future: impl Future + 'static + Send) { } pub unsafe fn spawn_unchecked(future: impl Future) { - let future1: Pin>> = Box::pin(future); + let future1: Pin>> = Box::pin(future); let wait_for = first_poll_sub(unsafe { std::mem::transmute(future1) }); 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 index 6be83b49a..817c5c585 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/future_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs @@ -106,6 +106,10 @@ impl FutureReader { } } + pub unsafe fn from_handle(handle: *mut u8) -> Self { + Self::new(unsafe { Stream::from_handle(handle as usize) }) + } + pub fn take_handle(&self) -> *mut () { self.handle.take_handle() as *mut () } From 8b0964559437d9c25f2422d539a8be2674b833cb Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 12 Apr 2025 10:14:17 +0200 Subject: [PATCH 520/672] cargo fmt --- .../symmetric_stream/src/lib.rs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index 785f0e40a..ffc8c08b1 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -93,7 +93,10 @@ impl GuestStreamObj for StreamObj { let buf = buffer.get::().get_address().take_handle() as *mut (); let size = buffer.get::().capacity(); #[cfg(feature = "trace")] - println!("Stream::start_read {:x} {buf:x?} {size} =>", self.0.read_ready_event_send.handle()); + println!( + "Stream::start_read {:x} {buf:x?} {size} =>", + self.0.read_ready_event_send.handle() + ); 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 { @@ -112,9 +115,11 @@ impl GuestStreamObj for StreamObj { let addr = self.0.ready_addr.swap(null_mut(), Ordering::Relaxed); let capacity = self.0.ready_capacity.swap(0, Ordering::Relaxed); #[cfg(feature = "trace")] - println!("Stream::read_result {:x} {addr:x?} {size}", self.0.read_ready_event_send.handle()); + println!( + "Stream::read_result {:x} {addr:x?} {size}", + self.0.read_ready_event_send.handle() + ); if addr as usize == EOF_MARKER { - None } else { Some(symmetric_stream::Buffer::new(Buffer { @@ -136,7 +141,10 @@ impl GuestStreamObj for StreamObj { .read_addr .swap(core::ptr::null_mut(), Ordering::Relaxed); #[cfg(feature = "trace")] - println!("Stream::start_write {:x} {addr:x?} {size}", self.0.read_ready_event_send.handle()); + println!( + "Stream::start_write {:x} {addr:x?} {size}", + self.0.read_ready_event_send.handle() + ); self.0.ready_capacity.store(size, Ordering::Release); symmetric_stream::Buffer::new(Buffer { addr, @@ -154,7 +162,10 @@ impl GuestStreamObj for StreamObj { (0, EOF_MARKER as usize as *mut ()) }; #[cfg(feature = "trace")] - println!("Stream::finish_write {:x} {addr:x?} {elements} =>", self.0.read_ready_event_send.handle()); + println!( + "Stream::finish_write {:x} {addr:x?} {elements} =>", + self.0.read_ready_event_send.handle() + ); 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); From 138bea41edb31ea5478683e4e7e93252a5bb4039 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 12 Apr 2025 10:26:56 +0200 Subject: [PATCH 521/672] reintroduce pointers in symmetric --- crates/core/src/abi.rs | 32 +- crates/cpp/src/lib.rs | 2026 ++++++++++++++++++++-------------------- 2 files changed, 1032 insertions(+), 1026 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index a237a823e..cf90547a8 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -740,10 +740,10 @@ pub fn call( ) { if matches!(lift_lower, LiftLower::Symmetric) { let sig = wasm_signature_symmetric(resolve, variant, func, true); - Generator::new(resolve, bindgen) + Generator::new(resolve, bindgen, true) .call_with_signature(func, sig, variant, lift_lower, async_); } else { - Generator::new(resolve, bindgen).call(func, variant, lift_lower, async_); + Generator::new(resolve, bindgen, false).call(func, variant, lift_lower, async_); } } @@ -754,7 +754,7 @@ pub fn lower_to_memory( value: B::Operand, ty: &Type, ) { - let mut generator = Generator::new(resolve, bindgen); + let mut generator = Generator::new(resolve, bindgen, false); // TODO: make this configurable? Right this this function is only called for // future/stream callbacks so it's appropriate to skip realloc here as it's // all "lower for wasm import", but this might get reused for something else @@ -770,7 +770,7 @@ pub fn lift_from_memory( address: B::Operand, ty: &Type, ) -> B::Operand { - let mut generator = Generator::new(resolve, bindgen); + let mut generator = Generator::new(resolve, bindgen, false); generator.read_from_memory(ty, address, Default::default()); generator.stack.pop().unwrap() } @@ -782,7 +782,7 @@ pub fn lift_from_memory( /// functions and will primarily generate `GuestDeallocate*` instructions, /// plus others used as input to those instructions. pub fn post_return(resolve: &Resolve, func: &Function, bindgen: &mut impl Bindgen) { - Generator::new(resolve, bindgen).post_return(func); + Generator::new(resolve, bindgen, false).post_return(func); } /// Returns whether the `Function` specified needs a post-return function to @@ -845,7 +845,7 @@ pub fn deallocate_lists_in_types( ptr: B::Operand, bindgen: &mut B, ) { - Generator::new(resolve, bindgen).deallocate_lists_in_types(types, ptr); + Generator::new(resolve, bindgen, false).deallocate_lists_in_types(types, ptr); } #[derive(Copy, Clone)] @@ -862,10 +862,11 @@ struct Generator<'a, B: Bindgen> { stack: Vec, return_pointer: Option, realloc: Option, + symmetric: bool, } impl<'a, B: Bindgen> Generator<'a, B> { - fn new(resolve: &'a Resolve, bindgen: &'a mut B) -> Generator<'a, B> { + fn new(resolve: &'a Resolve, bindgen: &'a mut B, symmetric: bool) -> Generator<'a, B> { Generator { resolve, bindgen, @@ -874,6 +875,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { stack: Vec::new(), return_pointer: None, realloc: None, + symmetric, } } @@ -1657,7 +1659,11 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::List(_) => self.write_list_to_memory(ty, addr, offset), TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Handle(_) => { - self.lower_and_emit(ty, addr, &I32Store { offset }) + if self.symmetric { + self.lower_and_emit(ty, addr, &PointerStore { offset }) + } else { + self.lower_and_emit(ty, addr, &I32Store { offset }) + } } // Decompose the record into its components and then write all @@ -1849,11 +1855,11 @@ impl<'a, B: Bindgen> Generator<'a, B> { TypeDefKind::List(_) => self.read_list_from_memory(ty, addr, offset), TypeDefKind::Future(_) | TypeDefKind::Stream(_) | TypeDefKind::Handle(_) => { - // if matches!(self.lift_lower, LiftLower::Symmetric) { - // self.emit_and_lift(ty, addr, &PointerLoad { offset }) - // } else { - self.emit_and_lift(ty, addr, &I32Load { offset }) - // } + if self.symmetric { + self.emit_and_lift(ty, addr, &PointerLoad { offset }) + } else { + self.emit_and_lift(ty, addr, &I32Load { offset }) + } } TypeDefKind::Resource => { diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 46d5be53b..37c0e01ea 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2705,61 +2705,61 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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()); - } + 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)); - } - } + 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()), - } - } + 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}")); - } + 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) - } + self.load_ext("uint8_t", *offset, operands, results) + } abi::Instruction::I32Load8S { offset } => { - self.load_ext("int8_t", *offset, operands, results) - } + self.load_ext("int8_t", *offset, operands, results) + } abi::Instruction::I32Load16U { offset } => { - self.load_ext("uint16_t", *offset, operands, results) - } + self.load_ext("uint16_t", *offset, operands, results) + } abi::Instruction::I32Load16S { offset } => { - self.load_ext("int16_t", *offset, operands, results) - } + self.load_ext("int16_t", *offset, operands, results) + } abi::Instruction::I64Load { offset } => { - self.load("int64_t", *offset, operands, results) - } + 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), @@ -2769,13 +2769,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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::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"), @@ -2792,727 +2792,727 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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); + 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); + 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); + 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); + 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); + 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}); + 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")); - } + )); + 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})")); - } + 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))); - } - } + 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); - } + // 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")); - } - } + 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()")); - } + 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}); + 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})")); + ); + 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();"); } - (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!(), - }, + 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})")); } - abi::Instruction::TupleLower { tuple, .. } => { - let op = &operands[0]; - for n in 0..tuple.types.len() { - results.push(format!("std::get<{n}>({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::>() - .join(", "), - ); - self.src.push_str(">("); - self.src.push_str(&operands.join(", ")); - self.src.push_str(");\n"); - results.push(format!("std::move({name})")); - } + 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::>() + .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)" - )); - } - } + match wit_bindgen_c::flags_repr(flags) { + Int::U8 | Int::U16 | Int::U32 => { + results.push(format!("((int32_t){})", operands.pop().unwrap())); } - abi::Instruction::FlagsLift { flags, ty, .. } => { - let typename = + Int::U64 => { + let name = 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)))" - )); - } - } + 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::VariantPayloadName => { - let name = format!("payload{}", self.tmp()); - results.push(name.clone()); - self.payloads.push(name); + } + } + 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::>(); - let payloads = self - .payloads - .drain(self.payloads.len() - variant.cases.len()..) - .collect::>(); - - 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); - } + 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::>(); + let payloads = self + .payloads + .drain(self.payloads.len() - variant.cases.len()..) + .collect::>(); + + 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]); + 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); + 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"); + 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::>(); - 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("}"); - } + 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::>(); + 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 = 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("}"); + // if named_enum { + // result.push_str("#[cfg(debug_assertions)]"); + // } + let blocks: Vec = 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); - } + 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])); - } + 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};"); - } + 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();"); + 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, - "\ + 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 (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}; + 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})")); - } + 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};"); - } + 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() - }; + 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, - "\ + 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 (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}; + let tmp = self.tmp(); + let resultname = self.tempname("result", tmp); + uwriteln!( + self.src, + "{full_type} {resultname}; if ({operand}==0) {{ {ok} {resultname}.emplace({ok_result}); @@ -3520,356 +3520,356 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { {err} {resultname}={err_type}{{{err_result}}}; }}" - ); - results.push(resultname); - } + ); + results.push(resultname); + } abi::Instruction::CallWasm { - name, - sig, - module_prefix, - } => { - let module_name = self + name, + sig, + module_prefix, + } => { + let module_name = self + .gen + .wasm_import_module + .as_ref() + .map(|e| { + self.gen .gen - .wasm_import_module + .import_prefix .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\ + .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) - } + 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) } } - 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")) + 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) } - 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()); + } + WasmType::PointerOrI64 => { + uwrite!(self.src, "WASM_I64_VAL({}),", value) + } } - self.src.push_str(&func); - self.src.push_str("("); - self.src.push_str(&operands.join(", ")); - self.src.push_str(");\n"); } + self.src.push_str("};\n"); + } else { + uwriteln!(self.src, "wasm_val_t *wasm_args = nullptr;"); } - 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})->",); + 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")) + } } - } else { - if matches!(func.kind, FunctionKind::Constructor(_)) - && self.gen.gen.opts.host_side() - { - let _ = namespace.pop(); + Some(WasmType::Length) => { + if self.gen.gen.opts.wasm64 { + (String::from("WASM_I64"), String::from("i64")) + } else { + (String::from("WASM_I32"), String::from("i32")) + } } - 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"); + 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("("); - 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 "), } - self.push_str(&operands.join(", ")); - if false - && matches!(func.kind, FunctionKind::Constructor(_)) - && !self.gen.gen.opts.is_only_handle(self.variant) + if let Some(CabiPostInformation { + module: _, + name: _cabi_post_name, + ret_type: cabi_post_type, + }) = self.cabi_post.as_ref() { - // acquire object from unique_ptr - self.push_str(").release();"); - results[0] = format!("(*({}))", results[0]); + 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 { - self.push_str(");\n"); + 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 self.needs_dealloc { - uwriteln!( - self.src, - "for (auto i: _deallocate) {{ free(i); }}\n - _deallocate.clear();" - ); + 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)); + } } - } - 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(); + 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"); + ); + } 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]); - } + 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, "}}"); - } + 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, "}}"); - } + 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::>(); - - 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"); - } + let blocks = self + .blocks + .drain(self.blocks.len() - blocks..) + .collect::>(); + + 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) - } + 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) - } + 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) - } + 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 { .. } => { - self.src.push_str("future_lower()"); - results.push(String::from("future")); - } + self.src.push_str("future_lower()"); + results.push(String::from("future")); + } abi::Instruction::FutureLift { .. } => { - self.src.push_str("future_lift()"); - results.push(String::from("future")); - } + self.src.push_str("future_lift()"); + results.push(String::from("future")); + } abi::Instruction::StreamLower { .. } => todo!(), abi::Instruction::StreamLift { .. } => todo!(), abi::Instruction::ErrorContextLower { .. } => todo!(), abi::Instruction::ErrorContextLift { .. } => 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); - } - } + for i in 0..*amt { + let tmp = self.tmp(); + let result = format!("result{}", tmp); + uwriteln!(self.src, "auto {result} = {};", operands[i]); + results.push(result); + } + } abi::Instruction::AsyncTaskReturn { .. } => todo!(), } } From d9ac71dca6710905c7e82ce5e85f6dd075474f89 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 12 Apr 2025 11:05:10 +0200 Subject: [PATCH 522/672] move common parts outside of async example --- .../tests/symmetric_async/async_cpp/Makefile | 2 +- .../async_cpp/src/async_module.cpp | 43 +------------ .../async_cpp/src/module_cpp.h | 1 - .../async_cpp/src/wit-common.h | 1 - .../symmetric_async/async_cpp/src/wit-guest.h | 1 - .../cpp-client/async_support.h | 61 +++++++++++++++++++ 6 files changed, 65 insertions(+), 44 deletions(-) delete mode 120000 crates/cpp/tests/symmetric_async/async_cpp/src/module_cpp.h delete mode 120000 crates/cpp/tests/symmetric_async/async_cpp/src/wit-common.h delete mode 120000 crates/cpp/tests/symmetric_async/async_cpp/src/wit-guest.h create mode 100644 crates/symmetric_executor/cpp-client/async_support.h diff --git a/crates/cpp/tests/symmetric_async/async_cpp/Makefile b/crates/cpp/tests/symmetric_async/async_cpp/Makefile index 7bc4e0fd8..b6ce50b3a 100644 --- a/crates/cpp/tests/symmetric_async/async_cpp/Makefile +++ b/crates/cpp/tests/symmetric_async/async_cpp/Makefile @@ -1,4 +1,4 @@ -CXXFLAGS=-g -Isrc -fPIC +CXXFLAGS=-g -Isrc -fPIC -I../../../../symmetric_executor/cpp-client -I../../../helper-types LDFLAGS=-L../../../../symmetric_executor/cpp-client \ -L../../../../symmetric_executor/target/debug \ -L../target/debug/deps 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 index c10c0d327..c05b946ad 100644 --- a/crates/cpp/tests/symmetric_async/async_cpp/src/async_module.cpp +++ b/crates/cpp/tests/symmetric_async/async_cpp/src/async_module.cpp @@ -9,6 +9,7 @@ void __component_type_object_force_link_async_module_public_use_in_this_compilat #endif #include "async_module_cpp.h" #include "module_cpp.h" +#include "async_support.h" #include // realloc #include @@ -23,32 +24,10 @@ void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { return ret; } -static symmetric::runtime::symmetric_executor::CallbackState fulfil_promise(void* data) { - std::unique_ptr> ptr((std::promise*)data); - ptr->set_value(); - return symmetric::runtime::symmetric_executor::CallbackState::kReady; -} - extern "C" void* testX3AtestX2FwaitX00X5BasyncX5Dsleep(int64_t); std::future test::test::wait::Sleep(uint64_t nanoseconds) { - std::promise result; - std::future result1 = result.get_future(); - void* wait = testX3AtestX2FwaitX00X5BasyncX5Dsleep((int64_t(nanoseconds))); - if (!wait) { result.set_value(); } else { - std::unique_ptr> ptr = std::make_unique>(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* fut) { - fut->get(); - delete fut; - return symmetric::runtime::symmetric_executor::CallbackState::kReady; + return lift_event(testX3AtestX2FwaitX00X5BasyncX5Dsleep(int64_t(nanoseconds))); } extern "C" @@ -66,23 +45,7 @@ void* testX3AtestX2Fstring_delayX00X5BasyncX5Dforward(uint8_t* arg0, size_t arg1 }; 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&& result1, - symmetric::runtime::symmetric_executor::EventGenerator &&gen){ - store(result1.get()); - gen.Activate(); - }, std::move(result1), std::move(gen)); - auto fut = std::make_unique>(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(); - } + return lower_async(std::move(result1), std::move(store)); } // Component Adapters 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 deleted file mode 120000 index c18f1b904..000000000 --- a/crates/cpp/tests/symmetric_async/async_cpp/src/module_cpp.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../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 deleted file mode 120000 index 14b0cb97c..000000000 --- a/crates/cpp/tests/symmetric_async/async_cpp/src/wit-common.h +++ /dev/null @@ -1 +0,0 @@ -../../../../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 deleted file mode 120000 index 459000308..000000000 --- a/crates/cpp/tests/symmetric_async/async_cpp/src/wit-guest.h +++ /dev/null @@ -1 +0,0 @@ -../../../../helper-types/wit-guest.h \ No newline at end of file diff --git a/crates/symmetric_executor/cpp-client/async_support.h b/crates/symmetric_executor/cpp-client/async_support.h new file mode 100644 index 000000000..d00fd308b --- /dev/null +++ b/crates/symmetric_executor/cpp-client/async_support.h @@ -0,0 +1,61 @@ + +#include +#include "module_cpp.h" + +static symmetric::runtime::symmetric_executor::CallbackState fulfil_promise(void* data) { + std::unique_ptr> ptr((std::promise*)data); + ptr->set_value(); + return symmetric::runtime::symmetric_executor::CallbackState::kReady; +} + +std::future lift_event(void* event) { + std::promise result; + std::future result1 = result.get_future(); + if (!event) { + result.set_value(); + } else { + std::unique_ptr> ptr = std::make_unique>(std::move(result)); + symmetric::runtime::symmetric_executor::EventSubscription ev = symmetric::runtime::symmetric_executor::EventSubscription(wit::ResourceImportBase((uint8_t*)event)); + 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* fut) { + fut->get(); + delete fut; + return symmetric::runtime::symmetric_executor::CallbackState::kReady; +} + +template +void* lower_async(std::future &&result1, std::function &&lower_result) { + if (result1.wait_for(std::chrono::seconds::zero()) == std::future_status::ready) { + lower_result(result1.get()); + return nullptr; + } else { + symmetric::runtime::symmetric_executor::EventGenerator gen; + auto waiting = gen.Subscribe(); + auto task = std::async(std::launch::async, [lower_result](std::future&& result1, + symmetric::runtime::symmetric_executor::EventGenerator &&gen){ + lower_result(result1.get()); + gen.Activate(); + }, std::move(result1), std::move(gen)); + auto fut = std::make_unique>(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(); + } +} + +template +std::future lift_future(uint8_t* stream) { + +} + +template +uint8_t* lower_future(std::future &&f) { + +} From bcbc00082c2d5dba56337230d6f3cf9a364485f4 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 13 Apr 2025 12:48:21 +0200 Subject: [PATCH 523/672] future support compiles (reading is work in progress) --- .../symmetric_future/future_cpp/Makefile | 13 ++++ .../symmetric_future/future_cpp/future.cpp | 9 +++ .../future_cpp/future_world.cpp | 6 +- .../cpp-client/async_support.h | 66 ++++++++++++++++++- 4 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 crates/cpp/tests/symmetric_future/future_cpp/future.cpp diff --git a/crates/cpp/tests/symmetric_future/future_cpp/Makefile b/crates/cpp/tests/symmetric_future/future_cpp/Makefile index e69de29bb..4c413a300 100644 --- a/crates/cpp/tests/symmetric_future/future_cpp/Makefile +++ b/crates/cpp/tests/symmetric_future/future_cpp/Makefile @@ -0,0 +1,13 @@ +CXXFLAGS=-g -I. -fPIC -I../../../../symmetric_executor/cpp-client -I../../../helper-types +LDFLAGS=-L../../../../symmetric_executor/cpp-client \ + -L../../../../symmetric_executor/target/debug \ + -L../target/debug/deps + +libfuture.so: future_world.o future.o + $(CXX) -shared -o $@ $^ $(LDFLAGS) -lruntime -lsource -lsymmetric_executor -lsymmetric_stream + +clean: + -rm libfuture.so future_world.o future.o + +run: libfuture.so + LD_LIBRARY_PATH=.:../target/debug/deps ../target/debug/main diff --git a/crates/cpp/tests/symmetric_future/future_cpp/future.cpp b/crates/cpp/tests/symmetric_future/future_cpp/future.cpp new file mode 100644 index 000000000..2c1cd0026 --- /dev/null +++ b/crates/cpp/tests/symmetric_future/future_cpp/future.cpp @@ -0,0 +1,9 @@ +#include "future_world_cpp.h" +#include + +std::future exports::test::test::future_test::Create() { + return std::async(std::launch::async, [](){ + auto value = ::test::test::future_source::Create(); + return value.get() * 2; + }); +} diff --git a/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp b/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp index f66e65122..fc3d1b49a 100644 --- a/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp +++ b/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp @@ -8,6 +8,7 @@ void __component_type_object_force_link_future_world_public_use_in_this_compilat } #endif #include "future_world_cpp.h" +#include "async_support.h" #include // realloc extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size); @@ -26,13 +27,14 @@ extern "C" uint8_t* testX3AtestX2Ffuture_sourceX00create(); std::future test::test::future_source::Create() { auto ret = testX3AtestX2Ffuture_sourceX00create(); - future_lift();return future; + return lift_future(ret); } + extern "C" uint8_t* testX3AtestX2Ffuture_testX00create() { auto result0 = exports::test::test::future_test::Create(); - future_lower();return future; + return lower_future(std::move(result0)); } // Component Adapters diff --git a/crates/symmetric_executor/cpp-client/async_support.h b/crates/symmetric_executor/cpp-client/async_support.h index d00fd308b..870c1a020 100644 --- a/crates/symmetric_executor/cpp-client/async_support.h +++ b/crates/symmetric_executor/cpp-client/async_support.h @@ -2,7 +2,7 @@ #include #include "module_cpp.h" -static symmetric::runtime::symmetric_executor::CallbackState fulfil_promise(void* data) { +static symmetric::runtime::symmetric_executor::CallbackState fulfil_promise_void(void* data) { std::unique_ptr> ptr((std::promise*)data); ptr->set_value(); return symmetric::runtime::symmetric_executor::CallbackState::kReady; @@ -16,7 +16,7 @@ std::future lift_event(void* event) { } else { std::unique_ptr> ptr = std::make_unique>(std::move(result)); symmetric::runtime::symmetric_executor::EventSubscription ev = symmetric::runtime::symmetric_executor::EventSubscription(wit::ResourceImportBase((uint8_t*)event)); - symmetric::runtime::symmetric_executor::CallbackFunction fun = symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase((uint8_t*)fulfil_promise)); + symmetric::runtime::symmetric_executor::CallbackFunction fun = symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase((uint8_t*)fulfil_promise_void)); 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)); } @@ -50,12 +50,72 @@ void* lower_async(std::future &&result1, std::function &&lower_res } } +template +struct fulfil_promise_data { + symmetric::runtime::symmetric_stream::StreamObj stream; + std::promise promise; +}; + +template +static symmetric::runtime::symmetric_executor::CallbackState fulfil_promise() { + // auto future = std::async(std::launch::async, [](std::future&& fut, future_writer &&wr){ + // }, std::move(f), std::move(handles.0)); + abort(); +} + template std::future lift_future(uint8_t* stream) { + std::promise promise; + std::future result= promise.get_future(); + auto stream2 = symmetric::runtime::symmetric_stream::StreamObj(wit::ResourceImportBase(stream)); + auto event = stream2.ReadReadySubscribe(); + std::unique_ptr> data = std::make_unique>(fulfil_promise_data{std::move(stream2), std::move(promise)}); + symmetric::runtime::symmetric_executor::Register(std::move(event), + symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase((uint8_t*)&fulfil_promise)), + symmetric::runtime::symmetric_executor::CallbackData(wit::ResourceImportBase((uint8_t*)data.release()))); + return result; +} + +template struct future_writer { + symmetric::runtime::symmetric_stream::StreamObj handle; +}; + +template struct future_reader { + symmetric::runtime::symmetric_stream::StreamObj handle; +}; +template +std::pair, future_reader> create_wasi_future() { + auto stream = symmetric::runtime::symmetric_stream::StreamObj(); + auto stream2 = stream.Clone(); + return std::make_pair, future_reader>( + future_writer{std::move(stream)}, future_reader{std::move(stream2)}); } template -uint8_t* lower_future(std::future &&f) { +struct write_to_future_data { + future_writer wr; + std::future fut; +}; +template +static symmetric::runtime::symmetric_executor::CallbackState write_to_future(void* data) { + std::unique_ptr> ptr((write_to_future_data*)data); + auto result = ptr->fut.get(); + auto buffer = ptr->wr.handle.StartWriting(); + assert(buffer.GetSize()==sizeof(T)); + T* dataptr = (T*)(buffer.GetAddress().into_handle()); + new (dataptr) T(std::move(result)); + ptr->wr.handle.FinishWriting(std::optional(std::move(buffer))); +} + +template +uint8_t* lower_future(std::future &&f) { + auto handles = create_wasi_future(); + auto wait_on = handles.first.handle.WriteReadySubscribe(); + auto fut = std::make_unique>(write_to_future_data{std::move(handles.first), std::move(f)}); + symmetric::runtime::symmetric_executor::Register(std::move(wait_on), + symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase((uint8_t*)&write_to_future)), + symmetric::runtime::symmetric_executor::CallbackData(wit::ResourceImportBase((uint8_t*)fut.release()))); + return handles.second.handle.into_handle(); } From e2e7ac40aec0ba0b1f6ce3b8032a4a67e48f85eb Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 13 Apr 2025 13:10:57 +0200 Subject: [PATCH 524/672] potentially complete implementation --- .../cpp-client/async_support.h | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/crates/symmetric_executor/cpp-client/async_support.h b/crates/symmetric_executor/cpp-client/async_support.h index 870c1a020..36b0707c4 100644 --- a/crates/symmetric_executor/cpp-client/async_support.h +++ b/crates/symmetric_executor/cpp-client/async_support.h @@ -50,17 +50,26 @@ void* lower_async(std::future &&result1, std::function &&lower_res } } +template +union MaybeUninit { + T value; + char dummy; +}; template struct fulfil_promise_data { symmetric::runtime::symmetric_stream::StreamObj stream; std::promise promise; + MaybeUninit value; }; template -static symmetric::runtime::symmetric_executor::CallbackState fulfil_promise() { - // auto future = std::async(std::launch::async, [](std::future&& fut, future_writer &&wr){ - // }, std::move(f), std::move(handles.0)); - abort(); +static symmetric::runtime::symmetric_executor::CallbackState fulfil_promise(void* data) { + std::unique_ptr> ptr((fulfil_promise_data*)data); + auto buffer = ptr->stream.ReadResult(); + ptr->promise.set_value(std::move(ptr->value.value)); + // matching in place destruction + ptr->value.value.~T(); + return symmetric::runtime::symmetric_executor::CallbackState::kReady; } template @@ -70,6 +79,11 @@ std::future lift_future(uint8_t* stream) { auto stream2 = symmetric::runtime::symmetric_stream::StreamObj(wit::ResourceImportBase(stream)); auto event = stream2.ReadReadySubscribe(); std::unique_ptr> data = std::make_unique>(fulfil_promise_data{std::move(stream2), std::move(promise)}); + symmetric::runtime::symmetric_stream::Buffer buf = symmetric::runtime::symmetric_stream::Buffer( + symmetric::runtime::symmetric_stream::Address(wit::ResourceImportBase((wit::ResourceImportBase::handle_t)&data->value.value)), + sizeof(T) + ); + data->stream.StartReading(std::move(buf)); symmetric::runtime::symmetric_executor::Register(std::move(event), symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase((uint8_t*)&fulfil_promise)), symmetric::runtime::symmetric_executor::CallbackData(wit::ResourceImportBase((uint8_t*)data.release()))); @@ -107,6 +121,7 @@ static symmetric::runtime::symmetric_executor::CallbackState write_to_future(voi T* dataptr = (T*)(buffer.GetAddress().into_handle()); new (dataptr) T(std::move(result)); ptr->wr.handle.FinishWriting(std::optional(std::move(buffer))); + return symmetric::runtime::symmetric_executor::CallbackState::kReady; } template From d36939793d52d39845869ce9a1c896dffe01e844 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 13 Apr 2025 14:10:35 +0200 Subject: [PATCH 525/672] no longer blocking on write --- .../tests/symmetric_future/main/Cargo.toml | 2 +- .../cpp-client/async_support.h | 41 +++++++++++++++---- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/crates/cpp/tests/symmetric_future/main/Cargo.toml b/crates/cpp/tests/symmetric_future/main/Cargo.toml index 82d68f2f1..4538ef8fa 100644 --- a/crates/cpp/tests/symmetric_future/main/Cargo.toml +++ b/crates/cpp/tests/symmetric_future/main/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" [dependencies] future = { path = "../future" } symmetric_executor = { path = "../../../../symmetric_executor", features = ["trace"]} -symmetric_stream = { path = "../../../../symmetric_executor/symmetric_stream" } +symmetric_stream = { path = "../../../../symmetric_executor/symmetric_stream", features = ["trace"]} wit-bindgen-symmetric-rt = { path = "../../../../symmetric_executor/rust-client" } diff --git a/crates/symmetric_executor/cpp-client/async_support.h b/crates/symmetric_executor/cpp-client/async_support.h index 36b0707c4..273831550 100644 --- a/crates/symmetric_executor/cpp-client/async_support.h +++ b/crates/symmetric_executor/cpp-client/async_support.h @@ -27,7 +27,11 @@ static symmetric::runtime::symmetric_executor::CallbackState wait_on_future(std: fut->get(); delete fut; return symmetric::runtime::symmetric_executor::CallbackState::kReady; -} +} + +// static void run_in_background() { + +// } template void* lower_async(std::future &&result1, std::function &&lower_result) { @@ -35,6 +39,7 @@ void* lower_async(std::future &&result1, std::function &&lower_res lower_result(result1.get()); return nullptr; } else { + // move to run_in_background symmetric::runtime::symmetric_executor::EventGenerator gen; auto waiting = gen.Subscribe(); auto task = std::async(std::launch::async, [lower_result](std::future&& result1, @@ -81,7 +86,7 @@ std::future lift_future(uint8_t* stream) { std::unique_ptr> data = std::make_unique>(fulfil_promise_data{std::move(stream2), std::move(promise)}); symmetric::runtime::symmetric_stream::Buffer buf = symmetric::runtime::symmetric_stream::Buffer( symmetric::runtime::symmetric_stream::Address(wit::ResourceImportBase((wit::ResourceImportBase::handle_t)&data->value.value)), - sizeof(T) + 1 //sizeof(T) ); data->stream.StartReading(std::move(buf)); symmetric::runtime::symmetric_executor::Register(std::move(event), @@ -115,12 +120,32 @@ struct write_to_future_data { template static symmetric::runtime::symmetric_executor::CallbackState write_to_future(void* data) { std::unique_ptr> ptr((write_to_future_data*)data); - auto result = ptr->fut.get(); - auto buffer = ptr->wr.handle.StartWriting(); - assert(buffer.GetSize()==sizeof(T)); - T* dataptr = (T*)(buffer.GetAddress().into_handle()); - new (dataptr) T(std::move(result)); - ptr->wr.handle.FinishWriting(std::optional(std::move(buffer))); + // is future ready? + if (ptr->fut.wait_for(std::chrono::seconds::zero()) == std::future_status::ready) { + auto buffer = ptr->wr.handle.StartWriting(); + assert(buffer.GetSize()==1); //sizeof(T)); + T* dataptr = (T*)(buffer.GetAddress().into_handle()); + auto result = ptr->fut.get(); + new (dataptr) T(std::move(result)); + ptr->wr.handle.FinishWriting(std::optional(std::move(buffer))); + } else { + // sadly there is no easier way to wait for a future in the background? + // move to run_in_background + symmetric::runtime::symmetric_executor::EventGenerator gen; + auto waiting = gen.Subscribe(); + auto task = std::async(std::launch::async, [](std::unique_ptr> &&ptr){ + auto buffer = ptr->wr.handle.StartWriting(); + // assert(buffer.GetSize()==1); //sizeof(T)); + T* dataptr = (T*)(buffer.GetAddress().into_handle()); + auto result = ptr->fut.get(); + new (dataptr) T(std::move(result)); + ptr->wr.handle.FinishWriting(std::optional(std::move(buffer))); + }, std::move(ptr)); + auto fut = std::make_unique>(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 symmetric::runtime::symmetric_executor::CallbackState::kReady; } From 47377f41c5b775e0906eff97521a3801e6d86e5a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 15 Apr 2025 00:09:03 +0200 Subject: [PATCH 526/672] future+stream c++ generation, fix code --- crates/cpp/src/lib.rs | 56 ++++++++++++++++--- .../cpp-client/async_support.h | 8 ++- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 37c0e01ea..4cd43a994 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2040,7 +2040,11 @@ impl CppInterfaceGenerator<'_> { + &self.optional_type_name(ty.as_ref(), from_namespace, flavor) + ">" } - TypeDefKind::Stream(_) => todo!(), + TypeDefKind::Stream(ty) => { + "wit::stream<".to_string() + + &self.optional_type_name(ty.as_ref(), from_namespace, flavor) + + ">" + } TypeDefKind::Type(ty) => self.type_name(ty, from_namespace, flavor), TypeDefKind::Unknown => todo!(), }, @@ -3850,16 +3854,50 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.store(ptr_type, *offset, operands) } abi::Instruction::LengthStore { offset } => self.store("size_t", *offset, operands), - abi::Instruction::FutureLower { .. } => { - self.src.push_str("future_lower()"); - results.push(String::from("future")); + abi::Instruction::FutureLower { payload, .. } => { + results.push(format!( + "lower_future<{}>({})", + self.gen.optional_type_name( + payload.as_ref(), + &self.namespace, + Flavor::InStruct + ), + operands[0] + )); } - abi::Instruction::FutureLift { .. } => { - self.src.push_str("future_lift()"); - results.push(String::from("future")); + abi::Instruction::FutureLift { payload, .. } => { + results.push(format!( + "lift_future<{}>({})", + self.gen.optional_type_name( + payload.as_ref(), + &self.namespace, + Flavor::InStruct + ), + operands[0] + )); + } + abi::Instruction::StreamLower { payload, .. } => { + results.push(format!( + "lower_stream<{}>({})", + self.gen.optional_type_name( + payload.as_ref(), + &self.namespace, + Flavor::InStruct + ), + operands[0] + )); + } + abi::Instruction::StreamLift { payload, .. } => { + results.push(format!( + "lift_stream<{}>({})", + self.gen.optional_type_name( + payload.as_ref(), + &self.namespace, + Flavor::InStruct + ), + operands[0] + )); } - abi::Instruction::StreamLower { .. } => todo!(), - abi::Instruction::StreamLift { .. } => todo!(), abi::Instruction::ErrorContextLower { .. } => todo!(), abi::Instruction::ErrorContextLift { .. } => todo!(), abi::Instruction::Flush { amt } => { diff --git a/crates/symmetric_executor/cpp-client/async_support.h b/crates/symmetric_executor/cpp-client/async_support.h index 273831550..1942e3052 100644 --- a/crates/symmetric_executor/cpp-client/async_support.h +++ b/crates/symmetric_executor/cpp-client/async_support.h @@ -127,20 +127,24 @@ static symmetric::runtime::symmetric_executor::CallbackState write_to_future(voi T* dataptr = (T*)(buffer.GetAddress().into_handle()); auto result = ptr->fut.get(); new (dataptr) T(std::move(result)); + buffer.SetSize(1); ptr->wr.handle.FinishWriting(std::optional(std::move(buffer))); } else { // sadly there is no easier way to wait for a future in the background? // move to run_in_background symmetric::runtime::symmetric_executor::EventGenerator gen; auto waiting = gen.Subscribe(); - auto task = std::async(std::launch::async, [](std::unique_ptr> &&ptr){ + auto task = std::async(std::launch::async, [](std::unique_ptr> &&ptr, + symmetric::runtime::symmetric_executor::EventGenerator &&gen){ auto buffer = ptr->wr.handle.StartWriting(); // assert(buffer.GetSize()==1); //sizeof(T)); T* dataptr = (T*)(buffer.GetAddress().into_handle()); auto result = ptr->fut.get(); new (dataptr) T(std::move(result)); + buffer.SetSize(1); ptr->wr.handle.FinishWriting(std::optional(std::move(buffer))); - }, std::move(ptr)); + gen.Activate(); + }, std::move(ptr), std::move(gen)); auto fut = std::make_unique>(std::move(task)); symmetric::runtime::symmetric_executor::Register(waiting.Dup(), symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase((uint8_t*)wait_on_future)), From 82f4fb55dc32667af0b6e74487345a80fe2a633c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 15 Apr 2025 00:17:09 +0200 Subject: [PATCH 527/672] move the variable --- crates/cpp/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 4cd43a994..b951e2c2a 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -3856,7 +3856,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::LengthStore { offset } => self.store("size_t", *offset, operands), abi::Instruction::FutureLower { payload, .. } => { results.push(format!( - "lower_future<{}>({})", + "lower_future<{}>(std::move({}))", self.gen.optional_type_name( payload.as_ref(), &self.namespace, @@ -3878,7 +3878,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } abi::Instruction::StreamLower { payload, .. } => { results.push(format!( - "lower_stream<{}>({})", + "lower_stream<{}>(std::move({}))", self.gen.optional_type_name( payload.as_ref(), &self.namespace, From 711608928feaff22e25236e1f646ae9cb79633f8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 15 Apr 2025 00:38:21 +0200 Subject: [PATCH 528/672] include future on demand --- crates/cpp/src/lib.rs | 10 +++++++++- .../tests/symmetric_future/future_cpp/future_world.cpp | 3 +-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index b951e2c2a..d22eb40b5 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -80,6 +80,7 @@ struct Includes { // needs wit types needs_wit: bool, needs_memory: bool, + needs_future: bool, } #[derive(Clone)] @@ -388,6 +389,9 @@ impl Cpp { if self.dependencies.needs_expected { self.include(""); } + if self.dependencies.needs_future { + self.include(""); + } if self.dependencies.needs_optional { self.include(""); } @@ -664,6 +668,9 @@ impl WorldGenerator for Cpp { world.name.to_shouty_snake_case(), ); } + if self.dependencies.needs_future { + uwriteln!(self.c_src_head, "#include \"async_support.h\"") + } self.finish_includes(); if self.opts.short_cut { @@ -1237,7 +1244,7 @@ impl CppInterfaceGenerator<'_> { &res.namespace, Flavor::Result(abi_variant), )); - res.result.push('>'); + // res.result.push('>'); } else { res.result = "void".into(); } @@ -2036,6 +2043,7 @@ impl CppInterfaceGenerator<'_> { } } TypeDefKind::Future(ty) => { + self.gen.dependencies.needs_future = true; "std::future<".to_string() + &self.optional_type_name(ty.as_ref(), from_namespace, flavor) + ">" diff --git a/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp b/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp index fc3d1b49a..3a2dc38ce 100644 --- a/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp +++ b/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp @@ -8,7 +8,6 @@ void __component_type_object_force_link_future_world_public_use_in_this_compilat } #endif #include "future_world_cpp.h" -#include "async_support.h" #include // realloc extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size); @@ -23,13 +22,13 @@ void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { } +#include "async_support.h" extern "C" uint8_t* testX3AtestX2Ffuture_sourceX00create(); std::future test::test::future_source::Create() { auto ret = testX3AtestX2Ffuture_sourceX00create(); return lift_future(ret); } - extern "C" uint8_t* testX3AtestX2Ffuture_testX00create() { From 56397cec8dc5fb77e09dc234aad94064e13ffeff Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 15 Apr 2025 00:49:14 +0200 Subject: [PATCH 529/672] stream example skeleton --- crates/cpp/tests/symmetric_stream/generate.sh | 1 + .../symmetric_stream/stream_cpp/Makefile | 13 +++++++ .../symmetric_stream/stream_cpp/impl.cpp | 7 ++++ .../stream_cpp/stream_world.cpp | 38 +++++++++++++++++++ .../stream_cpp/stream_world_cpp.h | 13 +++++++ 5 files changed, 72 insertions(+) create mode 100644 crates/cpp/tests/symmetric_stream/stream_cpp/Makefile create mode 100644 crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp create mode 100644 crates/cpp/tests/symmetric_stream/stream_cpp/stream_world.cpp create mode 100644 crates/cpp/tests/symmetric_stream/stream_cpp/stream_world_cpp.h diff --git a/crates/cpp/tests/symmetric_stream/generate.sh b/crates/cpp/tests/symmetric_stream/generate.sh index 21107d649..80176e26b 100755 --- a/crates/cpp/tests/symmetric_stream/generate.sh +++ b/crates/cpp/tests/symmetric_stream/generate.sh @@ -1,3 +1,4 @@ #!/bin/sh (cd stream/src ; ../../../../../../target/debug/wit-bindgen rust ../../wit/async_stream.wit --async none --symmetric) +(cd stream_cpp; ../../../../../target/debug/wit-bindgen cpp ../wit/async_stream.wit --symmetric) cargo fmt diff --git a/crates/cpp/tests/symmetric_stream/stream_cpp/Makefile b/crates/cpp/tests/symmetric_stream/stream_cpp/Makefile new file mode 100644 index 000000000..6d0513ddd --- /dev/null +++ b/crates/cpp/tests/symmetric_stream/stream_cpp/Makefile @@ -0,0 +1,13 @@ +CXXFLAGS=-g -I. -fPIC -I../../../../symmetric_executor/cpp-client -I../../../helper-types +LDFLAGS=-L../../../../symmetric_executor/cpp-client \ + -L../../../../symmetric_executor/target/debug \ + -L../target/debug/deps + +libstream.so: stream_world.o impl.o + $(CXX) -shared -o $@ $^ $(LDFLAGS) -lruntime -lsource -lsymmetric_executor -lsymmetric_stream + +clean: + -rm libstream.so stream_world.o impl.o + +run: libstream.so + LD_LIBRARY_PATH=.:../target/debug/deps ../target/debug/main diff --git a/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp b/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp new file mode 100644 index 000000000..f70924b66 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp @@ -0,0 +1,7 @@ +#include "stream_world_cpp.h" +#include + +wit::stream exports::test::test::future_test::Create() { + // create result + // while read, output value, value+1 +} diff --git a/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world.cpp b/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world.cpp new file mode 100644 index 000000000..198376ec3 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world.cpp @@ -0,0 +1,38 @@ +// 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_stream_world(void); +void __component_type_object_force_link_stream_world_public_use_in_this_compilation_unit(void) { + __component_type_object_force_link_stream_world(); +} +#endif +#include "stream_world_cpp.h" +#include // 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" uint8_t* testX3AtestX2Fstream_sourceX00create(); +wit::stream test::test::stream_source::Create() +{ + auto ret = testX3AtestX2Fstream_sourceX00create(); + return lift_stream(ret); +} +extern "C" +uint8_t* testX3AtestX2Fstream_testX00create() +{ + auto result0 = exports::test::test::stream_test::Create(); + return lower_stream(std::move(result0)); +} + +// Component Adapters diff --git a/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world_cpp.h b/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world_cpp.h new file mode 100644 index 000000000..1e2bd8baf --- /dev/null +++ b/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world_cpp.h @@ -0,0 +1,13 @@ +// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT! +#ifndef __CPP_GUEST_BINDINGS_STREAM_WORLD_H +#define __CPP_GUEST_BINDINGS_STREAM_WORLD_H +#define WIT_SYMMETRIC +#include +#include +namespace test {namespace test {namespace stream_source {wit::stream Create(); +// export_interface Interface(Id { idx: 1 }) +}}} +namespace exports {namespace test {namespace test {namespace stream_test {wit::stream Create(); +}}}} + +#endif From f5ae2a6c5d516dd863189bff5b4c7f68123d8f2c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 16 Apr 2025 01:04:20 +0200 Subject: [PATCH 530/672] ongoing work on stream support --- .../symmetric_stream/stream_cpp/impl.cpp | 18 +++++++++++-- .../stream_cpp/stream_world.cpp | 1 + .../stream_cpp/stream_world_cpp.h | 1 + .../cpp-client/async_support.h | 27 ++++++++++++++++++- .../cpp-client/stream_support.h | 15 +++++++++++ 5 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 crates/symmetric_executor/cpp-client/stream_support.h diff --git a/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp b/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp index f70924b66..d069c62bc 100644 --- a/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp +++ b/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp @@ -1,7 +1,21 @@ #include "stream_world_cpp.h" +#include "async_support.h" #include +#include +#include -wit::stream exports::test::test::future_test::Create() { - // create result +wit::stream exports::test::test::stream_test::Create() { + stream_writer wr{symmetric::runtime::symmetric_stream::StreamObj(wit::ResourceImportBase(wit::ResourceImportBase::invalid))}; + wit::stream rd{symmetric::runtime::symmetric_stream::StreamObj(wit::ResourceImportBase(wit::ResourceImportBase::invalid))}; + std::tie(wr, rd) = create_wasi_stream(); + // std::pair, wit::stream> my_stream = create_wasi_stream(); + wit::stream input = ::test::test::stream_source::Create(); + std::move(input).buffering(2).set_reader([stream = std::move(wr)](wit::span data){ + for (auto i: data) { + // stream.write(i); + // stream.write(i+1); + } + }); // while read, output value, value+1 + return rd; } diff --git a/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world.cpp b/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world.cpp index 198376ec3..fb8b9b3ee 100644 --- a/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world.cpp +++ b/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world.cpp @@ -8,6 +8,7 @@ void __component_type_object_force_link_stream_world_public_use_in_this_compilat } #endif #include "stream_world_cpp.h" +#include "async_support.h" #include // realloc extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size); diff --git a/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world_cpp.h b/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world_cpp.h index 1e2bd8baf..b77926d09 100644 --- a/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world_cpp.h +++ b/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world_cpp.h @@ -4,6 +4,7 @@ #define WIT_SYMMETRIC #include #include +#include namespace test {namespace test {namespace stream_source {wit::stream Create(); // export_interface Interface(Id { idx: 1 }) }}} diff --git a/crates/symmetric_executor/cpp-client/async_support.h b/crates/symmetric_executor/cpp-client/async_support.h index 1942e3052..0712021ec 100644 --- a/crates/symmetric_executor/cpp-client/async_support.h +++ b/crates/symmetric_executor/cpp-client/async_support.h @@ -1,6 +1,7 @@ #include #include "module_cpp.h" +#include "stream_support.h" static symmetric::runtime::symmetric_executor::CallbackState fulfil_promise_void(void* data) { std::unique_ptr> ptr((std::promise*)data); @@ -8,7 +9,7 @@ static symmetric::runtime::symmetric_executor::CallbackState fulfil_promise_void return symmetric::runtime::symmetric_executor::CallbackState::kReady; } -std::future lift_event(void* event) { +static std::future lift_event(void* event) { std::promise result; std::future result1 = result.get_future(); if (!event) { @@ -95,6 +96,11 @@ std::future lift_future(uint8_t* stream) { return result; } +template +wit::stream lift_stream(uint8_t* stream) { + return wit::stream{symmetric::runtime::symmetric_stream::StreamObj(wit::ResourceImportBase((wit::ResourceImportBase::handle_t)stream))}; +} + template struct future_writer { symmetric::runtime::symmetric_stream::StreamObj handle; }; @@ -111,6 +117,20 @@ std::pair, future_reader> create_wasi_future() { future_writer{std::move(stream)}, future_reader{std::move(stream2)}); } +template struct stream_writer { + symmetric::runtime::symmetric_stream::StreamObj handle; + + void write(T&& data); +}; + +template +std::pair, wit::stream> create_wasi_stream() { + auto stream = symmetric::runtime::symmetric_stream::StreamObj(); + auto stream2 = stream.Clone(); + return std::make_pair, wit::stream>( + stream_writer{std::move(stream)}, wit::stream{std::move(stream2)}); +} + template struct write_to_future_data { future_writer wr; @@ -163,3 +183,8 @@ uint8_t* lower_future(std::future &&f) { symmetric::runtime::symmetric_executor::CallbackData(wit::ResourceImportBase((uint8_t*)fut.release()))); return handles.second.handle.into_handle(); } + +template +uint8_t* lower_stream(wit::stream &&f) { + return f.handle.into_handle(); +} diff --git a/crates/symmetric_executor/cpp-client/stream_support.h b/crates/symmetric_executor/cpp-client/stream_support.h new file mode 100644 index 000000000..48959a301 --- /dev/null +++ b/crates/symmetric_executor/cpp-client/stream_support.h @@ -0,0 +1,15 @@ +#pragma once +#ifndef _STREAM_SUPPORT_H +#define _STREAM_SUPPORT_H + +#include "module_cpp.h" +#include + +namespace wit { template struct stream { + symmetric::runtime::symmetric_stream::StreamObj handle; + + stream buffering(uint32_t amount) &&; + void set_reader(std::function)>) &&; +}; } + +#endif From 30ebfc305cfe604d356ab8de2a51a6787f43e5bb Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 17 Apr 2025 00:08:05 +0200 Subject: [PATCH 531/672] stream write implementation --- .../symmetric_stream/stream_cpp/impl.cpp | 26 +++++--- .../cpp-client/async_support.h | 16 ++++- .../symmetric_executor/cpp-client/generate.sh | 0 .../symmetric_executor/cpp-client/module.cpp | 5 ++ .../cpp-client/module_cpp.h | 1 + .../cpp-client/stream_support.h | 65 +++++++++++++++++-- crates/symmetric_executor/generate.sh | 6 +- crates/symmetric_executor/wit/executor.wit | 2 + 8 files changed, 102 insertions(+), 19 deletions(-) mode change 100644 => 100755 crates/symmetric_executor/cpp-client/generate.sh diff --git a/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp b/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp index d069c62bc..42258ec8b 100644 --- a/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp +++ b/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp @@ -5,17 +5,23 @@ #include wit::stream exports::test::test::stream_test::Create() { - stream_writer wr{symmetric::runtime::symmetric_stream::StreamObj(wit::ResourceImportBase(wit::ResourceImportBase::invalid))}; - wit::stream rd{symmetric::runtime::symmetric_stream::StreamObj(wit::ResourceImportBase(wit::ResourceImportBase::invalid))}; - std::tie(wr, rd) = create_wasi_stream(); - // std::pair, wit::stream> my_stream = create_wasi_stream(); + auto streampair = create_wasi_stream(); + stream_writer* streampointer = std::make_unique>(std::move(streampair.first)).release(); wit::stream input = ::test::test::stream_source::Create(); - std::move(input).buffering(2).set_reader([stream = std::move(wr)](wit::span data){ - for (auto i: data) { - // stream.write(i); - // stream.write(i+1); + input.buffering(2); + std::move(input).set_reader([streampointer](wit::span data){ + if (!data.empty()) { + std::vector feed; + feed.reserve(data.size()*2); + for (auto i: data) { + feed.push_back(i); + feed.push_back(i+1); + } + streampointer->write(std::move(feed)); + } else { + // free the stream at EOF + std::unique_ptr>(streampointer); } }); - // while read, output value, value+1 - return rd; + return std::move(streampair).second; } diff --git a/crates/symmetric_executor/cpp-client/async_support.h b/crates/symmetric_executor/cpp-client/async_support.h index 0712021ec..374265040 100644 --- a/crates/symmetric_executor/cpp-client/async_support.h +++ b/crates/symmetric_executor/cpp-client/async_support.h @@ -120,7 +120,21 @@ std::pair, future_reader> create_wasi_future() { template struct stream_writer { symmetric::runtime::symmetric_stream::StreamObj handle; - void write(T&& data); + void write(std::vector&& data) { + while (!data.empty()) { + if (!handle.IsReadyToWrite()) { + symmetric::runtime::symmetric_executor::BlockOn(handle.WriteReadySubscribe()); + } + auto buffer = handle.StartWriting(); + auto capacity = buffer.Capacity(); + T* dest = (T*)buffer.GetAddress().into_handle(); + for (uint32_t i = 0; idata.size()) capacity = data.size(); + data.erase(data.begin(), data.begin() + capacity); + } + } }; template diff --git a/crates/symmetric_executor/cpp-client/generate.sh b/crates/symmetric_executor/cpp-client/generate.sh old mode 100644 new mode 100755 diff --git a/crates/symmetric_executor/cpp-client/module.cpp b/crates/symmetric_executor/cpp-client/module.cpp index b24ac613d..68d545703 100644 --- a/crates/symmetric_executor/cpp-client/module.cpp +++ b/crates/symmetric_executor/cpp-client/module.cpp @@ -35,6 +35,7 @@ extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bme 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_executorX400X2E1X2E0X00block_on(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); @@ -124,6 +125,10 @@ void symmetric::runtime::symmetric_executor::Register(EventSubscription&& trigge { symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00register(trigger.into_handle(), callback.into_handle(), data.into_handle()); } +void symmetric::runtime::symmetric_executor::BlockOn(EventSubscription&& event) +{ + symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00block_on(event.into_handle()); +} symmetric::runtime::symmetric_stream::Address::~Address() { if (handle!=nullptr) { diff --git a/crates/symmetric_executor/cpp-client/module_cpp.h b/crates/symmetric_executor/cpp-client/module_cpp.h index e4d9a7e9f..b47698546 100644 --- a/crates/symmetric_executor/cpp-client/module_cpp.h +++ b/crates/symmetric_executor/cpp-client/module_cpp.h @@ -74,6 +74,7 @@ enum class CallbackState : uint8_t { void Run(); void Register(EventSubscription&& trigger, CallbackFunction&& callback, CallbackData&& data); +void BlockOn(EventSubscription&& event); } namespace symmetric_stream {using EventSubscription = symmetric_executor::EventSubscription; class Address : public wit::ResourceImportBase{ diff --git a/crates/symmetric_executor/cpp-client/stream_support.h b/crates/symmetric_executor/cpp-client/stream_support.h index 48959a301..c34598b4c 100644 --- a/crates/symmetric_executor/cpp-client/stream_support.h +++ b/crates/symmetric_executor/cpp-client/stream_support.h @@ -5,11 +5,66 @@ #include "module_cpp.h" #include -namespace wit { template struct stream { - symmetric::runtime::symmetric_stream::StreamObj handle; +namespace wit { + template + union MaybeUninit + { + T value; + char dummy; + MaybeUninit() + : dummy() + { } + ~MaybeUninit() + { } + }; - stream buffering(uint32_t amount) &&; - void set_reader(std::function)>) &&; -}; } + template struct stream { + symmetric::runtime::symmetric_stream::StreamObj handle; + + uint32_t buffer_size = 1; + + struct background_object { + symmetric::runtime::symmetric_stream::StreamObj handle; + std::function)> reader; + std::vector> buffer; + + background_object(symmetric::runtime::symmetric_stream::StreamObj && h, + std::function)>&& r, std::vector> b) + : handle(std::move(h)), reader(std::move(r)), buffer(std::move(b)) {} + }; + + stream& buffering(uint32_t amount) { + buffer_size = amount; + return *this; + } + static symmetric::runtime::symmetric_executor::CallbackState data_available(background_object* data) { + auto buffer = data->handle.ReadResult(); + if (buffer.has_value()) { + assert(buffer->GetAddress().into_handle() == (wit::ResourceImportBase::handle_t)data->buffer.data()); + uint32_t size = buffer->GetSize(); + if (size>0) + data->reader(wit::span(&data->buffer[0].value, size)); + return symmetric::runtime::symmetric_executor::CallbackState::kPending; + } else { + data->reader(wit::span(&data->buffer[0].value, 0)); + auto release = std::unique_ptr(data); + return symmetric::runtime::symmetric_executor::CallbackState::kReady; + } + } + void set_reader(std::function)> &&fun) && { + std::vector> buffer(buffer_size, MaybeUninit()); + background_object* object = + std::make_unique(background_object{std::move(handle), std::move(fun), std::move(buffer)}).release(); + + symmetric::runtime::symmetric_stream::Buffer b( + symmetric::runtime::symmetric_stream::Address(wit::ResourceImportBase{(wit::ResourceImportBase::handle_t)object->buffer.data()}), buffer_size); + + object->handle.StartReading(std::move(b)); + symmetric::runtime::symmetric_executor::Register(object->handle.ReadReadySubscribe(), + symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase{(wit::ResourceImportBase::handle_t)&data_available}), + symmetric::runtime::symmetric_executor::CallbackData(wit::ResourceImportBase{(wit::ResourceImportBase::handle_t)object})); + } + }; +} #endif diff --git a/crates/symmetric_executor/generate.sh b/crates/symmetric_executor/generate.sh index 8f8b24ab5..ca5883e31 100755 --- a/crates/symmetric_executor/generate.sh +++ b/crates/symmetric_executor/generate.sh @@ -1,4 +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) +(cd rust-client/src;../../../../target/debug/wit-bindgen rust ../../wit -w module --symmetric --async none ; cd .. ; cargo fmt) +(cd src;../../../target/debug/wit-bindgen rust ../wit -w executor --symmetric --async none ; cd .. ; cargo fmt) +(cd symmetric_stream/src;../../../../target/debug/wit-bindgen rust ../../wit -w stream-impl --symmetric --async none ; cd .. ; cargo fmt) diff --git a/crates/symmetric_executor/wit/executor.wit b/crates/symmetric_executor/wit/executor.wit index edc79583c..f7d75701b 100644 --- a/crates/symmetric_executor/wit/executor.wit +++ b/crates/symmetric_executor/wit/executor.wit @@ -55,6 +55,8 @@ interface symmetric-executor { run: func(); /// Register a callback for an event register: func(trigger: event-subscription, callback: callback-function, data: callback-data); + /// Wait until one event occured (should logically switch between fibers) + block-on: func(event: event-subscription); } // language neutral stream implementation From 9688b23c6c4c2b0ad127470a748db49399056f87 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 17 Apr 2025 21:00:23 +0200 Subject: [PATCH 532/672] generate deleted resource copy operators to simplify compiler errors --- crates/cpp/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index d22eb40b5..eef3931fa 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2280,6 +2280,11 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> self.gen.h_src.src, "{pascal}& operator=({pascal}&&) = default;" ); + uwriteln!(self.gen.h_src.src, "{pascal}(const {pascal}&) = delete;"); + uwriteln!( + self.gen.h_src.src, + "{pascal}& operator=(const {pascal}&) = delete;" + ); self.gen.c_src.qualify(&namespc); uwriteln!( self.gen.c_src.src, From bd20db3e2a1a2906250c9819f7b9e92edd633462 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 17 Apr 2025 21:01:25 +0200 Subject: [PATCH 533/672] fix stream logic, output EOF message --- crates/cpp/tests/symmetric_stream/main/src/main.rs | 1 + crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/main/src/main.rs b/crates/cpp/tests/symmetric_stream/main/src/main.rs index ac43fe073..e9bffe53d 100644 --- a/crates/cpp/tests/symmetric_stream/main/src/main.rs +++ b/crates/cpp/tests/symmetric_stream/main/src/main.rs @@ -29,6 +29,7 @@ extern "C" fn ready(arg: *mut ()) -> CallbackState { CallbackState::Pending } else { // finished + println!("EOF on input"); CallbackState::Ready } } diff --git a/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp b/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp index 42258ec8b..a8ae91a98 100644 --- a/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp +++ b/crates/cpp/tests/symmetric_stream/stream_cpp/impl.cpp @@ -6,7 +6,7 @@ wit::stream exports::test::test::stream_test::Create() { auto streampair = create_wasi_stream(); - stream_writer* streampointer = std::make_unique>(std::move(streampair.first)).release(); + stream_writer* streampointer = std::make_unique>(std::move(std::move(streampair).first)).release(); wit::stream input = ::test::test::stream_source::Create(); input.buffering(2); std::move(input).set_reader([streampointer](wit::span data){ @@ -20,7 +20,7 @@ wit::stream exports::test::test::stream_test::Create() { streampointer->write(std::move(feed)); } else { // free the stream at EOF - std::unique_ptr>(streampointer); + auto release = std::unique_ptr>(streampointer); } }); return std::move(streampair).second; From e42ef68d1b78eea006b52ec27e33ed6b5852e059 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 17 Apr 2025 21:03:23 +0200 Subject: [PATCH 534/672] fix eof handling and requeue reading --- .../cpp-client/async_support.h | 16 +++++++++++++++- .../cpp-client/stream_support.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/crates/symmetric_executor/cpp-client/async_support.h b/crates/symmetric_executor/cpp-client/async_support.h index 374265040..ae454fdd7 100644 --- a/crates/symmetric_executor/cpp-client/async_support.h +++ b/crates/symmetric_executor/cpp-client/async_support.h @@ -128,13 +128,27 @@ template struct stream_writer { auto buffer = handle.StartWriting(); auto capacity = buffer.Capacity(); T* dest = (T*)buffer.GetAddress().into_handle(); - for (uint32_t i = 0; i(std::move(buffer))); + if (capacity>data.size()) capacity = data.size(); data.erase(data.begin(), data.begin() + capacity); } } + ~stream_writer() { + if (handle.get_handle()!=wit::ResourceImportBase::invalid) { + handle.FinishWriting(std::optional()); + } + } + stream_writer(const stream_writer&) = delete; + stream_writer& operator=(const stream_writer&) = delete; + stream_writer(stream_writer&&) = default; + stream_writer& operator=(stream_writer&&) = default; }; template diff --git a/crates/symmetric_executor/cpp-client/stream_support.h b/crates/symmetric_executor/cpp-client/stream_support.h index c34598b4c..19800e420 100644 --- a/crates/symmetric_executor/cpp-client/stream_support.h +++ b/crates/symmetric_executor/cpp-client/stream_support.h @@ -44,6 +44,7 @@ namespace wit { uint32_t size = buffer->GetSize(); if (size>0) data->reader(wit::span(&data->buffer[0].value, size)); + data->handle.StartReading(std::move(*buffer)); return symmetric::runtime::symmetric_executor::CallbackState::kPending; } else { data->reader(wit::span(&data->buffer[0].value, 0)); From fd7a07b99b2fd97d97656d328a4fa19cd98bee82 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 17 Apr 2025 21:08:21 +0200 Subject: [PATCH 535/672] report the change_event descriptor --- crates/symmetric_executor/src/lib.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 67defa149..508556b38 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -112,6 +112,18 @@ struct Executor { change_event: Option, } +impl Executor { + fn change_event(&mut self) -> EventFd { + *self.change_event.get_or_insert_with(|| { + let fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }; + if DEBUGGING { + println!("change event fd={}", fd); + } + fd + }) + } +} + static EXECUTOR: Mutex = Mutex::new(Executor { active_tasks: Vec::new(), change_event: None, @@ -127,11 +139,7 @@ impl symmetric_executor::Guest for Guest { type EventGenerator = EventGenerator; fn run() { - let change_event = *EXECUTOR - .lock() - .unwrap() - .change_event - .get_or_insert_with(|| unsafe { libc::eventfd(0, libc::EFD_NONBLOCK) }); + let change_event = EXECUTOR.lock().unwrap().change_event(); loop { let mut wait = libc::timeval { tv_sec: i64::MAX, @@ -322,9 +330,7 @@ impl symmetric_executor::Guest for Guest { 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 fd = lock.change_event(); let result = unsafe { libc::write( fd, From 909dd22c71973e0c111100919d17847fbf0f39da Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 17 Apr 2025 21:38:46 +0200 Subject: [PATCH 536/672] update tools --- Cargo.lock | 64 +++++++++++++++++++++++++++--------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8fa713fb8..9ddf8277c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -753,7 +753,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.228.0", + "wasm-encoder 0.229.0", "wit-bindgen-core", "wit-component", "wit-parser", @@ -920,11 +920,11 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.228.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6d761090983165c46c78f8a5d2205e5b1ec17f54" +version = "0.229.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d1b46998c4e6b30511b33c86f5ddb284e9b4d41d" dependencies = [ "leb128fmt", - "wasmparser 0.228.0", + "wasmparser 0.229.0", ] [[package]] @@ -945,13 +945,13 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.228.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6d761090983165c46c78f8a5d2205e5b1ec17f54" +version = "0.229.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d1b46998c4e6b30511b33c86f5ddb284e9b4d41d" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.228.0", - "wasmparser 0.228.0", + "wasm-encoder 0.229.0", + "wasmparser 0.229.0", ] [[package]] @@ -967,8 +967,8 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.228.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6d761090983165c46c78f8a5d2205e5b1ec17f54" +version = "0.229.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d1b46998c4e6b30511b33c86f5ddb284e9b4d41d" dependencies = [ "bitflags", "hashbrown", @@ -979,20 +979,20 @@ dependencies = [ [[package]] name = "wast" -version = "228.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6d761090983165c46c78f8a5d2205e5b1ec17f54" +version = "229.0.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d1b46998c4e6b30511b33c86f5ddb284e9b4d41d" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.0", - "wasm-encoder 0.228.0", + "wasm-encoder 0.229.0", ] [[package]] name = "wat" -version = "1.228.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6d761090983165c46c78f8a5d2205e5b1ec17f54" +version = "1.229.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d1b46998c4e6b30511b33c86f5ddb284e9b4d41d" dependencies = [ "wast", ] @@ -1105,8 +1105,8 @@ dependencies = [ "anyhow", "clap", "heck", - "wasm-encoder 0.228.0", - "wasm-metadata 0.228.0", + "wasm-encoder 0.229.0", + "wasm-metadata 0.229.0", "wit-bindgen-core", "wit-component", ] @@ -1118,7 +1118,7 @@ dependencies = [ "anyhow", "clap", "env_logger", - "wasm-encoder 0.228.0", + "wasm-encoder 0.229.0", "wit-bindgen-bridge", "wit-bindgen-c", "wit-bindgen-core", @@ -1148,8 +1148,8 @@ dependencies = [ "clap", "heck", "test-helpers", - "wasm-encoder 0.228.0", - "wasm-metadata 0.228.0", + "wasm-encoder 0.229.0", + "wasm-metadata 0.229.0", "wit-bindgen-c", "wit-bindgen-core", "wit-component", @@ -1163,7 +1163,7 @@ dependencies = [ "clap", "heck", "indexmap", - "wasm-metadata 0.228.0", + "wasm-metadata 0.229.0", "wit-bindgen-core", "wit-component", "wit-parser", @@ -1213,7 +1213,7 @@ dependencies = [ "serde_json", "syn", "test-helpers", - "wasm-metadata 0.228.0", + "wasm-metadata 0.229.0", "wit-bindgen", "wit-bindgen-core", "wit-bindgen-rt", @@ -1250,8 +1250,8 @@ dependencies = [ "wac-parser", "wac-types", "wasi-preview1-component-adapter-provider", - "wasm-encoder 0.228.0", - "wasmparser 0.228.0", + "wasm-encoder 0.229.0", + "wasmparser 0.229.0", "wat", "wit-bindgen-csharp", "wit-component", @@ -1260,8 +1260,8 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.228.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6d761090983165c46c78f8a5d2205e5b1ec17f54" +version = "0.229.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d1b46998c4e6b30511b33c86f5ddb284e9b4d41d" dependencies = [ "anyhow", "bitflags", @@ -1270,17 +1270,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.228.0", - "wasm-metadata 0.228.0", - "wasmparser 0.228.0", + "wasm-encoder 0.229.0", + "wasm-metadata 0.229.0", + "wasmparser 0.229.0", "wat", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.228.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#6d761090983165c46c78f8a5d2205e5b1ec17f54" +version = "0.229.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d1b46998c4e6b30511b33c86f5ddb284e9b4d41d" dependencies = [ "anyhow", "id-arena", @@ -1291,5 +1291,5 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.228.0", + "wasmparser 0.229.0", ] From a95a3aaceaaaa2382755cbb26ee0ae48b0108d20 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 23 Apr 2025 00:11:13 +0200 Subject: [PATCH 537/672] proper handling of MaybeUninit --- crates/symmetric_executor/cpp-client/async_support.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/symmetric_executor/cpp-client/async_support.h b/crates/symmetric_executor/cpp-client/async_support.h index ae454fdd7..bf135e176 100644 --- a/crates/symmetric_executor/cpp-client/async_support.h +++ b/crates/symmetric_executor/cpp-client/async_support.h @@ -60,6 +60,14 @@ template union MaybeUninit { T value; char dummy; + MaybeUninit() + : dummy() + { } + ~MaybeUninit() + { } + MaybeUninit(const MaybeUninit &) = delete; + // assume that value isn't valid yet + MaybeUninit(MaybeUninit &&b) : dummy() { } }; template struct fulfil_promise_data { From 26bd90116bbac19c69a3bb3eddee8199a5db1c76 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 23 Apr 2025 00:11:49 +0200 Subject: [PATCH 538/672] proper handling of stream copy/ctor --- crates/symmetric_executor/cpp-client/stream_support.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/symmetric_executor/cpp-client/stream_support.h b/crates/symmetric_executor/cpp-client/stream_support.h index 19800e420..1d56badcc 100644 --- a/crates/symmetric_executor/cpp-client/stream_support.h +++ b/crates/symmetric_executor/cpp-client/stream_support.h @@ -14,6 +14,9 @@ namespace wit { MaybeUninit() : dummy() { } + MaybeUninit(MaybeUninit const& b) + : dummy() + { } ~MaybeUninit() { } }; @@ -22,6 +25,10 @@ namespace wit { symmetric::runtime::symmetric_stream::StreamObj handle; uint32_t buffer_size = 1; + + static stream new_empty() { + return stream{symmetric::runtime::symmetric_stream::StreamObj(wit::ResourceImportBase()), 1}; + } struct background_object { symmetric::runtime::symmetric_stream::StreamObj handle; @@ -65,6 +72,10 @@ namespace wit { symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase{(wit::ResourceImportBase::handle_t)&data_available}), symmetric::runtime::symmetric_executor::CallbackData(wit::ResourceImportBase{(wit::ResourceImportBase::handle_t)object})); } + stream(const stream&) = delete; + stream(stream&&) = default; + stream& operator=(const stream&) = delete; + stream& operator=(stream&&) = default; }; } From 3ba8e4cb5904a267c410bbf015013dc087b51861 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 23 Apr 2025 00:15:05 +0200 Subject: [PATCH 539/672] handle inconsistent waiting states --- .../rust-client/src/async_support.rs | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 84a42a1f4..0b9b4ebd0 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -83,8 +83,9 @@ extern "C" fn symmetric_callback(obj: *mut ()) -> symmetric_executor::CallbackSt Poll::Ready(_) => CallbackState::Ready, Poll::Pending => { let state = obj.cast::(); - let waiting_for = unsafe { &mut *state }.waiting_for.take(); - super::register(waiting_for.unwrap(), symmetric_callback, obj); + if let Some(waiting_for) = unsafe { &mut *state }.waiting_for.take() { + super::register(waiting_for, symmetric_callback, obj); + } // as we registered this callback on a new event stop calling // from the old event CallbackState::Ready @@ -101,14 +102,17 @@ pub fn first_poll_sub(future: BoxFuture) -> *mut () { 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 + if let Some(waiting_for) = unsafe { &mut *state }.waiting_for.take() { + let completion_event = EventGenerator::new(); + let wait_chain = completion_event.subscribe().take_handle() as *mut (); + unsafe { &mut *state } + .completion_event + .replace(completion_event); + super::register(waiting_for, symmetric_callback, state.cast()); + wait_chain + } else { + core::ptr::null_mut() + } } } } @@ -146,6 +150,8 @@ pub fn spawn(future: impl Future + 'static + Send) { pub unsafe fn spawn_unchecked(future: impl Future) { let future1: Pin>> = Box::pin(future); let wait_for = first_poll_sub(unsafe { std::mem::transmute(future1) }); - let wait_for = unsafe { EventSubscription::from_handle(wait_for as usize) }; - drop(wait_for); + if !wait_for.is_null() { + let wait_for = unsafe { EventSubscription::from_handle(wait_for as usize) }; + drop(wait_for); + } } From 378e64d775d68ba49dc8540792b7e31196f64e92 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 23 Apr 2025 00:16:06 +0200 Subject: [PATCH 540/672] store closed handling separately from data --- .../symmetric_executor/symmetric_stream/src/lib.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index ffc8c08b1..476576e4a 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -1,7 +1,7 @@ use std::{ ptr::null_mut, sync::{ - atomic::{AtomicIsize, AtomicPtr, AtomicUsize, Ordering}, + atomic::{AtomicBool, AtomicIsize, AtomicPtr, AtomicUsize, Ordering}, Arc, }, }; @@ -65,6 +65,8 @@ struct StreamInner { ready_addr: AtomicPtr<()>, ready_size: AtomicIsize, ready_capacity: AtomicUsize, + // if the writer closes before the reader has consumed the last data + write_closed: AtomicBool, } struct StreamObj(Arc); @@ -79,6 +81,7 @@ impl GuestStreamObj for StreamObj { ready_addr: AtomicPtr::new(core::ptr::null_mut()), ready_size: AtomicIsize::new(results::BLOCKED), ready_capacity: AtomicUsize::new(0), + write_closed: AtomicBool::new(false), }; #[cfg(feature = "trace")] println!("Stream::new {:x}", inner.read_ready_event_send.handle()); @@ -87,6 +90,7 @@ impl GuestStreamObj for StreamObj { fn is_write_closed(&self) -> bool { self.0.ready_addr.load(Ordering::Acquire) as usize == EOF_MARKER + || self.0.write_closed.load(Ordering::Acquire) } fn start_reading(&self, buffer: symmetric_stream::Buffer) { @@ -159,6 +163,13 @@ impl GuestStreamObj for StreamObj { let addr = buffer.get::().get_address().take_handle() as *mut (); (elements, addr) } else { + if self.is_write_closed() { + todo!("double close"); + } + if !self.0.ready_addr.load(Ordering::Relaxed).is_null() { + self.0.write_closed.store(true, Ordering::Release); + return; + } (0, EOF_MARKER as usize as *mut ()) }; #[cfg(feature = "trace")] From ceb2551788331ef60445121f3518fa99aa20d11c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 24 Apr 2025 23:02:27 +0200 Subject: [PATCH 541/672] future lifting option --- .../cpp-client/async_support.h | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/crates/symmetric_executor/cpp-client/async_support.h b/crates/symmetric_executor/cpp-client/async_support.h index bf135e176..5f4fb0230 100644 --- a/crates/symmetric_executor/cpp-client/async_support.h +++ b/crates/symmetric_executor/cpp-client/async_support.h @@ -69,37 +69,35 @@ union MaybeUninit { // assume that value isn't valid yet MaybeUninit(MaybeUninit &&b) : dummy() { } }; -template +template struct fulfil_promise_data { symmetric::runtime::symmetric_stream::StreamObj stream; std::promise promise; - MaybeUninit value; + uint8_t value[LIFT::SIZE]; }; -template +template static symmetric::runtime::symmetric_executor::CallbackState fulfil_promise(void* data) { - std::unique_ptr> ptr((fulfil_promise_data*)data); + std::unique_ptr> ptr((fulfil_promise_data*)data); auto buffer = ptr->stream.ReadResult(); - ptr->promise.set_value(std::move(ptr->value.value)); - // matching in place destruction - ptr->value.value.~T(); + ptr->promise.set_value(LIFT::lift(ptr->value)); return symmetric::runtime::symmetric_executor::CallbackState::kReady; } -template +template std::future lift_future(uint8_t* stream) { std::promise promise; std::future result= promise.get_future(); auto stream2 = symmetric::runtime::symmetric_stream::StreamObj(wit::ResourceImportBase(stream)); auto event = stream2.ReadReadySubscribe(); - std::unique_ptr> data = std::make_unique>(fulfil_promise_data{std::move(stream2), std::move(promise)}); + std::unique_ptr> data = std::make_unique>(fulfil_promise_data{std::move(stream2), std::move(promise)}); symmetric::runtime::symmetric_stream::Buffer buf = symmetric::runtime::symmetric_stream::Buffer( - symmetric::runtime::symmetric_stream::Address(wit::ResourceImportBase((wit::ResourceImportBase::handle_t)&data->value.value)), - 1 //sizeof(T) + symmetric::runtime::symmetric_stream::Address(wit::ResourceImportBase((wit::ResourceImportBase::handle_t)&data->value)), + 1 ); data->stream.StartReading(std::move(buf)); symmetric::runtime::symmetric_executor::Register(std::move(event), - symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase((uint8_t*)&fulfil_promise)), + symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase((uint8_t*)&fulfil_promise)), symmetric::runtime::symmetric_executor::CallbackData(wit::ResourceImportBase((uint8_t*)data.release()))); return result; } From 6342519dd243f3069f1fc6a120f8babc396058cb Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 25 Apr 2025 00:43:47 +0200 Subject: [PATCH 542/672] implement the changed interface (in progress) --- .../rust-client/src/async_support.rs | 28 +- .../symmetric_executor/rust-client/src/lib.rs | 15 +- .../rust-client/src/module.rs | 534 ++++--- crates/symmetric_executor/src/executor.rs | 1338 +++++------------ crates/symmetric_executor/src/lib.rs | 15 +- .../symmetric_stream/src/stream_impl.rs | 747 +++++---- crates/symmetric_executor/wit/executor.wit | 29 +- crates/symmetric_executor/wit/world.wit | 2 +- 8 files changed, 1184 insertions(+), 1524 deletions(-) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 0b9b4ebd0..c85cab632 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -1,13 +1,11 @@ use futures::{task::Waker, FutureExt}; use std::{ - future::Future, - pin::Pin, - task::{Context, Poll, RawWaker, RawWakerVTable}, + future::Future, pin::Pin, ptr::null_mut, task::{Context, Poll, RawWaker, RawWakerVTable} }; -use crate::module::symmetric::runtime::symmetric_executor::{ - self, CallbackState, EventGenerator, EventSubscription, -}; +use crate::{module::symmetric::runtime::symmetric_executor::{ + self, EventGenerator, EventSubscription, +}, EventSubscription2}; pub use future_support::{FutureReader, FutureWriter}; pub use stream_support::{results, Stream, StreamReader, StreamWriter}; @@ -78,17 +76,19 @@ pub async fn wait_on(wait_for: EventSubscription) { .await } -extern "C" fn symmetric_callback(obj: *mut ()) -> symmetric_executor::CallbackState { +extern "C" fn symmetric_callback(obj: *mut (), event: *mut EventSubscription2) -> *mut EventSubscription2 { + drop(unsafe { EventSubscription::from_handle(event as usize) }); match unsafe { poll(obj.cast()) } { - Poll::Ready(_) => CallbackState::Ready, + Poll::Ready(_) => null_mut(), Poll::Pending => { let state = obj.cast::(); - if let Some(waiting_for) = unsafe { &mut *state }.waiting_for.take() { - super::register(waiting_for, symmetric_callback, obj); - } - // as we registered this callback on a new event stop calling - // from the old event - CallbackState::Ready + unsafe { &mut *state }.waiting_for.take().map_or(null_mut(), |evt| evt.take_handle() as *mut()).cast() + // if let Some(waiting_for) = { + // super::register(waiting_for, symmetric_callback, obj); + // } + // // as we registered this callback on a new event stop calling + // // from the old event + // CallbackState::Ready } } } diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index bd9ecc496..77dbe7377 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -1,16 +1,19 @@ use module::symmetric::runtime::symmetric_executor::{self, CallbackData, CallbackFunction}; pub use module::symmetric::runtime::symmetric_executor::{ - run, CallbackState, EventGenerator, EventSubscription, + run, EventGenerator, EventSubscription, }; pub use module::symmetric::runtime::symmetric_stream; pub mod async_support; mod module; -pub fn register( +pub struct EventSubscription2; +pub struct EventGenerator2; + +pub fn register( event: EventSubscription, - f: extern "C" fn(*mut ()) -> CallbackState, - data: *mut (), + f: extern "C" fn(*mut T, *mut EventSubscription2) -> *mut EventSubscription2, + data: *mut T, ) { let callback = unsafe { CallbackFunction::from_handle(f as *const () as usize) }; let cb_data = unsafe { CallbackData::from_handle(data as usize) }; @@ -27,7 +30,7 @@ fn cabi_realloc_wit_bindgen_0_37_0( todo!() } -pub unsafe fn subscribe_event_send_ptr(event_send: *mut ()) -> EventSubscription { +pub unsafe fn subscribe_event_send_ptr(event_send: *mut EventGenerator2) -> EventSubscription { let gen: EventGenerator = unsafe { EventGenerator::from_handle(event_send as usize) }; // (unsafe {Arc::from_raw(event_send.cast()) }); let subscription = gen.subscribe(); @@ -36,7 +39,7 @@ pub unsafe fn subscribe_event_send_ptr(event_send: *mut ()) -> EventSubscription subscription } -pub unsafe fn activate_event_send_ptr(event_send: *mut ()) { +pub unsafe fn activate_event_send_ptr(event_send: *mut EventGenerator2) { let gen: EventGenerator = unsafe { EventGenerator::from_handle(event_send as usize) }; gen.activate(); // avoid consuming the generator diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index 3f47e9e41..e019fcc7e 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -1,4 +1,4 @@ -// Generated by `wit-bindgen` 0.37.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.41.0. DO NOT EDIT! // Options used: #[allow(dead_code, clippy::all)] pub mod symmetric { @@ -6,7 +6,7 @@ pub mod symmetric { /// 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)] + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] pub mod symmetric_executor { #[used] #[doc(hidden)] @@ -17,7 +17,11 @@ pub mod symmetric { /// These pseudo-resources are just used to /// pass pointers to register /// This wraps a user provided function of type - /// `fn (callback-data) -> callback-state` + /// `fn (callback-data, event-subscription) -> null_or` + /// + /// The executor passes the subscription as the second argument, + /// the same callback is re-registered for the returned subscription + /// (returning the second arguments keeps the registration active) #[derive(Debug)] #[repr(transparent)] @@ -29,7 +33,7 @@ pub mod symmetric { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -48,24 +52,26 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-function" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_function( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_function(_handle) + }; } } } - /// This wraps opaque user data, freed by the callback once - /// it returns ready + /// This wraps opaque user data, freed by the callback when + /// it returns null (ready) #[derive(Debug)] #[repr(transparent)] @@ -77,7 +83,7 @@ pub mod symmetric { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -96,18 +102,20 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-data" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_data( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_data(_handle) + }; } } } @@ -124,7 +132,7 @@ pub mod symmetric { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -143,18 +151,20 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]event-subscription" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_subscription( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_subscription(_handle) + }; } } } @@ -171,7 +181,7 @@ pub mod symmetric { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -190,18 +200,69 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]event-generator" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_generator( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_generator(_handle) + }; + } + } + } + + /// Handle to cancel a callback registration + + #[derive(Debug)] + #[repr(transparent)] + pub struct CallbackRegistration { + handle: _rt::Resource, + } + + impl CallbackRegistration { + #[doc(hidden)] + pub unsafe fn from_handle(handle: usize) -> Self { + Self { + handle: unsafe { _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 CallbackRegistration { + #[inline] + unsafe fn drop(_handle: usize) { + { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { + #[cfg_attr( + target_arch = "wasm32", + link_name = "[resource-drop]callback-registration" + )] + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_registration( + _: usize, + ); + } + + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_registration(_handle) + }; } } } @@ -229,7 +290,7 @@ pub mod symmetric { #[doc(hidden)] pub unsafe fn _lift(val: u8) -> CallStatus { if !cfg!(debug_assertions) { - return ::core::mem::transmute(val); + return unsafe { ::core::mem::transmute(val) }; } match val { @@ -241,57 +302,23 @@ pub mod symmetric { } } - /// 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) + #[allow(async_fn_in_trait)] pub fn ready(&self) -> bool { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-subscription.ready" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Eready( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Eready( _: *mut u8, ) -> i32; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Eready((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Eready((self).handle() as *mut u8); _rt::bool_lift(ret as u8) } } @@ -299,19 +326,20 @@ pub mod symmetric { impl EventSubscription { #[allow(unused_unsafe, clippy::all)] /// Create a timeout event + #[allow(async_fn_in_trait)] pub fn from_timeout(nanoseconds: u64) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[static]event-subscription.from-timeout" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout( _: i64, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(_rt::as_i64(&nanoseconds)); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(_rt::as_i64(&nanoseconds)); EventSubscription::from_handle(ret as usize) } } @@ -319,19 +347,20 @@ pub mod symmetric { impl EventSubscription { #[allow(unused_unsafe, clippy::all)] /// Duplicate the subscription (e.g. for repeated callback registering, same cost as subscribe) + #[allow(async_fn_in_trait)] pub fn dup(&self) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-subscription.dup" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Edup( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Edup( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Edup((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Edup((self).handle() as *mut u8); EventSubscription::from_handle(ret as usize) } } @@ -339,36 +368,38 @@ pub mod symmetric { impl EventSubscription { #[allow(unused_unsafe, clippy::all)] /// Reset subscription to be inactive, only next trigger will ready it + #[allow(async_fn_in_trait)] pub fn reset(&self) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-subscription.reset" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Ereset( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Ereset( _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Ereset((self).handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Ereset((self).handle() as *mut u8); } } } impl EventGenerator { #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] pub fn new() -> Self { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[constructor]event-generator" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BconstructorX5Devent_generator( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BconstructorX5Devent_generator( ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BconstructorX5Devent_generator(); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BconstructorX5Devent_generator(); EventGenerator::from_handle(ret as usize) } } @@ -376,19 +407,20 @@ pub mod symmetric { impl EventGenerator { #[allow(unused_unsafe, clippy::all)] /// Get the receiving side (to pass to other parts of the program) + #[allow(async_fn_in_trait)] pub fn subscribe(&self) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-generator.subscribe" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Esubscribe( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Esubscribe( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Esubscribe((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Esubscribe((self).handle() as *mut u8); EventSubscription::from_handle(ret as usize) } } @@ -396,69 +428,95 @@ pub mod symmetric { impl EventGenerator { #[allow(unused_unsafe, clippy::all)] /// Trigger all subscribers + #[allow(async_fn_in_trait)] 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" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-generator.activate" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Eactivate( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Eactivate( _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Eactivate((self).handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Eactivate((self).handle() as *mut u8); + } + } + } + impl CallbackRegistration { + #[allow(unused_unsafe, clippy::all)] + /// returns the data passed to the registration + #[allow(async_fn_in_trait)] + pub fn cancel(obj: CallbackRegistration) -> CallbackData { + unsafe { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { + #[cfg_attr( + target_arch = "wasm32", + link_name = "[static]callback-registration.cancel" + )] + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Dcallback_registrationX2Ecancel( + _: *mut u8, + ) -> *mut u8; + } + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Dcallback_registrationX2Ecancel((&obj).take_handle() as *mut u8); + CallbackData::from_handle(ret as usize) } } } #[allow(unused_unsafe, clippy::all)] /// Wait until all registered events have completed + #[allow(async_fn_in_trait)] pub fn run() -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "run")] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00run(); + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00run(); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00run(); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00run(); } } #[allow(unused_unsafe, clippy::all)] /// Register a callback for an event + #[allow(async_fn_in_trait)] pub fn register( trigger: EventSubscription, callback: CallbackFunction, data: CallbackData, - ) -> () { + ) -> CallbackRegistration { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "register")] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00register( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00register( _: *mut u8, _: *mut u8, _: *mut u8, - ); + ) -> *mut u8; } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00register( + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00register( (&trigger).take_handle() as *mut u8, (&callback).take_handle() as *mut u8, (&data).take_handle() as *mut u8, ); + CallbackRegistration::from_handle(ret as usize) } } } /// language neutral stream implementation - #[allow(dead_code, unused_imports, clippy::all)] + #[allow(dead_code, async_fn_in_trait, 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 std::ptr::null_mut; + use super::super::super::_rt; pub type EventSubscription = super::super::super::symmetric::runtime::symmetric_executor::EventSubscription; @@ -473,7 +531,7 @@ pub mod symmetric { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -492,18 +550,20 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]address" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Daddress( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Daddress( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Daddress(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Daddress(_handle) + }; } } } @@ -520,7 +580,7 @@ pub mod symmetric { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -539,15 +599,17 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "[resource-drop]buffer")] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dbuffer( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dbuffer( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dbuffer(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dbuffer(_handle) + }; } } } @@ -562,7 +624,7 @@ pub mod symmetric { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -581,130 +643,138 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] #[cfg_attr(not(target_arch = "wasm32"), link(name = "symmetric_stream"))] - extern "C" { + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]stream-obj" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dstream_obj( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dstream_obj( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dstream_obj(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dstream_obj(_handle) + }; } } } impl Buffer { #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] pub fn new(addr: Address, capacity: u64) -> Self { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "[constructor]buffer")] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dbuffer( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BconstructorX5Dbuffer( _: *mut u8, _: i64, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dbuffer((&addr).take_handle() as *mut u8, _rt::as_i64(&capacity)); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BconstructorX5Dbuffer((&addr).take_handle() as *mut u8, _rt::as_i64(&capacity)); Buffer::from_handle(ret as usize) } } } impl Buffer { #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] pub fn get_address(&self) -> Address { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]buffer.get-address" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eget_address( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eget_address( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eget_address((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eget_address((self).handle() as *mut u8); Address::from_handle(ret as usize) } } } impl Buffer { #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] pub fn get_size(&self) -> u64 { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]buffer.get-size" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eget_size( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eget_size( _: *mut u8, ) -> i64; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eget_size((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eget_size((self).handle() as *mut u8); ret as u64 } } } impl Buffer { #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] pub fn set_size(&self, size: u64) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]buffer.set-size" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eset_size( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eset_size( _: *mut u8, _: i64, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Eset_size((self).handle() as *mut u8, _rt::as_i64(&size)); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eset_size((self).handle() as *mut u8, _rt::as_i64(&size)); } } } impl Buffer { #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] pub fn capacity(&self) -> u64 { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]buffer.capacity" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Ecapacity( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Ecapacity( _: *mut u8, ) -> i64; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Ecapacity((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Ecapacity((self).handle() as *mut u8); ret as u64 } } } impl StreamObj { #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] pub fn new() -> Self { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[constructor]stream-obj" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dstream_obj( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BconstructorX5Dstream_obj( ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dstream_obj(); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BconstructorX5Dstream_obj(); StreamObj::from_handle(ret as usize) } } @@ -712,19 +782,20 @@ pub mod symmetric { impl StreamObj { #[allow(unused_unsafe, clippy::all)] /// create a new instance e.g. for reading or tasks + #[allow(async_fn_in_trait)] pub fn clone(&self) -> StreamObj { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.clone" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eclone( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eclone( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eclone((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eclone((self).handle() as *mut u8); StreamObj::from_handle(ret as usize) } } @@ -732,75 +803,79 @@ pub mod symmetric { impl StreamObj { #[allow(unused_unsafe, clippy::all)] /// reading (in roughly chronological order) + #[allow(async_fn_in_trait)] pub fn is_write_closed(&self) -> bool { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.is-write-closed" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_write_closed( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eis_write_closed( _: *mut u8, ) -> i32; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_write_closed((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eis_write_closed((self).handle() as *mut u8); _rt::bool_lift(ret as u8) } } } impl StreamObj { #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] pub fn start_reading(&self, buffer: Buffer) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.start-reading" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_reading( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Estart_reading( _: *mut u8, _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_reading((self).handle() as *mut u8, (&buffer).take_handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Estart_reading((self).handle() as *mut u8, (&buffer).take_handle() as *mut u8); } } } impl StreamObj { #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] pub fn write_ready_activate(&self) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.write-ready-activate" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_activate( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_activate( _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_activate((self).handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_activate((self).handle() as *mut u8); } } } impl StreamObj { #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] pub fn read_ready_subscribe(&self) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.read-ready-subscribe" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_subscribe( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_ready_subscribe( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_subscribe((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_ready_subscribe((self).handle() as *mut u8); super::super::super::symmetric::runtime::symmetric_executor::EventSubscription::from_handle(ret as usize) } } @@ -808,37 +883,38 @@ pub mod symmetric { impl StreamObj { #[allow(unused_unsafe, clippy::all)] /// none is EOF + #[allow(async_fn_in_trait)] pub fn read_result(&self) -> Option { unsafe { #[cfg_attr(target_pointer_width = "64", repr(align(8)))] #[cfg_attr(target_pointer_width = "32", repr(align(4)))] struct RetArea( - [::core::mem::MaybeUninit; 2 * core::mem::size_of::<*const u8>()], + [::core::mem::MaybeUninit; 2 * ::core::mem::size_of::<*const u8>()], ); let mut ret_area = RetArea( [::core::mem::MaybeUninit::uninit(); - 2 * core::mem::size_of::<*const u8>()], + 2 * ::core::mem::size_of::<*const u8>()], ); let ptr0 = ret_area.0.as_mut_ptr().cast::(); - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.read-result" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_result( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_result( _: *mut u8, _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_result((self).handle() as *mut u8, ptr0); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_result((self).handle() as *mut u8, ptr0); let l1 = i32::from(*ptr0.add(0).cast::()); match l1 { 0 => None, 1 => { let e = { let l2 = *ptr0 - .add(core::mem::size_of::<*const u8>()) + .add(::core::mem::size_of::<*const u8>()) .cast::<*mut u8>(); Buffer::from_handle(l2 as usize) @@ -853,57 +929,60 @@ pub mod symmetric { impl StreamObj { #[allow(unused_unsafe, clippy::all)] /// writing + #[allow(async_fn_in_trait)] pub fn is_ready_to_write(&self) -> bool { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.is-ready-to-write" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_ready_to_write( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eis_ready_to_write( _: *mut u8, ) -> i32; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_ready_to_write((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eis_ready_to_write((self).handle() as *mut u8); _rt::bool_lift(ret as u8) } } } impl StreamObj { #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] pub fn write_ready_subscribe(&self) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.write-ready-subscribe" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_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)] + #[allow(async_fn_in_trait)] pub fn start_writing(&self) -> Buffer { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.start-writing" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_writing( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Estart_writing( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Estart_writing((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Estart_writing((self).handle() as *mut u8); Buffer::from_handle(ret as usize) } } @@ -911,43 +990,45 @@ pub mod symmetric { impl StreamObj { #[allow(unused_unsafe, clippy::all)] /// none is EOF + #[allow(async_fn_in_trait)] pub fn finish_writing(&self, buffer: Option) -> () { unsafe { let (result0_0, result0_1) = match &buffer { Some(e) => (1i32, (e).take_handle() as *mut u8), - None => (0i32, core::ptr::null_mut()), + None => (0i32, null_mut()), }; - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.finish-writing" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Efinish_writing( _: *mut u8, _: i32, _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing((self).handle() as *mut u8, result0_0, result0_1); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Efinish_writing((self).handle() as *mut u8, result0_0, result0_1); } } } impl StreamObj { #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] pub fn read_ready_activate(&self) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.read-ready-activate" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_activate( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_ready_activate( _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_activate((self).handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_ready_activate((self).handle() as *mut u8); } } } @@ -1094,51 +1175,52 @@ mod _rt { if cfg!(debug_assertions) { panic!("invalid enum discriminant") } else { - core::hint::unreachable_unchecked() + unsafe { 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")] +#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:symmetric:runtime@0.2.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"; +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1759] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xe2\x0c\x01A\x02\x01\ +A\x05\x01B\"\x04\0\x11callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\x04\ +\0\x12event-subscription\x03\x01\x04\0\x0fevent-generator\x03\x01\x04\0\x15callb\ +ack-registration\x03\x01\x01m\x02\x07started\x0bnot-started\x04\0\x0bcall-status\ +\x03\0\x05\x01h\x02\x01@\x01\x04self\x07\0\x7f\x04\0\x20[method]event-subscripti\ +on.ready\x01\x08\x01i\x02\x01@\x01\x0bnanosecondsw\0\x09\x04\0'[static]event-sub\ +scription.from-timeout\x01\x0a\x01@\x01\x04self\x07\0\x09\x04\0\x1e[method]event\ +-subscription.dup\x01\x0b\x01@\x01\x04self\x07\x01\0\x04\0\x20[method]event-subs\ +cription.reset\x01\x0c\x01i\x03\x01@\0\0\x0d\x04\0\x1c[constructor]event-generat\ +or\x01\x0e\x01h\x03\x01@\x01\x04self\x0f\0\x09\x04\0![method]event-generator.sub\ +scribe\x01\x10\x01@\x01\x04self\x0f\x01\0\x04\0\x20[method]event-generator.activ\ +ate\x01\x11\x01i\x04\x01i\x01\x01@\x01\x03obj\x12\0\x13\x04\0$[static]callback-r\ +egistration.cancel\x01\x14\x01@\0\x01\0\x04\0\x03run\x01\x15\x01i\0\x01@\x03\x07\ +trigger\x09\x08callback\x16\x04data\x13\0\x12\x04\0\x08register\x01\x17\x03\0*sy\ +mmetric:runtime/symmetric-executor@0.2.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\x04\ +self\x08\0w\x04\0\x17[method]buffer.get-size\x01\x0a\x01@\x02\x04self\x08\x04siz\ +ew\x01\0\x04\0\x17[method]buffer.set-size\x01\x0b\x04\0\x17[method]buffer.capaci\ +ty\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\x04\ +self\x0e\0\x7f\x04\0\"[method]stream-obj.is-write-closed\x01\x10\x01@\x02\x04sel\ +f\x0e\x06buffer\x06\x01\0\x04\0\x20[method]stream-obj.start-reading\x01\x11\x01@\ +\x01\x04self\x0e\x01\0\x04\0'[method]stream-obj.write-ready-activate\x01\x12\x01\ +i\x01\x01@\x01\x04self\x0e\0\x13\x04\0'[method]stream-obj.read-ready-subscribe\x01\ +\x14\x01k\x06\x01@\x01\x04self\x0e\0\x15\x04\0\x1e[method]stream-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]s\ +tream-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-act\ +ivate\x01\x12\x03\0(symmetric:runtime/symmetric-stream@0.2.0\x05\x02\x04\0\x1esy\ +mmetric:runtime/module@0.2.0\x04\0\x0b\x0c\x01\0\x06module\x03\0\0\0G\x09produce\ +rs\x01\x0cprocessed-by\x02\x0dwit-component\x070.228.0\x10wit-bindgen-rust\x060.\ +41.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/symmetric_executor/src/executor.rs b/crates/symmetric_executor/src/executor.rs index 5a827f5aa..496c836f2 100644 --- a/crates/symmetric_executor/src/executor.rs +++ b/crates/symmetric_executor/src/executor.rs @@ -1,4 +1,4 @@ -// Generated by `wit-bindgen` 0.36.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.41.0. DO NOT EDIT! // Options used: #[allow(dead_code, clippy::all)] pub mod exports { @@ -7,7 +7,7 @@ pub mod exports { /// 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)] + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] pub mod symmetric_executor { #[used] #[doc(hidden)] @@ -18,7 +18,11 @@ pub mod exports { /// These pseudo-resources are just used to /// pass pointers to register /// This wraps a user provided function of type - /// `fn (callback-data) -> callback-state` + /// `fn (callback-data, event-subscription) -> null_or` + /// + /// The executor passes the subscription as the second argument, + /// the same callback is re-registered for the returned subscription + /// (returning the second arguments keeps the registration active) #[derive(Debug)] #[repr(transparent)] @@ -64,7 +68,7 @@ pub mod exports { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -100,7 +104,8 @@ pub mod exports { #[doc(hidden)] pub unsafe fn dtor(handle: *mut u8) { Self::type_guard::(); - let _ = _rt::Box::from_raw(handle as *mut _CallbackFunctionRep); + let _ = + unsafe { _rt::Box::from_raw(handle as *mut _CallbackFunctionRep) }; } fn as_ptr(&self) -> *mut _CallbackFunctionRep { @@ -147,25 +152,27 @@ pub mod exports { unsafe fn drop(_handle: usize) { { #[link( - wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0" + wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0" )] - extern "C" { + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-function" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_function( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_function(_handle) + }; } } } - /// This wraps opaque user data, freed by the callback once - /// it returns ready + /// This wraps opaque user data, freed by the callback when + /// it returns null (ready) #[derive(Debug)] #[repr(transparent)] @@ -210,7 +217,7 @@ pub mod exports { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -246,7 +253,7 @@ pub mod exports { #[doc(hidden)] pub unsafe fn dtor(handle: *mut u8) { Self::type_guard::(); - let _ = _rt::Box::from_raw(handle as *mut _CallbackDataRep); + let _ = unsafe { _rt::Box::from_raw(handle as *mut _CallbackDataRep) }; } fn as_ptr(&self) -> *mut _CallbackDataRep { @@ -293,19 +300,21 @@ pub mod exports { unsafe fn drop(_handle: usize) { { #[link( - wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0" + wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0" )] - extern "C" { + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-data" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_data( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_data(_handle) + }; } } } @@ -356,7 +365,7 @@ pub mod exports { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -392,7 +401,8 @@ pub mod exports { #[doc(hidden)] pub unsafe fn dtor(handle: *mut u8) { Self::type_guard::(); - let _ = _rt::Box::from_raw(handle as *mut _EventSubscriptionRep); + let _ = + unsafe { _rt::Box::from_raw(handle as *mut _EventSubscriptionRep) }; } fn as_ptr(&self) -> *mut _EventSubscriptionRep { @@ -439,19 +449,21 @@ pub mod exports { unsafe fn drop(_handle: usize) { { #[link( - wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0" + wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0" )] - extern "C" { + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]event-subscription" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_subscription( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_subscription(_handle) + }; } } } @@ -502,7 +514,7 @@ pub mod exports { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -538,7 +550,7 @@ pub mod exports { #[doc(hidden)] pub unsafe fn dtor(handle: *mut u8) { Self::type_guard::(); - let _ = _rt::Box::from_raw(handle as *mut _EventGeneratorRep); + let _ = unsafe { _rt::Box::from_raw(handle as *mut _EventGeneratorRep) }; } fn as_ptr(&self) -> *mut _EventGeneratorRep { @@ -585,19 +597,173 @@ pub mod exports { unsafe fn drop(_handle: usize) { { #[link( - wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0" + wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0" )] - extern "C" { + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]event-generator" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_generator( + _: usize, + ); + } + + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_generator(_handle) + }; + } + } + } + + /// Handle to cancel a callback registration + + #[derive(Debug)] + #[repr(transparent)] + pub struct CallbackRegistration { + handle: _rt::Resource, + } + + type _CallbackRegistrationRep = Option; + + impl CallbackRegistration { + /// 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 `CallbackRegistration`. + pub fn new(val: T) -> Self { + Self::type_guard::(); + let val: _CallbackRegistrationRep = Some(val); + let ptr: *mut _CallbackRegistrationRep = + _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(&self) -> &T { + let ptr = unsafe { &*self.as_ptr::() }; + ptr.as_ref().unwrap() + } + + /// Gets mutable access to the underlying `T` which represents this + /// resource. + pub fn get_mut(&mut self) -> &mut T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.as_mut().unwrap() + } + + /// Consumes this resource and returns the underlying `T`. + pub fn into_inner(self) -> T { + let ptr = unsafe { &mut *self.as_ptr::() }; + ptr.take().unwrap() + } + + #[doc(hidden)] + pub unsafe fn from_handle(handle: usize) -> Self { + Self { + handle: unsafe { _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 `GuestCallbackRegistration` trait twice + // so guard against using it with two different types here. + #[doc(hidden)] + fn type_guard() { + use core::any::TypeId; + static mut LAST_TYPE: Option = None; + unsafe { + assert!(!cfg!(target_feature = "atomics")); + let id = TypeId::of::(); + 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(handle: *mut u8) { + Self::type_guard::(); + let _ = unsafe { + _rt::Box::from_raw(handle as *mut _CallbackRegistrationRep) + }; + } + + fn as_ptr( + &self, + ) -> *mut _CallbackRegistrationRep { + CallbackRegistration::type_guard::(); + T::_resource_rep(self.handle()).cast() + } + } + + /// A borrowed version of [`CallbackRegistration`] which represents a borrowed value + /// with the lifetime `'a`. + #[derive(Debug)] + #[repr(transparent)] + pub struct CallbackRegistrationBorrow<'a> { + rep: *mut u8, + _marker: core::marker::PhantomData<&'a CallbackRegistration>, + } + + impl<'a> CallbackRegistrationBorrow<'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(&self) -> &T { + let ptr = unsafe { &mut *self.as_ptr::() }; + 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(&self) -> *mut _CallbackRegistrationRep { + CallbackRegistration::type_guard::(); + self.rep.cast() + } + } + + unsafe impl _rt::WasmResource for CallbackRegistration { + #[inline] + unsafe fn drop(_handle: usize) { + { + #[link( + wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0" + )] + unsafe extern "C" { + #[cfg_attr( + target_arch = "wasm32", + link_name = "[resource-drop]callback-registration" + )] + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_registration( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_registration(_handle) + }; } } } @@ -627,7 +793,7 @@ pub mod exports { #[doc(hidden)] pub unsafe fn _lift(val: u8) -> CallStatus { if !cfg!(debug_assertions) { - return ::core::mem::transmute(val); + return unsafe { ::core::mem::transmute(val) }; } match val { @@ -639,160 +805,171 @@ pub mod exports { } } - /// 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)] + #[allow(non_snake_case, unused_unsafe)] 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, + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] 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 + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] 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 + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] 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()); + unsafe { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + { + T::reset(EventSubscriptionBorrow::lift(arg0 as usize).get()) + }; + } } #[doc(hidden)] - #[allow(non_snake_case)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_constructor_event_generator_cabi( ) -> *mut u8 { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); - let result0 = EventGenerator::new(T::new()); - (result0).take_handle() as *mut u8 + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] 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 + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] 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()); + unsafe { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + { + T::activate(EventGeneratorBorrow::lift(arg0 as usize).get()) + }; + } } #[doc(hidden)] - #[allow(non_snake_case)] + #[allow(non_snake_case, unused_unsafe)] + pub unsafe fn _export_static_callback_registration_cancel_cabi< + T: GuestCallbackRegistration, + >( + arg0: *mut u8, + ) -> *mut u8 { + unsafe { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + let result0 = + { T::cancel(CallbackRegistration::from_handle(arg0 as usize)) }; + (result0).take_handle() as *mut u8 + } + } + #[doc(hidden)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_run_cabi() { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); - T::run(); + unsafe { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + { + T::run() + }; + } } #[doc(hidden)] - #[allow(non_snake_case)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_register_cabi( 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), - ); + ) -> *mut u8 { + unsafe { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + let result0 = { + T::register( + EventSubscription::from_handle(arg0 as usize), + CallbackFunction::from_handle(arg1 as usize), + CallbackData::from_handle(arg2 as usize), + ) + }; + (result0).take_handle() as *mut u8 + } } pub trait Guest { type CallbackFunction: GuestCallbackFunction; type CallbackData: GuestCallbackData; type EventSubscription: GuestEventSubscription; type EventGenerator: GuestEventGenerator; + type CallbackRegistration: GuestCallbackRegistration; /// Wait until all registered events have completed + #[allow(async_fn_in_trait)] fn run() -> (); /// Register a callback for an event + #[allow(async_fn_in_trait)] fn register( trigger: EventSubscription, callback: CallbackFunction, data: CallbackData, - ) -> (); + ) -> CallbackRegistration; } #[doc(hidden)] #[allow(non_snake_case)] @@ -871,12 +1048,16 @@ pub mod exports { } /// Whether the event is active (used by poll implementation) + #[allow(async_fn_in_trait)] fn ready(&self) -> bool; /// Create a timeout event + #[allow(async_fn_in_trait)] fn from_timeout(nanoseconds: u64) -> EventSubscription; /// Duplicate the subscription (e.g. for repeated callback registering, same cost as subscribe) + #[allow(async_fn_in_trait)] fn dup(&self) -> EventSubscription; /// Reset subscription to be inactive, only next trigger will ready it + #[allow(async_fn_in_trait)] fn reset(&self) -> (); } #[doc(hidden)] @@ -905,86 +1086,131 @@ pub mod exports { handle as *mut u8 } + #[allow(async_fn_in_trait)] fn new() -> Self; /// Get the receiving side (to pass to other parts of the program) + #[allow(async_fn_in_trait)] fn subscribe(&self) -> EventSubscription; /// Trigger all subscribers + #[allow(async_fn_in_trait)] fn activate(&self) -> (); } #[doc(hidden)] + #[allow(non_snake_case)] + pub unsafe fn _export_drop_callbackRegistration_cabi< + T: GuestCallbackRegistration, + >( + arg0: usize, + ) { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + CallbackRegistration::dtor::(arg0 as *mut u8); + } + pub trait GuestCallbackRegistration: '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 + } + + /// returns the data passed to the registration + #[allow(async_fn_in_trait)] + fn cancel(obj: CallbackRegistration) -> CallbackData; + } + #[doc(hidden)] - macro_rules! __export_symmetric_runtime_symmetric_executor_0_1_0_cabi{ + macro_rules! __export_symmetric_runtime_symmetric_executor_0_2_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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Eready(arg0: *mut u8,) -> i32 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(arg0: i64,) -> *mut u8 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Edup(arg0: *mut u8,) -> *mut u8 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Ereset(arg0: *mut u8,) { + unsafe { $($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>() + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BconstructorX5Devent_generator() -> *mut u8 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Esubscribe(arg0: *mut u8,) -> *mut u8 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Eactivate(arg0: *mut u8,) { + unsafe { $($path_to_types)*::_export_method_event_generator_activate_cabi::<<$ty as $($path_to_types)*::Guest>::EventGenerator>(arg0) } + } + #[cfg_attr(target_arch = "wasm32", export_name = "[static]callback-registration.cancel")] + #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Dcallback_registrationX2Ecancel(arg0: *mut u8,) -> *mut u8 { + unsafe { $($path_to_types)*::_export_static_callback_registration_cancel_cabi::<<$ty as $($path_to_types)*::Guest>::CallbackRegistration>(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>() + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00run() { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00register(arg0: *mut u8,arg1: *mut u8,arg2: *mut u8,) -> *mut u8 { + unsafe { $($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) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_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) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_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) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_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) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_generator(arg0: usize) { $($path_to_types)*::_export_drop_eventGenerator_cabi::<<$ty as $($path_to_types)*::Guest>::EventGenerator>(arg0) } + #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_registration(arg0: usize) { + $($path_to_types)*::_export_drop_callbackRegistration_cabi::<<$ty as $($path_to_types)*::Guest>::CallbackRegistration>(arg0) + } + };); } #[doc(hidden)] - pub(crate) use __export_symmetric_runtime_symmetric_executor_0_1_0_cabi; + pub(crate) use __export_symmetric_runtime_symmetric_executor_0_2_0_cabi; } } } @@ -1092,800 +1318,11 @@ mod _rt { 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; - 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 { - handle: u32, - _phantom: PhantomData, - } - - impl fmt::Debug for FutureWriter { - 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 { - writer: Option>, - future: Pin>>, - } - - impl Future for CancelableWrite { - 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 CancelableWrite { - /// 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 { - self.cancel_mut() - } - - fn cancel_mut(&mut self) -> FutureWriter { - 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 Drop for CancelableWrite { - fn drop(&mut self) { - if self.writer.is_some() { - self.cancel_mut(); - } - } - } - - impl FutureWriter { - /// Write the specified value to this `future`. - pub fn write(self, v: T) -> CancelableWrite { - 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>> - } - 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 Drop for FutureWriter { - 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 { - reader: Option>, - future: Pin>>>, - } - - impl Future for CancelableRead { - type Output = Option; - - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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 CancelableRead { - /// 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 { - self.cancel_mut() - } - - fn cancel_mut(&mut self) -> FutureReader { - 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 Drop for CancelableRead { - fn drop(&mut self) { - if self.reader.is_some() { - self.cancel_mut(); - } - } - } - - /// Represents the readable end of a Component Model `future`. - pub struct FutureReader { - handle: u32, - _phantom: PhantomData, - } - - impl fmt::Debug for FutureReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FutureReader") - .field("handle", &self.handle) - .finish() - } - } - - impl FutureReader { - #[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 IntoFuture for FutureReader { - type Output = Option; - type IntoFuture = CancelableRead; - - /// 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>>, - 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 Drop for FutureReader { - 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; - async fn read(stream: u32, values: &mut [MaybeUninit]) -> Option; - fn cancel_write(stream: u32); - fn cancel_read(stream: u32); - fn close_writable(stream: u32); - fn close_readable(stream: u32); - } - - struct CancelWriteOnDrop { - handle: Option, - _phantom: PhantomData, - } - - impl Drop for CancelWriteOnDrop { - 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 { - handle: u32, - future: Option + 'static>>>, - _phantom: PhantomData, - } - - impl StreamWriter { - /// 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 fmt::Debug for StreamWriter { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StreamWriter") - .field("handle", &self.handle) - .finish() - } - } - - impl Sink> for StreamWriter { - type Error = Infallible; - - fn poll_ready(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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) -> 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:: { - 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:: { - 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> { - self.poll_ready(cx) - } - - fn poll_close(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - self.poll_ready(cx) - } - } - - impl Drop for StreamWriter { - 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 { - handle: Option, - _phantom: PhantomData, - } - - impl Drop for CancelReadOnDrop { - 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 { - handle: u32, - future: Option>> + 'static>>>, - _phantom: PhantomData, - } - - impl StreamReader { - /// 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 fmt::Debug for StreamReader { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("StreamReader") - .field("handle", &self.handle) - .finish() - } - } - - impl StreamReader { - #[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 Stream for StreamReader { - type Item = Vec; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - 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:: { - handle: Some(handle), - _phantom: PhantomData, - }; - Box::pin(async move { - let mut buffer = iter::repeat_with(MaybeUninit::uninit) - .take(ceiling(64 * 1024, mem::size_of::())) - .collect::>(); - - let result = if let Some(count) = - T::read(handle, &mut buffer).await - { - buffer.truncate(count); - Some(unsafe { - mem::transmute::>, Vec>(buffer) - }) - } else { - None - }; - cancel_on_drop.handle = None; - drop(cancel_on_drop); - result - }) - as Pin>> - } - Handle::LocalOpen => { - let (tx, rx) = oneshot::channel(); - entry.insert(Handle::LocalWaiting(tx)); - let mut cancel_on_drop = CancelReadOnDrop:: { - 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 Drop for StreamReader { - 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() -> (FutureWriter, FutureReader) { - 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() -> (StreamWriter, StreamReader) { - 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. +/// 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!`. /// @@ -1906,33 +1343,34 @@ mod _rt { 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); + $($path_to_types_root)*::exports::symmetric::runtime::symmetric_executor::__export_symmetric_runtime_symmetric_executor_0_2_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"] +#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:symmetric:runtime@0.2.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"; +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 836] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xc5\x05\x01A\x02\x01\ +A\x02\x01B\"\x04\0\x11callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\x04\ +\0\x12event-subscription\x03\x01\x04\0\x0fevent-generator\x03\x01\x04\0\x15callb\ +ack-registration\x03\x01\x01m\x02\x07started\x0bnot-started\x04\0\x0bcall-status\ +\x03\0\x05\x01h\x02\x01@\x01\x04self\x07\0\x7f\x04\0\x20[method]event-subscripti\ +on.ready\x01\x08\x01i\x02\x01@\x01\x0bnanosecondsw\0\x09\x04\0'[static]event-sub\ +scription.from-timeout\x01\x0a\x01@\x01\x04self\x07\0\x09\x04\0\x1e[method]event\ +-subscription.dup\x01\x0b\x01@\x01\x04self\x07\x01\0\x04\0\x20[method]event-subs\ +cription.reset\x01\x0c\x01i\x03\x01@\0\0\x0d\x04\0\x1c[constructor]event-generat\ +or\x01\x0e\x01h\x03\x01@\x01\x04self\x0f\0\x09\x04\0![method]event-generator.sub\ +scribe\x01\x10\x01@\x01\x04self\x0f\x01\0\x04\0\x20[method]event-generator.activ\ +ate\x01\x11\x01i\x04\x01i\x01\x01@\x01\x03obj\x12\0\x13\x04\0$[static]callback-r\ +egistration.cancel\x01\x14\x01@\0\x01\0\x04\0\x03run\x01\x15\x01i\0\x01@\x03\x07\ +trigger\x09\x08callback\x16\x04data\x13\0\x12\x04\0\x08register\x01\x17\x04\0*sy\ +mmetric:runtime/symmetric-executor@0.2.0\x05\0\x04\0\x20symmetric:runtime/execut\ +or@0.2.0\x04\0\x0b\x0e\x01\0\x08executor\x03\0\0\0G\x09producers\x01\x0cprocesse\ +d-by\x02\x0dwit-component\x070.228.0\x10wit-bindgen-rust\x060.41.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 508556b38..945cdee74 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -3,12 +3,12 @@ use std::{ mem::transmute, sync::{ atomic::{AtomicBool, AtomicU32, Ordering}, - Arc, Mutex, OnceLock, + Arc, Mutex, }, time::{Duration, SystemTime}, }; -use executor::exports::symmetric::runtime::symmetric_executor::{self, CallbackState}; +use executor::exports::symmetric::runtime::symmetric_executor::{self, GuestCallbackRegistration}; const DEBUGGING: bool = cfg!(feature = "trace"); const INVALID_FD: EventFd = -1; @@ -107,6 +107,12 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { } } +impl GuestCallbackRegistration for Ignore { + fn cancel(obj: symmetric_executor::CallbackRegistration) -> symmetric_executor::CallbackData { + todo!() + } +} + struct Executor { active_tasks: Vec, change_event: Option, @@ -135,6 +141,7 @@ static NEW_TASKS: Mutex> = Mutex::new(Vec::new()); impl symmetric_executor::Guest for Guest { type CallbackFunction = Ignore; type CallbackData = Ignore; + type CallbackRegistration = Ignore; type EventSubscription = EventSubscription; type EventGenerator = EventGenerator; @@ -283,9 +290,9 @@ impl symmetric_executor::Guest for Guest { trigger: symmetric_executor::EventSubscription, callback: symmetric_executor::CallbackFunction, data: symmetric_executor::CallbackData, - ) -> () { + ) -> symmetric_executor::CallbackRegistration { let trigger: EventSubscription = trigger.into_inner(); - let cb: fn(*mut ()) -> CallbackState = unsafe { transmute(callback.take_handle()) }; + let cb: fn(*mut (), *mut ()) -> *mut () = unsafe { transmute(callback.take_handle()) }; let data = data.take_handle() as *mut (); let event_fd = match &trigger.inner { EventType::Triggered { diff --git a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs index 9f6e88524..40c803f6d 100644 --- a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs +++ b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs @@ -1,4 +1,4 @@ -// Generated by `wit-bindgen` 0.37.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.41.0. DO NOT EDIT! // Options used: #[allow(dead_code, clippy::all)] pub mod symmetric { @@ -6,7 +6,7 @@ pub mod symmetric { /// 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)] + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] pub mod symmetric_executor { #[used] #[doc(hidden)] @@ -17,7 +17,11 @@ pub mod symmetric { /// These pseudo-resources are just used to /// pass pointers to register /// This wraps a user provided function of type - /// `fn (callback-data) -> callback-state` + /// `fn (callback-data, event-subscription) -> null_or` + /// + /// The executor passes the subscription as the second argument, + /// the same callback is re-registered for the returned subscription + /// (returning the second arguments keeps the registration active) #[derive(Debug)] #[repr(transparent)] @@ -29,7 +33,7 @@ pub mod symmetric { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -48,24 +52,26 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-function" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_function( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_function(_handle) + }; } } } - /// This wraps opaque user data, freed by the callback once - /// it returns ready + /// This wraps opaque user data, freed by the callback when + /// it returns null (ready) #[derive(Debug)] #[repr(transparent)] @@ -77,7 +83,7 @@ pub mod symmetric { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -96,18 +102,20 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-data" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_data( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_data(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_data(_handle) + }; } } } @@ -124,7 +132,7 @@ pub mod symmetric { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -143,18 +151,20 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]event-subscription" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_subscription( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_subscription(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_subscription(_handle) + }; } } } @@ -171,7 +181,7 @@ pub mod symmetric { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -190,18 +200,69 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]event-generator" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_generator( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Devent_generator(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_generator(_handle) + }; + } + } + } + + /// Handle to cancel a callback registration + + #[derive(Debug)] + #[repr(transparent)] + pub struct CallbackRegistration { + handle: _rt::Resource, + } + + impl CallbackRegistration { + #[doc(hidden)] + pub unsafe fn from_handle(handle: usize) -> Self { + Self { + handle: unsafe { _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 CallbackRegistration { + #[inline] + unsafe fn drop(_handle: usize) { + { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { + #[cfg_attr( + target_arch = "wasm32", + link_name = "[resource-drop]callback-registration" + )] + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_registration( + _: usize, + ); + } + + unsafe { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_registration(_handle) + }; } } } @@ -229,7 +290,7 @@ pub mod symmetric { #[doc(hidden)] pub unsafe fn _lift(val: u8) -> CallStatus { if !cfg!(debug_assertions) { - return ::core::mem::transmute(val); + return unsafe { ::core::mem::transmute(val) }; } match val { @@ -241,57 +302,23 @@ pub mod symmetric { } } - /// 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) + #[allow(async_fn_in_trait)] pub fn ready(&self) -> bool { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-subscription.ready" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Eready( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Eready( _: *mut u8, ) -> i32; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Eready((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Eready((self).handle() as *mut u8); _rt::bool_lift(ret as u8) } } @@ -299,19 +326,20 @@ pub mod symmetric { impl EventSubscription { #[allow(unused_unsafe, clippy::all)] /// Create a timeout event + #[allow(async_fn_in_trait)] pub fn from_timeout(nanoseconds: u64) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[static]event-subscription.from-timeout" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout( _: i64, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(_rt::as_i64(&nanoseconds)); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(_rt::as_i64(&nanoseconds)); EventSubscription::from_handle(ret as usize) } } @@ -319,19 +347,20 @@ pub mod symmetric { impl EventSubscription { #[allow(unused_unsafe, clippy::all)] /// Duplicate the subscription (e.g. for repeated callback registering, same cost as subscribe) + #[allow(async_fn_in_trait)] pub fn dup(&self) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-subscription.dup" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Edup( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Edup( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Edup((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Edup((self).handle() as *mut u8); EventSubscription::from_handle(ret as usize) } } @@ -339,36 +368,38 @@ pub mod symmetric { impl EventSubscription { #[allow(unused_unsafe, clippy::all)] /// Reset subscription to be inactive, only next trigger will ready it + #[allow(async_fn_in_trait)] pub fn reset(&self) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-subscription.reset" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Ereset( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Ereset( _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Ereset((self).handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Ereset((self).handle() as *mut u8); } } } impl EventGenerator { #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] pub fn new() -> Self { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[constructor]event-generator" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BconstructorX5Devent_generator( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BconstructorX5Devent_generator( ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BconstructorX5Devent_generator(); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BconstructorX5Devent_generator(); EventGenerator::from_handle(ret as usize) } } @@ -376,19 +407,20 @@ pub mod symmetric { impl EventGenerator { #[allow(unused_unsafe, clippy::all)] /// Get the receiving side (to pass to other parts of the program) + #[allow(async_fn_in_trait)] pub fn subscribe(&self) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-generator.subscribe" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Esubscribe( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Esubscribe( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Esubscribe((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Esubscribe((self).handle() as *mut u8); EventSubscription::from_handle(ret as usize) } } @@ -396,56 +428,81 @@ pub mod symmetric { impl EventGenerator { #[allow(unused_unsafe, clippy::all)] /// Trigger all subscribers + #[allow(async_fn_in_trait)] pub fn activate(&self) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-generator.activate" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Eactivate( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Eactivate( _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Eactivate((self).handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Eactivate((self).handle() as *mut u8); + } + } + } + impl CallbackRegistration { + #[allow(unused_unsafe, clippy::all)] + /// returns the data passed to the registration + #[allow(async_fn_in_trait)] + pub fn cancel(obj: CallbackRegistration) -> CallbackData { + unsafe { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { + #[cfg_attr( + target_arch = "wasm32", + link_name = "[static]callback-registration.cancel" + )] + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Dcallback_registrationX2Ecancel( + _: *mut u8, + ) -> *mut u8; + } + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Dcallback_registrationX2Ecancel((&obj).take_handle() as *mut u8); + CallbackData::from_handle(ret as usize) } } } #[allow(unused_unsafe, clippy::all)] /// Wait until all registered events have completed + #[allow(async_fn_in_trait)] pub fn run() -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "run")] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00run(); + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00run(); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00run(); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00run(); } } #[allow(unused_unsafe, clippy::all)] /// Register a callback for an event + #[allow(async_fn_in_trait)] pub fn register( trigger: EventSubscription, callback: CallbackFunction, data: CallbackData, - ) -> () { + ) -> CallbackRegistration { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + unsafe extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "register")] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00register( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00register( _: *mut u8, _: *mut u8, _: *mut u8, - ); + ) -> *mut u8; } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00register( + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00register( (&trigger).take_handle() as *mut u8, (&callback).take_handle() as *mut u8, (&data).take_handle() as *mut u8, ); + CallbackRegistration::from_handle(ret as usize) } } } @@ -456,7 +513,7 @@ pub mod exports { pub mod symmetric { pub mod runtime { /// language neutral stream implementation - #[allow(dead_code, unused_imports, clippy::all)] + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] pub mod symmetric_stream { #[used] #[doc(hidden)] @@ -509,7 +566,7 @@ pub mod exports { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -545,7 +602,7 @@ pub mod exports { #[doc(hidden)] pub unsafe fn dtor(handle: *mut u8) { Self::type_guard::(); - let _ = _rt::Box::from_raw(handle as *mut _AddressRep); + let _ = unsafe { _rt::Box::from_raw(handle as *mut _AddressRep) }; } fn as_ptr(&self) -> *mut _AddressRep { @@ -591,18 +648,20 @@ pub mod exports { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]address" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Daddress( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Daddress( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Daddress(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Daddress(_handle) + }; } } } @@ -652,7 +711,7 @@ pub mod exports { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -688,7 +747,7 @@ pub mod exports { #[doc(hidden)] pub unsafe fn dtor(handle: *mut u8) { Self::type_guard::(); - let _ = _rt::Box::from_raw(handle as *mut _BufferRep); + let _ = unsafe { _rt::Box::from_raw(handle as *mut _BufferRep) }; } fn as_ptr(&self) -> *mut _BufferRep { @@ -734,18 +793,20 @@ pub mod exports { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]buffer" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dbuffer( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dbuffer( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dbuffer(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dbuffer(_handle) + }; } } } @@ -793,7 +854,7 @@ pub mod exports { #[doc(hidden)] pub unsafe fn from_handle(handle: usize) -> Self { Self { - handle: _rt::Resource::from_handle(handle), + handle: unsafe { _rt::Resource::from_handle(handle) }, } } @@ -829,7 +890,7 @@ pub mod exports { #[doc(hidden)] pub unsafe fn dtor(handle: *mut u8) { Self::type_guard::(); - let _ = _rt::Box::from_raw(handle as *mut _StreamObjRep); + let _ = unsafe { _rt::Box::from_raw(handle as *mut _StreamObjRep) }; } fn as_ptr(&self) -> *mut _StreamObjRep { @@ -875,232 +936,284 @@ pub mod exports { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.1.0")] - extern "C" { + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]stream-obj" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dstream_obj( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dstream_obj( _: usize, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Dstream_obj(_handle); + unsafe { + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dstream_obj(_handle) + }; } } } #[doc(hidden)] - #[allow(non_snake_case)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_constructor_buffer_cabi( 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 + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_method_buffer_get_address_cabi( 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 + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_method_buffer_get_size_cabi( 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) + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_method_buffer_set_size_cabi( 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); + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_method_buffer_capacity_cabi( 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) + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_constructor_stream_obj_cabi() -> *mut u8 { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); - let result0 = StreamObj::new(T::new()); - (result0).take_handle() as *mut u8 + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_method_stream_obj_clone_cabi( 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 + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_method_stream_obj_is_write_closed_cabi( 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, + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_method_stream_obj_start_reading_cabi( 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), - ); + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] 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()); + unsafe { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + { + T::write_ready_activate(StreamObjBorrow::lift(arg0 as usize).get()) + }; + } } #[doc(hidden)] - #[allow(non_snake_case)] + #[allow(non_snake_case, unused_unsafe)] 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 + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_method_stream_obj_read_result_cabi( 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::() = (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::() = (0i32) as u8; - } - }; + unsafe { + #[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::() = (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::() = (0i32) as u8; + } + }; + } } #[doc(hidden)] - #[allow(non_snake_case)] + #[allow(non_snake_case, unused_unsafe)] 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, + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] 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 + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_method_stream_obj_start_writing_cabi( 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 + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] pub unsafe fn _export_method_stream_obj_finish_writing_cabi( arg0: *mut u8, arg1: i32, - arg2: *mut u8, + arg2: i32, ) { - #[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(), - }, - ); + unsafe { + #[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)] + #[allow(non_snake_case, unused_unsafe)] 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()); + unsafe { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + { + T::read_ready_activate(StreamObjBorrow::lift(arg0 as usize).get()) + }; + } } pub trait Guest { type Address: GuestAddress; @@ -1155,10 +1268,15 @@ pub mod exports { handle as *mut u8 } + #[allow(async_fn_in_trait)] fn new(addr: Address, capacity: u64) -> Self; + #[allow(async_fn_in_trait)] fn get_address(&self) -> Address; + #[allow(async_fn_in_trait)] fn get_size(&self) -> u64; + #[allow(async_fn_in_trait)] fn set_size(&self, size: u64) -> (); + #[allow(async_fn_in_trait)] fn capacity(&self) -> u64; } #[doc(hidden)] @@ -1185,133 +1303,145 @@ pub mod exports { handle as *mut u8 } + #[allow(async_fn_in_trait)] fn new() -> Self; /// create a new instance e.g. for reading or tasks + #[allow(async_fn_in_trait)] fn clone(&self) -> StreamObj; /// reading (in roughly chronological order) + #[allow(async_fn_in_trait)] fn is_write_closed(&self) -> bool; + #[allow(async_fn_in_trait)] fn start_reading(&self, buffer: Buffer) -> (); + #[allow(async_fn_in_trait)] fn write_ready_activate(&self) -> (); + #[allow(async_fn_in_trait)] fn read_ready_subscribe(&self) -> EventSubscription; /// none is EOF + #[allow(async_fn_in_trait)] fn read_result(&self) -> Option; /// writing + #[allow(async_fn_in_trait)] fn is_ready_to_write(&self) -> bool; + #[allow(async_fn_in_trait)] fn write_ready_subscribe(&self) -> EventSubscription; + #[allow(async_fn_in_trait)] fn start_writing(&self) -> Buffer; /// none is EOF + #[allow(async_fn_in_trait)] fn finish_writing(&self, buffer: Option) -> (); + #[allow(async_fn_in_trait)] fn read_ready_activate(&self) -> (); } #[doc(hidden)] - macro_rules! __export_symmetric_runtime_symmetric_stream_0_1_0_cabi{ + macro_rules! __export_symmetric_runtime_symmetric_stream_0_2_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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BconstructorX5Dbuffer(arg0: *mut u8,arg1: i64,) -> *mut u8 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eget_address(arg0: *mut u8,) -> *mut u8 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eget_size(arg0: *mut u8,) -> i64 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eset_size(arg0: *mut u8,arg1: i64,) { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Ecapacity(arg0: *mut u8,) -> i64 { + unsafe { $($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>() + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BconstructorX5Dstream_obj() -> *mut u8 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eclone(arg0: *mut u8,) -> *mut u8 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eis_write_closed(arg0: *mut u8,) -> i32 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Estart_reading(arg0: *mut u8,arg1: *mut u8,) { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_activate(arg0: *mut u8,) { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_ready_subscribe(arg0: *mut u8,) -> *mut u8 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_result(arg0: *mut u8,arg1: *mut u8,) { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eis_ready_to_write(arg0: *mut u8,) -> i32 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe(arg0: *mut u8,) -> *mut u8 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Estart_writing(arg0: *mut u8,) -> *mut u8 { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Efinish_writing(arg0: *mut u8,arg1: i32,arg2: i32,) { + unsafe { $($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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_ready_activate(arg0: *mut u8,) { + unsafe { $($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) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_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) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_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) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dstream_obj(arg0: usize) { + $($path_to_types)*::_export_drop_stream-obj_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0) } };); } #[doc(hidden)] - pub(crate) use __export_symmetric_runtime_symmetric_stream_0_1_0_cabi; + pub(crate) use __export_symmetric_runtime_symmetric_stream_0_2_0_cabi; } } } @@ -1462,7 +1592,7 @@ mod _rt { if cfg!(debug_assertions) { panic!("invalid enum discriminant") } else { - core::hint::unreachable_unchecked() + unsafe { core::hint::unreachable_unchecked() } } } extern crate alloc as alloc_crate; @@ -1490,52 +1620,53 @@ mod _rt { 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); + $($path_to_types_root)*::exports::symmetric::runtime::symmetric_stream::__export_symmetric_runtime_symmetric_stream_0_2_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")] +#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:symmetric:runtime@0.2.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"; +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1769] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xe7\x0c\x01A\x02\x01\ +A\x05\x01B\"\x04\0\x11callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\x04\ +\0\x12event-subscription\x03\x01\x04\0\x0fevent-generator\x03\x01\x04\0\x15callb\ +ack-registration\x03\x01\x01m\x02\x07started\x0bnot-started\x04\0\x0bcall-status\ +\x03\0\x05\x01h\x02\x01@\x01\x04self\x07\0\x7f\x04\0\x20[method]event-subscripti\ +on.ready\x01\x08\x01i\x02\x01@\x01\x0bnanosecondsw\0\x09\x04\0'[static]event-sub\ +scription.from-timeout\x01\x0a\x01@\x01\x04self\x07\0\x09\x04\0\x1e[method]event\ +-subscription.dup\x01\x0b\x01@\x01\x04self\x07\x01\0\x04\0\x20[method]event-subs\ +cription.reset\x01\x0c\x01i\x03\x01@\0\0\x0d\x04\0\x1c[constructor]event-generat\ +or\x01\x0e\x01h\x03\x01@\x01\x04self\x0f\0\x09\x04\0![method]event-generator.sub\ +scribe\x01\x10\x01@\x01\x04self\x0f\x01\0\x04\0\x20[method]event-generator.activ\ +ate\x01\x11\x01i\x04\x01i\x01\x01@\x01\x03obj\x12\0\x13\x04\0$[static]callback-r\ +egistration.cancel\x01\x14\x01@\0\x01\0\x04\0\x03run\x01\x15\x01i\0\x01@\x03\x07\ +trigger\x09\x08callback\x16\x04data\x13\0\x12\x04\0\x08register\x01\x17\x03\0*sy\ +mmetric:runtime/symmetric-executor@0.2.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\x04\ +self\x08\0w\x04\0\x17[method]buffer.get-size\x01\x0a\x01@\x02\x04self\x08\x04siz\ +ew\x01\0\x04\0\x17[method]buffer.set-size\x01\x0b\x04\0\x17[method]buffer.capaci\ +ty\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\x04\ +self\x0e\0\x7f\x04\0\"[method]stream-obj.is-write-closed\x01\x10\x01@\x02\x04sel\ +f\x0e\x06buffer\x06\x01\0\x04\0\x20[method]stream-obj.start-reading\x01\x11\x01@\ +\x01\x04self\x0e\x01\0\x04\0'[method]stream-obj.write-ready-activate\x01\x12\x01\ +i\x01\x01@\x01\x04self\x0e\0\x13\x04\0'[method]stream-obj.read-ready-subscribe\x01\ +\x14\x01k\x06\x01@\x01\x04self\x0e\0\x15\x04\0\x1e[method]stream-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]s\ +tream-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-act\ +ivate\x01\x12\x04\0(symmetric:runtime/symmetric-stream@0.2.0\x05\x02\x04\0#symme\ +tric:runtime/stream-impl@0.2.0\x04\0\x0b\x11\x01\0\x0bstream-impl\x03\0\0\0G\x09\ +producers\x01\x0cprocessed-by\x02\x0dwit-component\x070.228.0\x10wit-bindgen-rus\ +t\x060.41.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/symmetric_executor/wit/executor.wit b/crates/symmetric_executor/wit/executor.wit index f7d75701b..0755ab2c3 100644 --- a/crates/symmetric_executor/wit/executor.wit +++ b/crates/symmetric_executor/wit/executor.wit @@ -7,10 +7,14 @@ interface symmetric-executor { // pass pointers to register /// This wraps a user provided function of type - /// `fn (callback-data) -> callback-state` + /// `fn (callback-data, event-subscription) -> null_or` + /// + /// The executor passes the subscription as the second argument, + /// the same callback is re-registered for the returned subscription + /// (returning the second arguments keeps the registration active) resource callback-function; - /// This wraps opaque user data, freed by the callback once - /// it returns ready + /// This wraps opaque user data, freed by the callback when + /// it returns null (ready) resource callback-data; /// The receiving side of an event @@ -33,6 +37,12 @@ interface symmetric-executor { activate: func(); } + /// Handle to cancel a callback registration + resource callback-registration { + /// returns the data passed to the registration + cancel: static func(obj: callback-registration) -> callback-data; + } + /// 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, @@ -42,21 +52,10 @@ interface symmetric-executor { 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); - /// Wait until one event occured (should logically switch between fibers) - block-on: func(event: event-subscription); + register: func(trigger: event-subscription, callback: callback-function, data: callback-data) -> callback-registration; } // language neutral stream implementation diff --git a/crates/symmetric_executor/wit/world.wit b/crates/symmetric_executor/wit/world.wit index 57b1fbe86..09e9b4b31 100644 --- a/crates/symmetric_executor/wit/world.wit +++ b/crates/symmetric_executor/wit/world.wit @@ -1,4 +1,4 @@ -package symmetric:runtime@0.1.0; +package symmetric:runtime@0.2.0; world executor { export symmetric-executor; From 1b06421ae0b41efc5843128a570cf359a4e68a83 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 1 May 2025 21:00:03 +0200 Subject: [PATCH 543/672] keep pattern approach for the callback --- .../rust-client/src/async_support.rs | 23 ++- .../symmetric_executor/rust-client/src/lib.rs | 4 +- crates/symmetric_executor/src/lib.rs | 152 +++++++++++------- crates/symmetric_executor/wit/executor.wit | 15 +- 4 files changed, 122 insertions(+), 72 deletions(-) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index c85cab632..28a4ef6ea 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -1,11 +1,15 @@ use futures::{task::Waker, FutureExt}; use std::{ - future::Future, pin::Pin, ptr::null_mut, task::{Context, Poll, RawWaker, RawWakerVTable} + future::Future, + pin::Pin, + ptr::null_mut, + task::{Context, Poll, RawWaker, RawWakerVTable}, }; -use crate::{module::symmetric::runtime::symmetric_executor::{ - self, EventGenerator, EventSubscription, -}, EventSubscription2}; +use crate::{ + module::symmetric::runtime::symmetric_executor::{self, EventGenerator, EventSubscription}, + EventSubscription2, +}; pub use future_support::{FutureReader, FutureWriter}; pub use stream_support::{results, Stream, StreamReader, StreamWriter}; @@ -76,13 +80,20 @@ pub async fn wait_on(wait_for: EventSubscription) { .await } -extern "C" fn symmetric_callback(obj: *mut (), event: *mut EventSubscription2) -> *mut EventSubscription2 { +extern "C" fn symmetric_callback( + obj: *mut (), + event: *mut EventSubscription2, +) -> *mut EventSubscription2 { drop(unsafe { EventSubscription::from_handle(event as usize) }); match unsafe { poll(obj.cast()) } { Poll::Ready(_) => null_mut(), Poll::Pending => { let state = obj.cast::(); - unsafe { &mut *state }.waiting_for.take().map_or(null_mut(), |evt| evt.take_handle() as *mut()).cast() + unsafe { &mut *state } + .waiting_for + .take() + .map_or(null_mut(), |evt| evt.take_handle() as *mut ()) + .cast() // if let Some(waiting_for) = { // super::register(waiting_for, symmetric_callback, obj); // } diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index 77dbe7377..21acb9a53 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -1,7 +1,5 @@ use module::symmetric::runtime::symmetric_executor::{self, CallbackData, CallbackFunction}; -pub use module::symmetric::runtime::symmetric_executor::{ - run, EventGenerator, EventSubscription, -}; +pub use module::symmetric::runtime::symmetric_executor::{run, EventGenerator, EventSubscription}; pub use module::symmetric::runtime::symmetric_stream; pub mod async_support; diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 945cdee74..30001f597 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -2,7 +2,7 @@ use std::{ ffi::c_int, mem::transmute, sync::{ - atomic::{AtomicBool, AtomicU32, Ordering}, + atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}, Arc, Mutex, }, time::{Duration, SystemTime}, @@ -20,17 +20,19 @@ struct Guest; executor::export!(Guest with_types_in executor); struct Ignore; +struct OpaqueData; impl symmetric_executor::GuestCallbackFunction for Ignore {} -impl symmetric_executor::GuestCallbackData for Ignore {} +impl symmetric_executor::GuestCallbackData for OpaqueData {} +struct CallbackRegistrationInternal(usize); -impl symmetric_executor::GuestEventSubscription for EventSubscription { +impl symmetric_executor::GuestEventSubscription for EventSubscriptionInternal { 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 { + symmetric_executor::EventSubscription::new(EventSubscriptionInternal { inner: EventType::SystemTime(when), }) } @@ -64,7 +66,7 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { if DEBUGGING { println!("subscribe({:x})", Arc::as_ptr(&self.0) as usize); } - symmetric_executor::EventSubscription::new(EventSubscription { + symmetric_executor::EventSubscription::new(EventSubscriptionInternal { inner: EventType::Triggered { last_counter: AtomicU32::new(0), event: Arc::clone(&self.0), @@ -107,8 +109,8 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { } } -impl GuestCallbackRegistration for Ignore { - fn cancel(obj: symmetric_executor::CallbackRegistration) -> symmetric_executor::CallbackData { +impl GuestCallbackRegistration for CallbackRegistrationInternal { + fn cancel(_obj: symmetric_executor::CallbackRegistration) -> symmetric_executor::CallbackData { todo!() } } @@ -138,11 +140,14 @@ static EXECUTOR: Mutex = Mutex::new(Executor { static EXECUTOR_BUSY: AtomicBool = AtomicBool::new(false); static NEW_TASKS: Mutex> = Mutex::new(Vec::new()); +const CURRENT_PATTERN: *const SubscriptionPattern = 1 as *const SubscriptionPattern; +const NULL_PATTERN: *const SubscriptionPattern = core::ptr::null(); + impl symmetric_executor::Guest for Guest { type CallbackFunction = Ignore; - type CallbackData = Ignore; - type CallbackRegistration = Ignore; - type EventSubscription = EventSubscription; + type CallbackData = OpaqueData; + type CallbackRegistration = CallbackRegistrationInternal; + type EventSubscription = EventSubscriptionInternal; type EventGenerator = EventGenerator; fn run() { @@ -175,7 +180,13 @@ impl symmetric_executor::Guest for Guest { ); } task.callback.take_if(|CallbackEntry(f, data)| { - matches!((f)(*data), CallbackState::Ready) + match (f)(*data, CURRENT_PATTERN) { + NULL_PATTERN => true, + CURRENT_PATTERN => false, + event => { + let evt_subsrc = unsafe { symmetric_executor::EventSubscription::from_handle(event as usize) }; + NEW_TASKS.lock().unwrap().push(QueuedEvent::new(evt_subsrc, CallbackEntry(*f, *data))); false } + } }); } else { match &task.inner { @@ -202,8 +213,13 @@ impl symmetric_executor::Guest for Guest { tvptr = core::ptr::from_mut(&mut wait); } else { task.callback.take_if(|CallbackEntry(f, data)| { - matches!((f)(*data), CallbackState::Ready) - }); + match (f)(*data, CURRENT_PATTERN) { + NULL_PATTERN => true, + CURRENT_PATTERN => false, + event => { + let evt_subsrc = unsafe { symmetric_executor::EventSubscription::from_handle(event as usize) }; + NEW_TASKS.lock().unwrap().push(QueuedEvent::new(evt_subsrc, CallbackEntry(*f, *data))); false } + } }); } } } @@ -291,48 +307,12 @@ impl symmetric_executor::Guest for Guest { callback: symmetric_executor::CallbackFunction, data: symmetric_executor::CallbackData, ) -> symmetric_executor::CallbackRegistration { - let trigger: EventSubscription = trigger.into_inner(); - let cb: fn(*mut (), *mut ()) -> *mut () = 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 - ); - } - } - } + // let trigger: EventSubscriptionInternal = trigger.into_inner(); + let cb: CallbackType = unsafe { transmute(callback.take_handle()) }; + let data = data.take_handle() as *mut OpaqueData; + + let subscr = QueuedEvent::new(trigger, CallbackEntry(cb, data)); + let id = subscr.id; match EXECUTOR.try_lock() { Ok(mut lock) => { lock.active_tasks.push(subscr); @@ -361,6 +341,7 @@ impl symmetric_executor::Guest for Guest { } } } + symmetric_executor::CallbackRegistration::new(CallbackRegistrationInternal(id)) } } @@ -374,21 +355,74 @@ struct EventInner { struct EventGenerator(Arc>); -struct CallbackEntry(fn(*mut ()) -> CallbackState, *mut ()); +struct SubscriptionPattern; +type CallbackType = fn(*mut OpaqueData, *const SubscriptionPattern) -> *const SubscriptionPattern; +struct CallbackEntry(CallbackType, *mut OpaqueData); unsafe impl Send for CallbackEntry {} -struct EventSubscription { +struct EventSubscriptionInternal { inner: EventType, } struct QueuedEvent { + id: usize, inner: EventType, event_fd: EventFd, callback: Option, } -impl EventSubscription { +static ID_SOURCE: AtomicUsize = AtomicUsize::new(0); + +impl QueuedEvent { + fn new(event: symmetric_executor::EventSubscription, callback: CallbackEntry) -> Self { + let id = ID_SOURCE.fetch_add(1, Ordering::Relaxed); + let trigger: EventSubscriptionInternal = event.into_inner(); + let inner = trigger.inner; + let event_fd = match &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, + }; + if DEBUGGING { + match &inner { + EventType::Triggered { + last_counter: _, + event, + } => println!( + "register(Trigger {:x} fd {event_fd}, {:x},{:x})", + Arc::as_ptr(event) as usize, + callback.0 as usize, + callback.1 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(), + callback.0 as usize, + callback.1 as usize + ); + } + } + } + QueuedEvent { + id, + inner, + callback: Some(callback), + event_fd, + } + } +} + +impl EventSubscriptionInternal { fn dup(&self) -> Self { let inner = match &self.inner { EventType::Triggered { @@ -411,7 +445,7 @@ impl EventSubscription { } EventType::SystemTime(system_time) => EventType::SystemTime(*system_time), }; - EventSubscription { inner } + EventSubscriptionInternal { inner } } } diff --git a/crates/symmetric_executor/wit/executor.wit b/crates/symmetric_executor/wit/executor.wit index 0755ab2c3..0b3df24f8 100644 --- a/crates/symmetric_executor/wit/executor.wit +++ b/crates/symmetric_executor/wit/executor.wit @@ -7,11 +7,18 @@ interface symmetric-executor { // pass pointers to register /// This wraps a user provided function of type - /// `fn (callback-data, event-subscription) -> null_or` + /// `fn (callback-data, keep-pattern) -> null-or-pattern-or-new-subscription` /// - /// The executor passes the subscription as the second argument, - /// the same callback is re-registered for the returned subscription - /// (returning the second arguments keeps the registration active) + /// The executor passes a pattern representing + /// the current subscription as the second argument. + /// + /// Return value: + /// - null: deregister callback, data is assumed to be freed + /// - keep-pattern: re-register callback on the same event + /// - event-subscription: register callback on different event + /// + /// As the code is likely interested in the cancellation object + /// from a new registration, the last return value case is less preferrable. resource callback-function; /// This wraps opaque user data, freed by the callback when /// it returns null (ready) From c5c98a7f94805e7aa5b48f2338718e20d8f5f7e1 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 2 May 2025 09:16:21 +0200 Subject: [PATCH 544/672] avoid Alex latest changes to wasm-tools for now --- Cargo.lock | 7 +++---- crates/cpp/src/lib.rs | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7ad962a4a..d82e3c215 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -997,8 +997,7 @@ checksum = "ddbd7f2a9e3635abe5d4df93b12cadc8d6818079785ee4fab3719ae3c85a064e" [[package]] name = "wasm-compose" version = "0.229.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54b2e985adc26dec5d6b9d40bf95923fbabb5bc5244582430016f88a129ebf48" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d1b46998c4e6b30511b33c86f5ddb284e9b4d41d" dependencies = [ "anyhow", "heck 0.4.1", @@ -1199,7 +1198,7 @@ version = "0.1.0" dependencies = [ "anyhow", "clap", - "heck", + "heck 0.5.0", "wit-bindgen-core", "wit-component", ] @@ -1252,7 +1251,7 @@ version = "0.3.0" dependencies = [ "anyhow", "clap", - "heck", + "heck 0.5.0", "test-helpers", "wasm-encoder 0.229.0", "wasm-metadata 0.229.0", diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index eef3931fa..df41fa847 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -3922,6 +3922,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } abi::Instruction::AsyncTaskReturn { .. } => todo!(), + abi::Instruction::DropHandle { .. } => todo!(), } } From c8e738b0d94d1c9500de01f04a745636950d2e76 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 2 May 2025 10:25:57 +0200 Subject: [PATCH 545/672] close to working again --- .../symmetric_executor/cpp-client/module.cpp | 152 ++++---- .../cpp-client/module_cpp.h | 36 +- .../rust-client/src/async_support.rs | 30 +- .../symmetric_executor/rust-client/src/lib.rs | 22 +- .../rust-client/src/module.rs | 334 ++++++++++-------- crates/symmetric_executor/src/executor.rs | 150 +++++--- .../symmetric_stream/src/stream_impl.rs | 275 +++++++------- crates/symmetric_executor/wit/executor.wit | 24 +- crates/symmetric_executor/wit/world.wit | 2 +- 9 files changed, 566 insertions(+), 459 deletions(-) diff --git a/crates/symmetric_executor/cpp-client/module.cpp b/crates/symmetric_executor/cpp-client/module.cpp index 68d545703..ae22d4e74 100644 --- a/crates/symmetric_executor/cpp-client/module.cpp +++ b/crates/symmetric_executor/cpp-client/module.cpp @@ -22,190 +22,200 @@ void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { } -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_executorX400X2E1X2E0X00block_on(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*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_function(uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_data(uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_subscription(uint8_t*); +extern "C" int32_t symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Eready(uint8_t*); +extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(int64_t); +extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Edup(uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Ereset(uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_generator(uint8_t*); +extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BconstructorX5Devent_generator(); +extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_generatorX2Esubscribe(uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_generatorX2Eactivate(uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_registration(uint8_t*); +extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BstaticX5Dcallback_registrationX2Ecancel(uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00run(); +extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00register(uint8_t*, uint8_t*, uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Daddress(uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Dbuffer(uint8_t*); +extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BconstructorX5Dbuffer(uint8_t*, int64_t); +extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eget_address(uint8_t*); +extern "C" int64_t symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eget_size(uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eset_size(uint8_t*, int64_t); +extern "C" int64_t symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Ecapacity(uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Dstream_obj(uint8_t*); +extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BconstructorX5Dstream_obj(); +extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eclone(uint8_t*); +extern "C" int32_t symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eis_write_closed(uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Estart_reading(uint8_t*, uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Ewrite_ready_activate(uint8_t*); +extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_ready_subscribe(uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_result(uint8_t*, uint8_t*); +extern "C" int32_t symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eis_ready_to_write(uint8_t*); +extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe(uint8_t*); +extern "C" uint8_t* symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Estart_writing(uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Efinish_writing(uint8_t*, int32_t, uint8_t*); +extern "C" void symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_ready_activate(uint8_t*); symmetric::runtime::symmetric_executor::CallbackFunction::~CallbackFunction() { if (handle!=nullptr) { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5Bresource_dropX5Dcallback_function(handle); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_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); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_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); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_subscription(handle); } } bool symmetric::runtime::symmetric_executor::EventSubscription::Ready() const { - auto ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Eready((*this).get_handle()); + auto ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_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))); + auto ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BstaticX5Devent_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()); + auto ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Edup((*this).get_handle()); return wit::ResourceImportBase{ret}; } void symmetric::runtime::symmetric_executor::EventSubscription::Reset() const { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_subscriptionX2Ereset((*this).get_handle()); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_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); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_generator(handle); } } symmetric::runtime::symmetric_executor::EventGenerator::EventGenerator() { - auto ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BconstructorX5Devent_generator(); + auto ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BconstructorX5Devent_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()); + auto ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_generatorX2Esubscribe((*this).get_handle()); return wit::ResourceImportBase{ret}; } void symmetric::runtime::symmetric_executor::EventGenerator::Activate() const { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00X5BmethodX5Devent_generatorX2Eactivate((*this).get_handle()); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_generatorX2Eactivate((*this).get_handle()); } symmetric::runtime::symmetric_executor::EventGenerator::EventGenerator(wit::ResourceImportBase&&b) : wit::ResourceImportBase(std::move(b)) {} -void symmetric::runtime::symmetric_executor::Run() +symmetric::runtime::symmetric_executor::CallbackRegistration::~CallbackRegistration() +{ + if (handle!=nullptr) { + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_registration(handle); + } +} +symmetric::runtime::symmetric_executor::CallbackData symmetric::runtime::symmetric_executor::CallbackRegistration::Cancel(CallbackRegistration&& obj) { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00run(); + auto ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BstaticX5Dcallback_registrationX2Ecancel(obj.into_handle()); + return wit::ResourceImportBase{ret}; } -void symmetric::runtime::symmetric_executor::Register(EventSubscription&& trigger, CallbackFunction&& callback, CallbackData&& data) +symmetric::runtime::symmetric_executor::CallbackRegistration::CallbackRegistration(wit::ResourceImportBase&&b) : wit::ResourceImportBase(std::move(b)) {} +void symmetric::runtime::symmetric_executor::Run() { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00register(trigger.into_handle(), callback.into_handle(), data.into_handle()); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00run(); } -void symmetric::runtime::symmetric_executor::BlockOn(EventSubscription&& event) +symmetric::runtime::symmetric_executor::CallbackRegistration symmetric::runtime::symmetric_executor::Register(EventSubscription&& trigger, CallbackFunction&& callback, CallbackData&& data) { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E1X2E0X00block_on(event.into_handle()); + auto ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00register(trigger.into_handle(), callback.into_handle(), data.into_handle()); + return wit::ResourceImportBase{ret}; } symmetric::runtime::symmetric_stream::Address::~Address() { if (handle!=nullptr) { - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5Bresource_dropX5Daddress(handle); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_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); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Dbuffer(handle); } } symmetric::runtime::symmetric_stream::Buffer::Buffer(Address&& addr, uint64_t capacity) { - auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dbuffer(addr.into_handle(), (int64_t(capacity))); + auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BconstructorX5Dbuffer(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()); + auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eget_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()); + auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eget_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))); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eset_size((*this).get_handle(), (int64_t(size))); } uint64_t symmetric::runtime::symmetric_stream::Buffer::Capacity() const { - auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5DbufferX2Ecapacity((*this).get_handle()); + auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Ecapacity((*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); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Dstream_obj(handle); } } symmetric::runtime::symmetric_stream::StreamObj::StreamObj() { - auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BconstructorX5Dstream_obj(); + auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BconstructorX5Dstream_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()); + auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_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()); + auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_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()); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Estart_reading((*this).get_handle(), buffer.into_handle()); } void symmetric::runtime::symmetric_stream::StreamObj::WriteReadyActivate() const { - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_activate((*this).get_handle()); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_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()); + auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_ready_subscribe((*this).get_handle()); return wit::ResourceImportBase{ret}; } std::optional 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); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_result((*this).get_handle(), ptr0); std::optional option1; if ((int32_t) (*((uint8_t*) (ptr0 + 0)))) { @@ -215,17 +225,17 @@ std::optional symmetric::runtime:: } bool symmetric::runtime::symmetric_stream::StreamObj::IsReadyToWrite() const { - auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eis_ready_to_write((*this).get_handle()); + auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_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()); + auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_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()); + auto ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Estart_writing((*this).get_handle()); return wit::ResourceImportBase{ret}; } void symmetric::runtime::symmetric_stream::StreamObj::FinishWriting(std::optional &&buffer) const @@ -240,11 +250,11 @@ void symmetric::runtime::symmetric_stream::StreamObj::FinishWriting(std::optiona option2 = (int32_t(0)); option3 = nullptr; } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Efinish_writing((*this).get_handle(), option2, option3); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Efinish_writing((*this).get_handle(), option2, option3); } void symmetric::runtime::symmetric_stream::StreamObj::ReadReadyActivate() const { - symmetricX3AruntimeX2Fsymmetric_streamX400X2E1X2E0X00X5BmethodX5Dstream_objX2Eread_ready_activate((*this).get_handle()); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_ready_activate((*this).get_handle()); } symmetric::runtime::symmetric_stream::StreamObj::StreamObj(wit::ResourceImportBase&&b) : wit::ResourceImportBase(std::move(b)) {} diff --git a/crates/symmetric_executor/cpp-client/module_cpp.h b/crates/symmetric_executor/cpp-client/module_cpp.h index b47698546..5ce0087bc 100644 --- a/crates/symmetric_executor/cpp-client/module_cpp.h +++ b/crates/symmetric_executor/cpp-client/module_cpp.h @@ -7,7 +7,18 @@ #include #include #include -namespace symmetric {namespace runtime {namespace symmetric_executor {class CallbackFunction : public wit::ResourceImportBase{ +namespace symmetric {namespace runtime {namespace symmetric_executor {/// These pseudo-resources are just used to +/// pass pointers to register +/// 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, +}; + +class CallbackFunction : public wit::ResourceImportBase{ public: @@ -54,6 +65,17 @@ class EventGenerator : public wit::ResourceImportBase{ EventGenerator& operator=(EventGenerator&&) = default; }; +class CallbackRegistration : public wit::ResourceImportBase{ + + public: + + ~CallbackRegistration(); + static CallbackData Cancel(CallbackRegistration&& obj); + CallbackRegistration(wit::ResourceImportBase &&); + CallbackRegistration(CallbackRegistration&&) = default; + CallbackRegistration& operator=(CallbackRegistration&&) = 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, @@ -63,18 +85,8 @@ enum class CallStatus : uint8_t { 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); -void BlockOn(EventSubscription&& event); +CallbackRegistration Register(EventSubscription&& trigger, CallbackFunction&& callback, CallbackData&& data); } namespace symmetric_stream {using EventSubscription = symmetric_executor::EventSubscription; class Address : public wit::ResourceImportBase{ diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 28a4ef6ea..56f302c75 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -2,12 +2,13 @@ use futures::{task::Waker, FutureExt}; use std::{ future::Future, pin::Pin, - ptr::null_mut, task::{Context, Poll, RawWaker, RawWakerVTable}, }; use crate::{ - module::symmetric::runtime::symmetric_executor::{self, EventGenerator, EventSubscription}, + module::symmetric::runtime::symmetric_executor::{ + CallbackState, EventGenerator, EventSubscription, + }, EventSubscription2, }; @@ -80,26 +81,17 @@ pub async fn wait_on(wait_for: EventSubscription) { .await } -extern "C" fn symmetric_callback( - obj: *mut (), - event: *mut EventSubscription2, -) -> *mut EventSubscription2 { - drop(unsafe { EventSubscription::from_handle(event as usize) }); +extern "C" fn symmetric_callback(obj: *mut ()) -> CallbackState { match unsafe { poll(obj.cast()) } { - Poll::Ready(_) => null_mut(), + Poll::Ready(_) => CallbackState::Ready, Poll::Pending => { let state = obj.cast::(); - unsafe { &mut *state } - .waiting_for - .take() - .map_or(null_mut(), |evt| evt.take_handle() as *mut ()) - .cast() - // if let Some(waiting_for) = { - // super::register(waiting_for, symmetric_callback, obj); - // } - // // as we registered this callback on a new event stop calling - // // from the old event - // CallbackState::Ready + if let Some(waiting_for) = unsafe { &mut *state }.waiting_for.take() { + super::register(waiting_for, symmetric_callback, obj); + } + // as we registered this callback on a new event stop calling + // from the old event + CallbackState::Ready } } } diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index 21acb9a53..c5b462d54 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -1,5 +1,5 @@ use module::symmetric::runtime::symmetric_executor::{self, CallbackData, CallbackFunction}; -pub use module::symmetric::runtime::symmetric_executor::{run, EventGenerator, EventSubscription}; +pub use module::symmetric::runtime::symmetric_executor::{run, CallbackState, EventGenerator, EventSubscription}; pub use module::symmetric::runtime::symmetric_stream; pub mod async_support; @@ -10,7 +10,7 @@ pub struct EventGenerator2; pub fn register( event: EventSubscription, - f: extern "C" fn(*mut T, *mut EventSubscription2) -> *mut EventSubscription2, + f: extern "C" fn(*mut T) -> CallbackState, data: *mut T, ) { let callback = unsafe { CallbackFunction::from_handle(f as *const () as usize) }; @@ -18,15 +18,15 @@ pub fn register( 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!() -} +// #[no_mangle] +// fn cabi_realloc_wit_bindgen_0_41_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 EventGenerator2) -> EventSubscription { let gen: EventGenerator = unsafe { EventGenerator::from_handle(event_send as usize) }; diff --git a/crates/symmetric_executor/rust-client/src/module.rs b/crates/symmetric_executor/rust-client/src/module.rs index e019fcc7e..1ed0a9ff2 100644 --- a/crates/symmetric_executor/rust-client/src/module.rs +++ b/crates/symmetric_executor/rust-client/src/module.rs @@ -16,12 +16,43 @@ pub mod symmetric { use super::super::super::_rt; /// These pseudo-resources are just used to /// pass pointers to register + /// 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 unsafe { ::core::mem::transmute(val) }; + } + + match val { + 0 => CallbackState::Pending, + 1 => CallbackState::Ready, + + _ => panic!("invalid enum discriminant"), + } + } + } + /// This wraps a user provided function of type - /// `fn (callback-data, event-subscription) -> null_or` - /// - /// The executor passes the subscription as the second argument, - /// the same callback is re-registered for the returned subscription - /// (returning the second arguments keeps the registration active) + /// `fn (callback-data) -> callback-state` #[derive(Debug)] #[repr(transparent)] @@ -52,26 +83,26 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-function" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_function( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_function( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_function(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_function(_handle) }; } } } /// This wraps opaque user data, freed by the callback when - /// it returns null (ready) + /// it returns ready #[derive(Debug)] #[repr(transparent)] @@ -102,19 +133,19 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-data" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_data( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_data( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_data(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_data(_handle) }; } } @@ -151,19 +182,19 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]event-subscription" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_subscription( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_subscription( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_subscription(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_subscription(_handle) }; } } @@ -200,19 +231,19 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]event-generator" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_generator( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_generator( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_generator(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_generator(_handle) }; } } @@ -249,19 +280,19 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-registration" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_registration( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_registration( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_registration(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_registration(_handle) }; } } @@ -308,17 +339,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn ready(&self) -> bool { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-subscription.ready" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Eready( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Eready( _: *mut u8, ) -> i32; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Eready((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Eready((self).handle() as *mut u8); _rt::bool_lift(ret as u8) } } @@ -329,17 +360,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn from_timeout(nanoseconds: u64) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[static]event-subscription.from-timeout" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BstaticX5Devent_subscriptionX2Efrom_timeout( _: i64, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(_rt::as_i64(&nanoseconds)); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(_rt::as_i64(&nanoseconds)); EventSubscription::from_handle(ret as usize) } } @@ -350,17 +381,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn dup(&self) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-subscription.dup" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Edup( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Edup( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Edup((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Edup((self).handle() as *mut u8); EventSubscription::from_handle(ret as usize) } } @@ -371,17 +402,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn reset(&self) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-subscription.reset" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Ereset( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Ereset( _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Ereset((self).handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Ereset((self).handle() as *mut u8); } } } @@ -390,16 +421,16 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn new() -> Self { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[constructor]event-generator" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BconstructorX5Devent_generator( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BconstructorX5Devent_generator( ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BconstructorX5Devent_generator(); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BconstructorX5Devent_generator(); EventGenerator::from_handle(ret as usize) } } @@ -410,17 +441,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn subscribe(&self) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-generator.subscribe" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Esubscribe( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_generatorX2Esubscribe( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Esubscribe((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_generatorX2Esubscribe((self).handle() as *mut u8); EventSubscription::from_handle(ret as usize) } } @@ -431,17 +462,18 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn activate(&self) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] + #[cfg_attr(not(target_arch = "wasm32"), link(name = "symmetric_executor"))] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-generator.activate" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Eactivate( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_generatorX2Eactivate( _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Eactivate((self).handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_generatorX2Eactivate((self).handle() as *mut u8); } } } @@ -451,17 +483,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn cancel(obj: CallbackRegistration) -> CallbackData { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[static]callback-registration.cancel" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Dcallback_registrationX2Ecancel( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BstaticX5Dcallback_registrationX2Ecancel( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Dcallback_registrationX2Ecancel((&obj).take_handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BstaticX5Dcallback_registrationX2Ecancel((&obj).take_handle() as *mut u8); CallbackData::from_handle(ret as usize) } } @@ -471,12 +503,12 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn run() -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "run")] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00run(); + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00run(); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00run(); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00run(); } } #[allow(unused_unsafe, clippy::all)] @@ -488,16 +520,16 @@ pub mod symmetric { data: CallbackData, ) -> CallbackRegistration { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "register")] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00register( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00register( _: *mut u8, _: *mut u8, _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00register( + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00register( (&trigger).take_handle() as *mut u8, (&callback).take_handle() as *mut u8, (&data).take_handle() as *mut u8, @@ -515,8 +547,6 @@ pub mod symmetric { static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; - use std::ptr::null_mut; - use super::super::super::_rt; pub type EventSubscription = super::super::super::symmetric::runtime::symmetric_executor::EventSubscription; @@ -550,19 +580,19 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]address" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Daddress( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Daddress( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Daddress(_handle) + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Daddress(_handle) }; } } @@ -599,16 +629,16 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "[resource-drop]buffer")] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dbuffer( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Dbuffer( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dbuffer(_handle) + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Dbuffer(_handle) }; } } @@ -643,20 +673,20 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] #[cfg_attr(not(target_arch = "wasm32"), link(name = "symmetric_stream"))] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]stream-obj" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dstream_obj( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Dstream_obj( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dstream_obj(_handle) + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Dstream_obj(_handle) }; } } @@ -667,15 +697,15 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn new(addr: Address, capacity: u64) -> Self { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "[constructor]buffer")] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BconstructorX5Dbuffer( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BconstructorX5Dbuffer( _: *mut u8, _: i64, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BconstructorX5Dbuffer((&addr).take_handle() as *mut u8, _rt::as_i64(&capacity)); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BconstructorX5Dbuffer((&addr).take_handle() as *mut u8, _rt::as_i64(&capacity)); Buffer::from_handle(ret as usize) } } @@ -685,17 +715,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn get_address(&self) -> Address { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]buffer.get-address" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eget_address( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eget_address( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eget_address((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eget_address((self).handle() as *mut u8); Address::from_handle(ret as usize) } } @@ -705,17 +735,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn get_size(&self) -> u64 { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]buffer.get-size" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eget_size( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eget_size( _: *mut u8, ) -> i64; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eget_size((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eget_size((self).handle() as *mut u8); ret as u64 } } @@ -725,18 +755,18 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn set_size(&self, size: u64) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]buffer.set-size" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eset_size( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eset_size( _: *mut u8, _: i64, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eset_size((self).handle() as *mut u8, _rt::as_i64(&size)); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eset_size((self).handle() as *mut u8, _rt::as_i64(&size)); } } } @@ -745,17 +775,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn capacity(&self) -> u64 { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]buffer.capacity" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Ecapacity( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Ecapacity( _: *mut u8, ) -> i64; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Ecapacity((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Ecapacity((self).handle() as *mut u8); ret as u64 } } @@ -765,16 +795,16 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn new() -> Self { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[constructor]stream-obj" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BconstructorX5Dstream_obj( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BconstructorX5Dstream_obj( ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BconstructorX5Dstream_obj(); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BconstructorX5Dstream_obj(); StreamObj::from_handle(ret as usize) } } @@ -785,17 +815,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn clone(&self) -> StreamObj { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.clone" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eclone( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eclone( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eclone((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eclone((self).handle() as *mut u8); StreamObj::from_handle(ret as usize) } } @@ -806,17 +836,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn is_write_closed(&self) -> bool { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.is-write-closed" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eis_write_closed( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eis_write_closed( _: *mut u8, ) -> i32; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eis_write_closed((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eis_write_closed((self).handle() as *mut u8); _rt::bool_lift(ret as u8) } } @@ -826,18 +856,18 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn start_reading(&self, buffer: Buffer) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.start-reading" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Estart_reading( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Estart_reading( _: *mut u8, _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Estart_reading((self).handle() as *mut u8, (&buffer).take_handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Estart_reading((self).handle() as *mut u8, (&buffer).take_handle() as *mut u8); } } } @@ -846,17 +876,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn write_ready_activate(&self) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.write-ready-activate" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_activate( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Ewrite_ready_activate( _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_activate((self).handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Ewrite_ready_activate((self).handle() as *mut u8); } } } @@ -865,17 +895,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn read_ready_subscribe(&self) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.read-ready-subscribe" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_ready_subscribe( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_ready_subscribe( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_ready_subscribe((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_ready_subscribe((self).handle() as *mut u8); super::super::super::symmetric::runtime::symmetric_executor::EventSubscription::from_handle(ret as usize) } } @@ -896,18 +926,18 @@ pub mod symmetric { 2 * ::core::mem::size_of::<*const u8>()], ); let ptr0 = ret_area.0.as_mut_ptr().cast::(); - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.read-result" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_result( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_result( _: *mut u8, _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_result((self).handle() as *mut u8, ptr0); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_result((self).handle() as *mut u8, ptr0); let l1 = i32::from(*ptr0.add(0).cast::()); match l1 { 0 => None, @@ -932,17 +962,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn is_ready_to_write(&self) -> bool { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.is-ready-to-write" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eis_ready_to_write( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eis_ready_to_write( _: *mut u8, ) -> i32; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eis_ready_to_write((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eis_ready_to_write((self).handle() as *mut u8); _rt::bool_lift(ret as u8) } } @@ -952,17 +982,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn write_ready_subscribe(&self) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.write-ready-subscribe" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe((self).handle() as *mut u8); super::super::super::symmetric::runtime::symmetric_executor::EventSubscription::from_handle(ret as usize) } } @@ -972,17 +1002,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn start_writing(&self) -> Buffer { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.start-writing" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Estart_writing( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Estart_writing( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Estart_writing((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Estart_writing((self).handle() as *mut u8); Buffer::from_handle(ret as usize) } } @@ -995,21 +1025,21 @@ pub mod symmetric { unsafe { let (result0_0, result0_1) = match &buffer { Some(e) => (1i32, (e).take_handle() as *mut u8), - None => (0i32, null_mut()), + None => (0i32, core::ptr::null_mut()), }; - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.finish-writing" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Efinish_writing( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Efinish_writing( _: *mut u8, _: i32, _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Efinish_writing((self).handle() as *mut u8, result0_0, result0_1); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Efinish_writing((self).handle() as *mut u8, result0_0, result0_1); } } } @@ -1018,17 +1048,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn read_ready_activate(&self) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]stream-obj.read-ready-activate" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_ready_activate( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_ready_activate( _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_ready_activate((self).handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_ready_activate((self).handle() as *mut u8); } } } @@ -1181,46 +1211,46 @@ mod _rt { } #[cfg(target_arch = "wasm32")] -#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:symmetric:runtime@0.2.0:module:encoded world")] +#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:symmetric:runtime@0.2.1:module:encoded world")] #[doc(hidden)] #[allow(clippy::octal_escapes)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1759] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xe2\x0c\x01A\x02\x01\ -A\x05\x01B\"\x04\0\x11callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\x04\ -\0\x12event-subscription\x03\x01\x04\0\x0fevent-generator\x03\x01\x04\0\x15callb\ -ack-registration\x03\x01\x01m\x02\x07started\x0bnot-started\x04\0\x0bcall-status\ -\x03\0\x05\x01h\x02\x01@\x01\x04self\x07\0\x7f\x04\0\x20[method]event-subscripti\ -on.ready\x01\x08\x01i\x02\x01@\x01\x0bnanosecondsw\0\x09\x04\0'[static]event-sub\ -scription.from-timeout\x01\x0a\x01@\x01\x04self\x07\0\x09\x04\0\x1e[method]event\ --subscription.dup\x01\x0b\x01@\x01\x04self\x07\x01\0\x04\0\x20[method]event-subs\ -cription.reset\x01\x0c\x01i\x03\x01@\0\0\x0d\x04\0\x1c[constructor]event-generat\ -or\x01\x0e\x01h\x03\x01@\x01\x04self\x0f\0\x09\x04\0![method]event-generator.sub\ -scribe\x01\x10\x01@\x01\x04self\x0f\x01\0\x04\0\x20[method]event-generator.activ\ -ate\x01\x11\x01i\x04\x01i\x01\x01@\x01\x03obj\x12\0\x13\x04\0$[static]callback-r\ -egistration.cancel\x01\x14\x01@\0\x01\0\x04\0\x03run\x01\x15\x01i\0\x01@\x03\x07\ -trigger\x09\x08callback\x16\x04data\x13\0\x12\x04\0\x08register\x01\x17\x03\0*sy\ -mmetric:runtime/symmetric-executor@0.2.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\x04\ -self\x08\0w\x04\0\x17[method]buffer.get-size\x01\x0a\x01@\x02\x04self\x08\x04siz\ -ew\x01\0\x04\0\x17[method]buffer.set-size\x01\x0b\x04\0\x17[method]buffer.capaci\ -ty\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\x04\ -self\x0e\0\x7f\x04\0\"[method]stream-obj.is-write-closed\x01\x10\x01@\x02\x04sel\ -f\x0e\x06buffer\x06\x01\0\x04\0\x20[method]stream-obj.start-reading\x01\x11\x01@\ -\x01\x04self\x0e\x01\0\x04\0'[method]stream-obj.write-ready-activate\x01\x12\x01\ -i\x01\x01@\x01\x04self\x0e\0\x13\x04\0'[method]stream-obj.read-ready-subscribe\x01\ -\x14\x01k\x06\x01@\x01\x04self\x0e\0\x15\x04\0\x1e[method]stream-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]s\ -tream-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-act\ -ivate\x01\x12\x03\0(symmetric:runtime/symmetric-stream@0.2.0\x05\x02\x04\0\x1esy\ -mmetric:runtime/module@0.2.0\x04\0\x0b\x0c\x01\0\x06module\x03\0\0\0G\x09produce\ -rs\x01\x0cprocessed-by\x02\x0dwit-component\x070.228.0\x10wit-bindgen-rust\x060.\ -41.0"; +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1796] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x87\x0d\x01A\x02\x01\ +A\x05\x01B$\x01m\x02\x07pending\x05ready\x04\0\x0ecallback-state\x03\0\0\x04\0\x11\ +callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\x04\0\x12event-subscrip\ +tion\x03\x01\x04\0\x0fevent-generator\x03\x01\x04\0\x15callback-registration\x03\ +\x01\x01m\x02\x07started\x0bnot-started\x04\0\x0bcall-status\x03\0\x07\x01h\x04\x01\ +@\x01\x04self\x09\0\x7f\x04\0\x20[method]event-subscription.ready\x01\x0a\x01i\x04\ +\x01@\x01\x0bnanosecondsw\0\x0b\x04\0'[static]event-subscription.from-timeout\x01\ +\x0c\x01@\x01\x04self\x09\0\x0b\x04\0\x1e[method]event-subscription.dup\x01\x0d\x01\ +@\x01\x04self\x09\x01\0\x04\0\x20[method]event-subscription.reset\x01\x0e\x01i\x05\ +\x01@\0\0\x0f\x04\0\x1c[constructor]event-generator\x01\x10\x01h\x05\x01@\x01\x04\ +self\x11\0\x0b\x04\0![method]event-generator.subscribe\x01\x12\x01@\x01\x04self\x11\ +\x01\0\x04\0\x20[method]event-generator.activate\x01\x13\x01i\x06\x01i\x03\x01@\x01\ +\x03obj\x14\0\x15\x04\0$[static]callback-registration.cancel\x01\x16\x01@\0\x01\0\ +\x04\0\x03run\x01\x17\x01i\x02\x01@\x03\x07trigger\x0b\x08callback\x18\x04data\x15\ +\0\x14\x04\0\x08register\x01\x19\x03\0*symmetric:runtime/symmetric-executor@0.2.\ +1\x05\0\x02\x03\0\0\x12event-subscription\x01B*\x02\x03\x02\x01\x01\x04\0\x12eve\ +nt-subscription\x03\0\0\x04\0\x07address\x03\x01\x04\0\x06buffer\x03\x01\x04\0\x0a\ +stream-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]buffe\ +r.get-size\x01\x0a\x01@\x02\x04self\x08\x04sizew\x01\0\x04\0\x17[method]buffer.s\ +et-size\x01\x0b\x04\0\x17[method]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]strea\ +m-obj.is-write-closed\x01\x10\x01@\x02\x04self\x0e\x06buffer\x06\x01\0\x04\0\x20\ +[method]stream-obj.start-reading\x01\x11\x01@\x01\x04self\x0e\x01\0\x04\0'[metho\ +d]stream-obj.write-ready-activate\x01\x12\x01i\x01\x01@\x01\x04self\x0e\0\x13\x04\ +\0'[method]stream-obj.read-ready-subscribe\x01\x14\x01k\x06\x01@\x01\x04self\x0e\ +\0\x15\x04\0\x1e[method]stream-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:runtim\ +e/symmetric-stream@0.2.1\x05\x02\x04\0\x1esymmetric:runtime/module@0.2.1\x04\0\x0b\ +\x0c\x01\0\x06module\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-comp\ +onent\x070.228.0\x10wit-bindgen-rust\x060.41.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/symmetric_executor/src/executor.rs b/crates/symmetric_executor/src/executor.rs index 496c836f2..85314f2ff 100644 --- a/crates/symmetric_executor/src/executor.rs +++ b/crates/symmetric_executor/src/executor.rs @@ -17,12 +17,45 @@ pub mod exports { use super::super::super::super::_rt; /// These pseudo-resources are just used to /// pass pointers to register + /// 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 unsafe { ::core::mem::transmute(val) }; + } + + match val { + 0 => CallbackState::Pending, + 1 => CallbackState::Ready, + + _ => panic!("invalid enum discriminant"), + } + } + } + /// This wraps a user provided function of type - /// `fn (callback-data, event-subscription) -> null_or` - /// - /// The executor passes the subscription as the second argument, - /// the same callback is re-registered for the returned subscription - /// (returning the second arguments keeps the registration active) + /// `fn (callback-data) -> callback-state` #[derive(Debug)] #[repr(transparent)] @@ -152,27 +185,27 @@ pub mod exports { unsafe fn drop(_handle: usize) { { #[link( - wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0" + wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1" )] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-function" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_function( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_function( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_function(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_function(_handle) }; } } } /// This wraps opaque user data, freed by the callback when - /// it returns null (ready) + /// it returns ready #[derive(Debug)] #[repr(transparent)] @@ -300,20 +333,20 @@ pub mod exports { unsafe fn drop(_handle: usize) { { #[link( - wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0" + wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1" )] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-data" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_data( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_data( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_data(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_data(_handle) }; } } @@ -449,20 +482,20 @@ pub mod exports { unsafe fn drop(_handle: usize) { { #[link( - wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0" + wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1" )] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]event-subscription" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_subscription( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_subscription( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_subscription(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_subscription(_handle) }; } } @@ -597,20 +630,20 @@ pub mod exports { unsafe fn drop(_handle: usize) { { #[link( - wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0" + wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1" )] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]event-generator" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_generator( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_generator( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_generator(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_generator(_handle) }; } } @@ -749,20 +782,20 @@ pub mod exports { unsafe fn drop(_handle: usize) { { #[link( - wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0" + wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1" )] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-registration" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_registration( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_registration( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_registration(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_registration(_handle) }; } } @@ -1129,88 +1162,88 @@ pub mod exports { } #[doc(hidden)] - macro_rules! __export_symmetric_runtime_symmetric_executor_0_2_0_cabi{ + macro_rules! __export_symmetric_runtime_symmetric_executor_0_2_1_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_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Eready(arg0: *mut u8,) -> i32 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Eready(arg0: *mut u8,) -> i32 { unsafe { $($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_executorX400X2E2X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(arg0: i64,) -> *mut u8 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(arg0: i64,) -> *mut u8 { unsafe { $($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_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Edup(arg0: *mut u8,) -> *mut u8 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Edup(arg0: *mut u8,) -> *mut u8 { unsafe { $($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_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Ereset(arg0: *mut u8,) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Ereset(arg0: *mut u8,) { unsafe { $($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_executorX400X2E2X2E0X00X5BconstructorX5Devent_generator() -> *mut u8 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BconstructorX5Devent_generator() -> *mut u8 { unsafe { $($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_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Esubscribe(arg0: *mut u8,) -> *mut u8 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_generatorX2Esubscribe(arg0: *mut u8,) -> *mut u8 { unsafe { $($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_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Eactivate(arg0: *mut u8,) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_generatorX2Eactivate(arg0: *mut u8,) { unsafe { $($path_to_types)*::_export_method_event_generator_activate_cabi::<<$ty as $($path_to_types)*::Guest>::EventGenerator>(arg0) } } #[cfg_attr(target_arch = "wasm32", export_name = "[static]callback-registration.cancel")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Dcallback_registrationX2Ecancel(arg0: *mut u8,) -> *mut u8 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BstaticX5Dcallback_registrationX2Ecancel(arg0: *mut u8,) -> *mut u8 { unsafe { $($path_to_types)*::_export_static_callback_registration_cancel_cabi::<<$ty as $($path_to_types)*::Guest>::CallbackRegistration>(arg0) } } #[cfg_attr(target_arch = "wasm32", export_name = "run")] #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00run() { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00run() { unsafe { $($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_executorX400X2E2X2E0X00register(arg0: *mut u8,arg1: *mut u8,arg2: *mut u8,) -> *mut u8 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00register(arg0: *mut u8,arg1: *mut u8,arg2: *mut u8,) -> *mut u8 { unsafe { $($path_to_types)*::_export_register_cabi::<$ty>(arg0, arg1, arg2) } } #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_function(arg0: usize) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_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_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_data(arg0: usize) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_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_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_subscription(arg0: usize) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_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_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_generator(arg0: usize) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_generator(arg0: usize) { $($path_to_types)*::_export_drop_eventGenerator_cabi::<<$ty as $($path_to_types)*::Guest>::EventGenerator>(arg0) } #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] - unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_registration(arg0: usize) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_registration(arg0: usize) { $($path_to_types)*::_export_drop_callbackRegistration_cabi::<<$ty as $($path_to_types)*::Guest>::CallbackRegistration>(arg0) } };); } #[doc(hidden)] - pub(crate) use __export_symmetric_runtime_symmetric_executor_0_2_0_cabi; + pub(crate) use __export_symmetric_runtime_symmetric_executor_0_2_1_cabi; } } } @@ -1343,34 +1376,35 @@ mod _rt { 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_2_0_cabi!($ty with_types_in $($path_to_types_root)*::exports::symmetric::runtime::symmetric_executor); + $($path_to_types_root)*::exports::symmetric::runtime::symmetric_executor::__export_symmetric_runtime_symmetric_executor_0_2_1_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")] -#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:symmetric:runtime@0.2.0:executor:encoded world")] +#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:symmetric:runtime@0.2.1:executor:encoded world")] #[doc(hidden)] #[allow(clippy::octal_escapes)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 836] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xc5\x05\x01A\x02\x01\ -A\x02\x01B\"\x04\0\x11callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\x04\ -\0\x12event-subscription\x03\x01\x04\0\x0fevent-generator\x03\x01\x04\0\x15callb\ -ack-registration\x03\x01\x01m\x02\x07started\x0bnot-started\x04\0\x0bcall-status\ -\x03\0\x05\x01h\x02\x01@\x01\x04self\x07\0\x7f\x04\0\x20[method]event-subscripti\ -on.ready\x01\x08\x01i\x02\x01@\x01\x0bnanosecondsw\0\x09\x04\0'[static]event-sub\ -scription.from-timeout\x01\x0a\x01@\x01\x04self\x07\0\x09\x04\0\x1e[method]event\ --subscription.dup\x01\x0b\x01@\x01\x04self\x07\x01\0\x04\0\x20[method]event-subs\ -cription.reset\x01\x0c\x01i\x03\x01@\0\0\x0d\x04\0\x1c[constructor]event-generat\ -or\x01\x0e\x01h\x03\x01@\x01\x04self\x0f\0\x09\x04\0![method]event-generator.sub\ -scribe\x01\x10\x01@\x01\x04self\x0f\x01\0\x04\0\x20[method]event-generator.activ\ -ate\x01\x11\x01i\x04\x01i\x01\x01@\x01\x03obj\x12\0\x13\x04\0$[static]callback-r\ -egistration.cancel\x01\x14\x01@\0\x01\0\x04\0\x03run\x01\x15\x01i\0\x01@\x03\x07\ -trigger\x09\x08callback\x16\x04data\x13\0\x12\x04\0\x08register\x01\x17\x04\0*sy\ -mmetric:runtime/symmetric-executor@0.2.0\x05\0\x04\0\x20symmetric:runtime/execut\ -or@0.2.0\x04\0\x0b\x0e\x01\0\x08executor\x03\0\0\0G\x09producers\x01\x0cprocesse\ -d-by\x02\x0dwit-component\x070.228.0\x10wit-bindgen-rust\x060.41.0"; +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 873] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xea\x05\x01A\x02\x01\ +A\x02\x01B$\x01m\x02\x07pending\x05ready\x04\0\x0ecallback-state\x03\0\0\x04\0\x11\ +callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\x04\0\x12event-subscrip\ +tion\x03\x01\x04\0\x0fevent-generator\x03\x01\x04\0\x15callback-registration\x03\ +\x01\x01m\x02\x07started\x0bnot-started\x04\0\x0bcall-status\x03\0\x07\x01h\x04\x01\ +@\x01\x04self\x09\0\x7f\x04\0\x20[method]event-subscription.ready\x01\x0a\x01i\x04\ +\x01@\x01\x0bnanosecondsw\0\x0b\x04\0'[static]event-subscription.from-timeout\x01\ +\x0c\x01@\x01\x04self\x09\0\x0b\x04\0\x1e[method]event-subscription.dup\x01\x0d\x01\ +@\x01\x04self\x09\x01\0\x04\0\x20[method]event-subscription.reset\x01\x0e\x01i\x05\ +\x01@\0\0\x0f\x04\0\x1c[constructor]event-generator\x01\x10\x01h\x05\x01@\x01\x04\ +self\x11\0\x0b\x04\0![method]event-generator.subscribe\x01\x12\x01@\x01\x04self\x11\ +\x01\0\x04\0\x20[method]event-generator.activate\x01\x13\x01i\x06\x01i\x03\x01@\x01\ +\x03obj\x14\0\x15\x04\0$[static]callback-registration.cancel\x01\x16\x01@\0\x01\0\ +\x04\0\x03run\x01\x17\x01i\x02\x01@\x03\x07trigger\x0b\x08callback\x18\x04data\x15\ +\0\x14\x04\0\x08register\x01\x19\x04\0*symmetric:runtime/symmetric-executor@0.2.\ +1\x05\0\x04\0\x20symmetric:runtime/executor@0.2.1\x04\0\x0b\x0e\x01\0\x08executo\ +r\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0dwit-component\x070.228.0\x10\ +wit-bindgen-rust\x060.41.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs index 40c803f6d..d0cb7cb59 100644 --- a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs +++ b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs @@ -16,12 +16,43 @@ pub mod symmetric { use super::super::super::_rt; /// These pseudo-resources are just used to /// pass pointers to register + /// 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 unsafe { ::core::mem::transmute(val) }; + } + + match val { + 0 => CallbackState::Pending, + 1 => CallbackState::Ready, + + _ => panic!("invalid enum discriminant"), + } + } + } + /// This wraps a user provided function of type - /// `fn (callback-data, event-subscription) -> null_or` - /// - /// The executor passes the subscription as the second argument, - /// the same callback is re-registered for the returned subscription - /// (returning the second arguments keeps the registration active) + /// `fn (callback-data) -> callback-state` #[derive(Debug)] #[repr(transparent)] @@ -52,26 +83,26 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-function" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_function( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_function( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_function(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_function(_handle) }; } } } /// This wraps opaque user data, freed by the callback when - /// it returns null (ready) + /// it returns ready #[derive(Debug)] #[repr(transparent)] @@ -102,19 +133,19 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-data" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_data( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_data( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_data(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_data(_handle) }; } } @@ -151,19 +182,19 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]event-subscription" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_subscription( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_subscription( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_subscription(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_subscription(_handle) }; } } @@ -200,19 +231,19 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]event-generator" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_generator( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_generator( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Devent_generator(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Devent_generator(_handle) }; } } @@ -249,19 +280,19 @@ pub mod symmetric { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]callback-registration" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_registration( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_registration( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5Bresource_dropX5Dcallback_registration(_handle) + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5Bresource_dropX5Dcallback_registration(_handle) }; } } @@ -308,17 +339,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn ready(&self) -> bool { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-subscription.ready" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Eready( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Eready( _: *mut u8, ) -> i32; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Eready((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Eready((self).handle() as *mut u8); _rt::bool_lift(ret as u8) } } @@ -329,17 +360,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn from_timeout(nanoseconds: u64) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[static]event-subscription.from-timeout" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BstaticX5Devent_subscriptionX2Efrom_timeout( _: i64, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(_rt::as_i64(&nanoseconds)); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BstaticX5Devent_subscriptionX2Efrom_timeout(_rt::as_i64(&nanoseconds)); EventSubscription::from_handle(ret as usize) } } @@ -350,17 +381,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn dup(&self) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-subscription.dup" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Edup( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Edup( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Edup((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Edup((self).handle() as *mut u8); EventSubscription::from_handle(ret as usize) } } @@ -371,17 +402,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn reset(&self) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-subscription.reset" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Ereset( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Ereset( _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_subscriptionX2Ereset((self).handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_subscriptionX2Ereset((self).handle() as *mut u8); } } } @@ -390,16 +421,16 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn new() -> Self { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[constructor]event-generator" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BconstructorX5Devent_generator( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BconstructorX5Devent_generator( ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BconstructorX5Devent_generator(); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BconstructorX5Devent_generator(); EventGenerator::from_handle(ret as usize) } } @@ -410,17 +441,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn subscribe(&self) -> EventSubscription { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-generator.subscribe" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Esubscribe( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_generatorX2Esubscribe( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Esubscribe((self).handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_generatorX2Esubscribe((self).handle() as *mut u8); EventSubscription::from_handle(ret as usize) } } @@ -431,17 +462,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn activate(&self) -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[method]event-generator.activate" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Eactivate( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_generatorX2Eactivate( _: *mut u8, ); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BmethodX5Devent_generatorX2Eactivate((self).handle() as *mut u8); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BmethodX5Devent_generatorX2Eactivate((self).handle() as *mut u8); } } } @@ -451,17 +482,17 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn cancel(obj: CallbackRegistration) -> CallbackData { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[static]callback-registration.cancel" )] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Dcallback_registrationX2Ecancel( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BstaticX5Dcallback_registrationX2Ecancel( _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00X5BstaticX5Dcallback_registrationX2Ecancel((&obj).take_handle() as *mut u8); + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00X5BstaticX5Dcallback_registrationX2Ecancel((&obj).take_handle() as *mut u8); CallbackData::from_handle(ret as usize) } } @@ -471,12 +502,12 @@ pub mod symmetric { #[allow(async_fn_in_trait)] pub fn run() -> () { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "run")] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00run(); + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00run(); } - symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00run(); + symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00run(); } } #[allow(unused_unsafe, clippy::all)] @@ -488,16 +519,16 @@ pub mod symmetric { data: CallbackData, ) -> CallbackRegistration { unsafe { - #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-executor@0.2.1")] unsafe extern "C" { #[cfg_attr(target_arch = "wasm32", link_name = "register")] - fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00register( + fn symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00register( _: *mut u8, _: *mut u8, _: *mut u8, ) -> *mut u8; } - let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E0X00register( + let ret = symmetricX3AruntimeX2Fsymmetric_executorX400X2E2X2E1X00register( (&trigger).take_handle() as *mut u8, (&callback).take_handle() as *mut u8, (&data).take_handle() as *mut u8, @@ -648,19 +679,19 @@ pub mod exports { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]address" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Daddress( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Daddress( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Daddress(_handle) + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Daddress(_handle) }; } } @@ -793,19 +824,19 @@ pub mod exports { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]buffer" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dbuffer( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Dbuffer( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dbuffer(_handle) + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Dbuffer(_handle) }; } } @@ -936,19 +967,19 @@ pub mod exports { #[inline] unsafe fn drop(_handle: usize) { { - #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.0")] + #[link(wasm_import_module = "symmetric:runtime/symmetric-stream@0.2.1")] unsafe extern "C" { #[cfg_attr( target_arch = "wasm32", link_name = "[resource-drop]stream-obj" )] - fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dstream_obj( + fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Dstream_obj( _: usize, ); } unsafe { - symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E0X00X5Bresource_dropX5Dstream_obj(_handle) + symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_dropX5Dstream_obj(_handle) }; } } @@ -1335,113 +1366,113 @@ pub mod exports { } #[doc(hidden)] - macro_rules! __export_symmetric_runtime_symmetric_stream_0_2_0_cabi{ + macro_rules! __export_symmetric_runtime_symmetric_stream_0_2_1_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_streamX400X2E2X2E0X00X5BconstructorX5Dbuffer(arg0: *mut u8,arg1: i64,) -> *mut u8 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BconstructorX5Dbuffer(arg0: *mut u8,arg1: i64,) -> *mut u8 { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eget_address(arg0: *mut u8,) -> *mut u8 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eget_address(arg0: *mut u8,) -> *mut u8 { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eget_size(arg0: *mut u8,) -> i64 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eget_size(arg0: *mut u8,) -> i64 { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Eset_size(arg0: *mut u8,arg1: i64,) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Eset_size(arg0: *mut u8,arg1: i64,) { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5DbufferX2Ecapacity(arg0: *mut u8,) -> i64 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5DbufferX2Ecapacity(arg0: *mut u8,) -> i64 { unsafe { $($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_streamX400X2E2X2E0X00X5BconstructorX5Dstream_obj() -> *mut u8 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BconstructorX5Dstream_obj() -> *mut u8 { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eclone(arg0: *mut u8,) -> *mut u8 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eclone(arg0: *mut u8,) -> *mut u8 { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eis_write_closed(arg0: *mut u8,) -> i32 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eis_write_closed(arg0: *mut u8,) -> i32 { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Estart_reading(arg0: *mut u8,arg1: *mut u8,) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Estart_reading(arg0: *mut u8,arg1: *mut u8,) { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_activate(arg0: *mut u8,) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Ewrite_ready_activate(arg0: *mut u8,) { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_ready_subscribe(arg0: *mut u8,) -> *mut u8 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_ready_subscribe(arg0: *mut u8,) -> *mut u8 { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_result(arg0: *mut u8,arg1: *mut u8,) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_result(arg0: *mut u8,arg1: *mut u8,) { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eis_ready_to_write(arg0: *mut u8,) -> i32 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eis_ready_to_write(arg0: *mut u8,) -> i32 { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe(arg0: *mut u8,) -> *mut u8 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Ewrite_ready_subscribe(arg0: *mut u8,) -> *mut u8 { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Estart_writing(arg0: *mut u8,) -> *mut u8 { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Estart_writing(arg0: *mut u8,) -> *mut u8 { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Efinish_writing(arg0: *mut u8,arg1: i32,arg2: i32,) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Efinish_writing(arg0: *mut u8,arg1: i32,arg2: i32,) { unsafe { $($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_streamX400X2E2X2E0X00X5BmethodX5Dstream_objX2Eread_ready_activate(arg0: *mut u8,) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Eread_ready_activate(arg0: *mut u8,) { unsafe { $($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_streamX400X2E2X2E0X00X5Bresource_dropX5Daddress(arg0: usize) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_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_streamX400X2E2X2E0X00X5Bresource_dropX5Dbuffer(arg0: usize) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_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_streamX400X2E2X2E0X00X5Bresource_dropX5Dstream_obj(arg0: usize) { - $($path_to_types)*::_export_drop_stream-obj_cabi::<<$ty as $($path_to_types)*::Guest>::StreamObj>(arg0) + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5Bresource_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_2_0_cabi; + pub(crate) use __export_symmetric_runtime_symmetric_stream_0_2_1_cabi; } } } @@ -1620,53 +1651,53 @@ mod _rt { 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_2_0_cabi!($ty with_types_in $($path_to_types_root)*::exports::symmetric::runtime::symmetric_stream); + $($path_to_types_root)*::exports::symmetric::runtime::symmetric_stream::__export_symmetric_runtime_symmetric_stream_0_2_1_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.41.0:symmetric:runtime@0.2.0:stream-impl:encoded world")] +#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:symmetric:runtime@0.2.1:stream-impl:encoded world")] #[doc(hidden)] #[allow(clippy::octal_escapes)] -pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1769] = *b"\ -\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\xe7\x0c\x01A\x02\x01\ -A\x05\x01B\"\x04\0\x11callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\x04\ -\0\x12event-subscription\x03\x01\x04\0\x0fevent-generator\x03\x01\x04\0\x15callb\ -ack-registration\x03\x01\x01m\x02\x07started\x0bnot-started\x04\0\x0bcall-status\ -\x03\0\x05\x01h\x02\x01@\x01\x04self\x07\0\x7f\x04\0\x20[method]event-subscripti\ -on.ready\x01\x08\x01i\x02\x01@\x01\x0bnanosecondsw\0\x09\x04\0'[static]event-sub\ -scription.from-timeout\x01\x0a\x01@\x01\x04self\x07\0\x09\x04\0\x1e[method]event\ --subscription.dup\x01\x0b\x01@\x01\x04self\x07\x01\0\x04\0\x20[method]event-subs\ -cription.reset\x01\x0c\x01i\x03\x01@\0\0\x0d\x04\0\x1c[constructor]event-generat\ -or\x01\x0e\x01h\x03\x01@\x01\x04self\x0f\0\x09\x04\0![method]event-generator.sub\ -scribe\x01\x10\x01@\x01\x04self\x0f\x01\0\x04\0\x20[method]event-generator.activ\ -ate\x01\x11\x01i\x04\x01i\x01\x01@\x01\x03obj\x12\0\x13\x04\0$[static]callback-r\ -egistration.cancel\x01\x14\x01@\0\x01\0\x04\0\x03run\x01\x15\x01i\0\x01@\x03\x07\ -trigger\x09\x08callback\x16\x04data\x13\0\x12\x04\0\x08register\x01\x17\x03\0*sy\ -mmetric:runtime/symmetric-executor@0.2.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\x04\ -self\x08\0w\x04\0\x17[method]buffer.get-size\x01\x0a\x01@\x02\x04self\x08\x04siz\ -ew\x01\0\x04\0\x17[method]buffer.set-size\x01\x0b\x04\0\x17[method]buffer.capaci\ -ty\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\x04\ -self\x0e\0\x7f\x04\0\"[method]stream-obj.is-write-closed\x01\x10\x01@\x02\x04sel\ -f\x0e\x06buffer\x06\x01\0\x04\0\x20[method]stream-obj.start-reading\x01\x11\x01@\ -\x01\x04self\x0e\x01\0\x04\0'[method]stream-obj.write-ready-activate\x01\x12\x01\ -i\x01\x01@\x01\x04self\x0e\0\x13\x04\0'[method]stream-obj.read-ready-subscribe\x01\ -\x14\x01k\x06\x01@\x01\x04self\x0e\0\x15\x04\0\x1e[method]stream-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]s\ -tream-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-act\ -ivate\x01\x12\x04\0(symmetric:runtime/symmetric-stream@0.2.0\x05\x02\x04\0#symme\ -tric:runtime/stream-impl@0.2.0\x04\0\x0b\x11\x01\0\x0bstream-impl\x03\0\0\0G\x09\ -producers\x01\x0cprocessed-by\x02\x0dwit-component\x070.228.0\x10wit-bindgen-rus\ -t\x060.41.0"; +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 1806] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x07\x8c\x0d\x01A\x02\x01\ +A\x05\x01B$\x01m\x02\x07pending\x05ready\x04\0\x0ecallback-state\x03\0\0\x04\0\x11\ +callback-function\x03\x01\x04\0\x0dcallback-data\x03\x01\x04\0\x12event-subscrip\ +tion\x03\x01\x04\0\x0fevent-generator\x03\x01\x04\0\x15callback-registration\x03\ +\x01\x01m\x02\x07started\x0bnot-started\x04\0\x0bcall-status\x03\0\x07\x01h\x04\x01\ +@\x01\x04self\x09\0\x7f\x04\0\x20[method]event-subscription.ready\x01\x0a\x01i\x04\ +\x01@\x01\x0bnanosecondsw\0\x0b\x04\0'[static]event-subscription.from-timeout\x01\ +\x0c\x01@\x01\x04self\x09\0\x0b\x04\0\x1e[method]event-subscription.dup\x01\x0d\x01\ +@\x01\x04self\x09\x01\0\x04\0\x20[method]event-subscription.reset\x01\x0e\x01i\x05\ +\x01@\0\0\x0f\x04\0\x1c[constructor]event-generator\x01\x10\x01h\x05\x01@\x01\x04\ +self\x11\0\x0b\x04\0![method]event-generator.subscribe\x01\x12\x01@\x01\x04self\x11\ +\x01\0\x04\0\x20[method]event-generator.activate\x01\x13\x01i\x06\x01i\x03\x01@\x01\ +\x03obj\x14\0\x15\x04\0$[static]callback-registration.cancel\x01\x16\x01@\0\x01\0\ +\x04\0\x03run\x01\x17\x01i\x02\x01@\x03\x07trigger\x0b\x08callback\x18\x04data\x15\ +\0\x14\x04\0\x08register\x01\x19\x03\0*symmetric:runtime/symmetric-executor@0.2.\ +1\x05\0\x02\x03\0\0\x12event-subscription\x01B*\x02\x03\x02\x01\x01\x04\0\x12eve\ +nt-subscription\x03\0\0\x04\0\x07address\x03\x01\x04\0\x06buffer\x03\x01\x04\0\x0a\ +stream-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]buffe\ +r.get-size\x01\x0a\x01@\x02\x04self\x08\x04sizew\x01\0\x04\0\x17[method]buffer.s\ +et-size\x01\x0b\x04\0\x17[method]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]strea\ +m-obj.is-write-closed\x01\x10\x01@\x02\x04self\x0e\x06buffer\x06\x01\0\x04\0\x20\ +[method]stream-obj.start-reading\x01\x11\x01@\x01\x04self\x0e\x01\0\x04\0'[metho\ +d]stream-obj.write-ready-activate\x01\x12\x01i\x01\x01@\x01\x04self\x0e\0\x13\x04\ +\0'[method]stream-obj.read-ready-subscribe\x01\x14\x01k\x06\x01@\x01\x04self\x0e\ +\0\x15\x04\0\x1e[method]stream-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:runtim\ +e/symmetric-stream@0.2.1\x05\x02\x04\0#symmetric:runtime/stream-impl@0.2.1\x04\0\ +\x0b\x11\x01\0\x0bstream-impl\x03\0\0\0G\x09producers\x01\x0cprocessed-by\x02\x0d\ +wit-component\x070.228.0\x10wit-bindgen-rust\x060.41.0"; #[inline(never)] #[doc(hidden)] diff --git a/crates/symmetric_executor/wit/executor.wit b/crates/symmetric_executor/wit/executor.wit index 0b3df24f8..1bb032a95 100644 --- a/crates/symmetric_executor/wit/executor.wit +++ b/crates/symmetric_executor/wit/executor.wit @@ -6,22 +6,20 @@ interface symmetric-executor { // These pseudo-resources are just used to // pass pointers to register + /// 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, + } + /// This wraps a user provided function of type - /// `fn (callback-data, keep-pattern) -> null-or-pattern-or-new-subscription` - /// - /// The executor passes a pattern representing - /// the current subscription as the second argument. - /// - /// Return value: - /// - null: deregister callback, data is assumed to be freed - /// - keep-pattern: re-register callback on the same event - /// - event-subscription: register callback on different event - /// - /// As the code is likely interested in the cancellation object - /// from a new registration, the last return value case is less preferrable. + /// `fn (callback-data) -> callback-state` resource callback-function; /// This wraps opaque user data, freed by the callback when - /// it returns null (ready) + /// it returns ready resource callback-data; /// The receiving side of an event diff --git a/crates/symmetric_executor/wit/world.wit b/crates/symmetric_executor/wit/world.wit index 09e9b4b31..9bee0e5db 100644 --- a/crates/symmetric_executor/wit/world.wit +++ b/crates/symmetric_executor/wit/world.wit @@ -1,4 +1,4 @@ -package symmetric:runtime@0.2.0; +package symmetric:runtime@0.2.1; world executor { export symmetric-executor; From d976667e01fe1409cf8c29b4d47983c9a5e3368a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 2 May 2025 10:36:59 +0200 Subject: [PATCH 546/672] fix limbo state of executor --- .../symmetric_executor/rust-client/src/lib.rs | 4 ++- crates/symmetric_executor/src/lib.rs | 27 +++++-------------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index c5b462d54..3e2258ecf 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -1,5 +1,7 @@ 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_executor::{ + run, CallbackState, EventGenerator, EventSubscription, +}; pub use module::symmetric::runtime::symmetric_stream; pub mod async_support; diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 30001f597..da94d8faa 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -8,7 +8,9 @@ use std::{ time::{Duration, SystemTime}, }; -use executor::exports::symmetric::runtime::symmetric_executor::{self, GuestCallbackRegistration}; +use executor::exports::symmetric::runtime::symmetric_executor::{ + self, CallbackState, GuestCallbackRegistration, +}; const DEBUGGING: bool = cfg!(feature = "trace"); const INVALID_FD: EventFd = -1; @@ -140,9 +142,6 @@ static EXECUTOR: Mutex = Mutex::new(Executor { static EXECUTOR_BUSY: AtomicBool = AtomicBool::new(false); static NEW_TASKS: Mutex> = Mutex::new(Vec::new()); -const CURRENT_PATTERN: *const SubscriptionPattern = 1 as *const SubscriptionPattern; -const NULL_PATTERN: *const SubscriptionPattern = core::ptr::null(); - impl symmetric_executor::Guest for Guest { type CallbackFunction = Ignore; type CallbackData = OpaqueData; @@ -180,13 +179,7 @@ impl symmetric_executor::Guest for Guest { ); } task.callback.take_if(|CallbackEntry(f, data)| { - match (f)(*data, CURRENT_PATTERN) { - NULL_PATTERN => true, - CURRENT_PATTERN => false, - event => { - let evt_subsrc = unsafe { symmetric_executor::EventSubscription::from_handle(event as usize) }; - NEW_TASKS.lock().unwrap().push(QueuedEvent::new(evt_subsrc, CallbackEntry(*f, *data))); false } - } + matches!((f)(*data), CallbackState::Ready) }); } else { match &task.inner { @@ -213,13 +206,8 @@ impl symmetric_executor::Guest for Guest { tvptr = core::ptr::from_mut(&mut wait); } else { task.callback.take_if(|CallbackEntry(f, data)| { - match (f)(*data, CURRENT_PATTERN) { - NULL_PATTERN => true, - CURRENT_PATTERN => false, - event => { - let evt_subsrc = unsafe { symmetric_executor::EventSubscription::from_handle(event as usize) }; - NEW_TASKS.lock().unwrap().push(QueuedEvent::new(evt_subsrc, CallbackEntry(*f, *data))); false } - } }); + matches!((f)(*data), CallbackState::Ready) + }); } } } @@ -355,8 +343,7 @@ struct EventInner { struct EventGenerator(Arc>); -struct SubscriptionPattern; -type CallbackType = fn(*mut OpaqueData, *const SubscriptionPattern) -> *const SubscriptionPattern; +type CallbackType = fn(*mut OpaqueData) -> CallbackState; struct CallbackEntry(CallbackType, *mut OpaqueData); unsafe impl Send for CallbackEntry {} From fe285f8c040d1229f161aa1d43691bdd31b40a78 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 2 May 2025 11:27:15 +0200 Subject: [PATCH 547/672] correct wrongly generated code --- crates/symmetric_executor/cpp-client/async_support.h | 3 ++- crates/symmetric_executor/symmetric_stream/src/stream_impl.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/symmetric_executor/cpp-client/async_support.h b/crates/symmetric_executor/cpp-client/async_support.h index 5f4fb0230..b4ad6881f 100644 --- a/crates/symmetric_executor/cpp-client/async_support.h +++ b/crates/symmetric_executor/cpp-client/async_support.h @@ -129,7 +129,8 @@ template struct stream_writer { void write(std::vector&& data) { while (!data.empty()) { if (!handle.IsReadyToWrite()) { - symmetric::runtime::symmetric_executor::BlockOn(handle.WriteReadySubscribe()); + abort(); + // symmetric::runtime::symmetric_executor::BlockOn(handle.WriteReadySubscribe()); } auto buffer = handle.StartWriting(); auto capacity = buffer.Capacity(); diff --git a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs index d0cb7cb59..686ab7093 100644 --- a/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs +++ b/crates/symmetric_executor/symmetric_stream/src/stream_impl.rs @@ -1211,7 +1211,7 @@ pub mod exports { pub unsafe fn _export_method_stream_obj_finish_writing_cabi( arg0: *mut u8, arg1: i32, - arg2: i32, + arg2: *mut u8, ) { unsafe { #[cfg(target_arch = "wasm32")] @@ -1446,7 +1446,7 @@ pub mod exports { } #[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_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Efinish_writing(arg0: *mut u8,arg1: i32,arg2: i32,) { + unsafe extern "C" fn symmetricX3AruntimeX2Fsymmetric_streamX400X2E2X2E1X00X5BmethodX5Dstream_objX2Efinish_writing(arg0: *mut u8,arg1: i32,arg2: *mut u8,) { unsafe { $($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")] From 49ff78ac3eeadce11efcdd4484cb839ef9b46029 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 2 May 2025 11:40:48 +0200 Subject: [PATCH 548/672] lift and lower support on futures --- .../src/async_support/future_support.rs | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) 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 index 817c5c585..656541846 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/future_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs @@ -88,14 +88,16 @@ pub struct FutureReader { handle: Stream, // future: Option>> + 'static + Send>>>, _phantom: PhantomData, + lift: unsafe fn(src: *const u8) -> T, } impl FutureReader { - pub fn new(handle: Stream) -> Self { + pub fn new(handle: Stream, lift: unsafe fn(src: *const u8) -> T) -> Self { Self { handle, // future: None, _phantom: PhantomData, + lift, } } @@ -106,8 +108,8 @@ impl FutureReader { } } - pub unsafe fn from_handle(handle: *mut u8) -> Self { - Self::new(unsafe { Stream::from_handle(handle as usize) }) + pub unsafe fn from_handle(handle: *mut u8, lift: unsafe fn(src: *const u8) -> T) -> Self { + Self::new(unsafe { Stream::from_handle(handle as usize) }, lift) } pub fn take_handle(&self) -> *mut () { @@ -123,6 +125,7 @@ impl Future for CancelableRead { if me.future.is_none() { let handle = me.reader.handle.clone(); + let lift = me.reader.lift; me.future = Some(Box::pin(async move { let mut buffer0 = MaybeUninit::::uninit(); let address = unsafe { Address::from_handle(&mut buffer0 as *mut _ as usize) }; @@ -135,7 +138,7 @@ impl Future for CancelableRead { if let Some(buffer2) = buffer2 { let count = buffer2.get_size(); if count > 0 { - Some(unsafe { buffer0.assume_init() }) + Some(unsafe { (lift)(buffer2.get_address().take_handle() as *const u8) }) } else { None } @@ -177,8 +180,14 @@ impl IntoFuture for FutureReader { } } -pub fn new_future() -> (FutureWriter, FutureReader) { +pub fn new_future( + lower: unsafe fn(value: T, dst: *mut u8), + lift: unsafe fn(src: *const u8) -> T, +) -> (FutureWriter, FutureReader) { let handle = Stream::new(); let handle2 = handle.clone(); - (FutureWriter::new(handle), FutureReader::new(handle2)) + ( + FutureWriter::new(handle, lower), + FutureReader::new(handle2, lift), + ) } From d6a91d804745f06353b541db7ffaf29a895616fe Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 2 May 2025 12:50:16 +0200 Subject: [PATCH 549/672] more complete future lifting and lowering support --- .../future/src/future_world.rs | 19 +++++++++++++++---- .../tests/symmetric_future/future/src/lib.rs | 2 +- .../future_cpp/future_world.cpp | 11 ++++++++++- .../rust-client/src/async_support.rs | 7 ++----- .../src/async_support/future_support.rs | 15 ++++++++------- 5 files changed, 36 insertions(+), 18 deletions(-) diff --git a/crates/cpp/tests/symmetric_future/future/src/future_world.rs b/crates/cpp/tests/symmetric_future/future/src/future_world.rs index 4d075cb46..7465f66ae 100644 --- a/crates/cpp/tests/symmetric_future/future/src/future_world.rs +++ b/crates/cpp/tests/symmetric_future/future/src/future_world.rs @@ -24,6 +24,7 @@ pub mod test { let ret = testX3AtestX2Ffuture_sourceX00create(); wit_bindgen_symmetric_rt::async_support::FutureReader::new( wit_bindgen_symmetric_rt::async_support::Stream::from_handle(ret), + ::lift ) } } @@ -144,7 +145,7 @@ mod _rt { self as i32 } } - pub use alloc_crate::boxed::Box; + // pub use alloc_crate::boxed::Box; #[cfg(target_arch = "wasm32")] pub fn run_ctors_once() { @@ -156,14 +157,24 @@ pub mod wit_future { #![allow(dead_code, unused_variables, clippy::all)] #[doc(hidden)] - pub trait FuturePayload: Unpin + Sized + 'static {} - impl FuturePayload for u32 {} + pub trait FuturePayload: Unpin + Sized + 'static { + unsafe fn lower(value: Self, dst: *mut u8) { todo!() } + unsafe fn lift(src: *const u8) -> Self { todo!() } + } + impl FuturePayload for u32 { + unsafe fn lower(value: Self, dst: *mut u8) { + *dst.cast() = value; + } + unsafe fn lift(src: *const u8) -> Self { + *src.cast() + } + } /// Creates a new Component Model `future` with the specified payload type. pub fn new() -> ( wit_bindgen_symmetric_rt::async_support::FutureWriter, wit_bindgen_symmetric_rt::async_support::FutureReader, ) { - wit_bindgen_symmetric_rt::async_support::future_support::new_future() + wit_bindgen_symmetric_rt::async_support::future_support::new_future(T::lower, T::lift) } } diff --git a/crates/cpp/tests/symmetric_future/future/src/lib.rs b/crates/cpp/tests/symmetric_future/future/src/lib.rs index 1ca28dcfd..18ed6147e 100644 --- a/crates/cpp/tests/symmetric_future/future/src/lib.rs +++ b/crates/cpp/tests/symmetric_future/future/src/lib.rs @@ -9,7 +9,7 @@ struct MyStruct; impl future_world::exports::test::test::future_test::Guest for MyStruct { fn create() -> async_support::FutureReader { - let (write, read) = async_support::future_support::new_future(); + let (write, read) = future_world::wit_future::new(); let input = future_source::create(); async_support::spawn(async move { let input = input.await.unwrap(); diff --git a/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp b/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp index 3a2dc38ce..da1f26953 100644 --- a/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp +++ b/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp @@ -23,11 +23,20 @@ void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { #include "async_support.h" + +template +struct IntLifting { + static constexpr size_t SIZE = sizeof(T); + static T lift(uint8_t const*ptr) { + return *(T const*)ptr; + } +}; + extern "C" uint8_t* testX3AtestX2Ffuture_sourceX00create(); std::future test::test::future_source::Create() { auto ret = testX3AtestX2Ffuture_sourceX00create(); - return lift_future(ret); + return lift_future>(ret); } extern "C" uint8_t* testX3AtestX2Ffuture_testX00create() diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 56f302c75..d5f72236b 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -5,11 +5,8 @@ use std::{ task::{Context, Poll, RawWaker, RawWakerVTable}, }; -use crate::{ - module::symmetric::runtime::symmetric_executor::{ - CallbackState, EventGenerator, EventSubscription, - }, - EventSubscription2, +use crate::module::symmetric::runtime::symmetric_executor::{ + CallbackState, EventGenerator, EventSubscription, }; pub use future_support::{FutureReader, FutureWriter}; 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 index 656541846..8bf8c563c 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/future_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs @@ -12,20 +12,20 @@ use crate::symmetric_stream::{Address, Buffer}; use super::{wait_on, Stream}; -//use super::Future; - pub struct FutureWriter { handle: Stream, - future: Option + 'static + Send>>>, + // future: Option + 'static + Send>>>, _phantom: PhantomData, + lower: unsafe fn(value: T, dst: *mut u8), } impl FutureWriter { - pub fn new(handle: Stream) -> Self { + pub fn new(handle: Stream, lower: unsafe fn(value: T, dst: *mut u8)) -> Self { Self { handle, - future: None, + // future: None, _phantom: PhantomData, + lower, } } @@ -56,14 +56,15 @@ impl Future for CancelableWrite { if me.future.is_none() { let handle = me.writer.handle.clone(); let data = me.data.take().unwrap(); + let lower = me.writer.lower; 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; - unsafe { (*addr).write(data) }; + let addr = buffer.get_address().take_handle() as *mut MaybeUninit as *mut u8; + unsafe { (lower)(data, addr) }; buffer.set_size(1); handle.finish_writing(Some(buffer)); }) as Pin + Send>>); From 5cc17c31d73c90332f7a4754f7e0812554cbeaaf Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 2 May 2025 17:12:33 +0200 Subject: [PATCH 550/672] new string test --- .../src/async_support/future_support.rs | 10 +--------- .../async/future-string/runner.rs | 14 ++++++++++++++ .../runtime-async/async/future-string/test.rs | 19 +++++++++++++++++++ .../async/future-string/test.wit | 12 ++++++++++++ 4 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 tests/runtime-async/async/future-string/runner.rs create mode 100644 tests/runtime-async/async/future-string/test.rs create mode 100644 tests/runtime-async/async/future-string/test.wit 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 index 8bf8c563c..02aafe269 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/future_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs @@ -51,8 +51,6 @@ impl Future for CancelableWrite { 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(); @@ -69,13 +67,7 @@ impl Future for CancelableWrite { handle.finish_writing(Some(buffer)); }) as Pin + Send>>); } - match me.future.as_mut().unwrap().poll_unpin(cx) { - Poll::Ready(()) => { - // me.writer = None; - Poll::Ready(()) - } - Poll::Pending => Poll::Pending, - } + me.future.as_mut().unwrap().poll_unpin(cx) } } diff --git a/tests/runtime-async/async/future-string/runner.rs b/tests/runtime-async/async/future-string/runner.rs new file mode 100644 index 000000000..fa7f23b12 --- /dev/null +++ b/tests/runtime-async/async/future-string/runner.rs @@ -0,0 +1,14 @@ +//@ args = '--async=-none' + +include!(env!("BINDINGS")); + +use wit_bindgen::rt::async_support; + +use crate::a::b::the_test::f; + +fn main() { + async_support::block_on(async { + let result = f().await; + assert_eq!(result, String::from("Hello")); + }); +} diff --git a/tests/runtime-async/async/future-string/test.rs b/tests/runtime-async/async/future-string/test.rs new file mode 100644 index 000000000..bcdb8cd02 --- /dev/null +++ b/tests/runtime-async/async/future-string/test.rs @@ -0,0 +1,19 @@ +include!(env!("BINDINGS")); + +struct Component; + +export!(Component); + +use crate::exports::a::b::the_test::Guest; + +use wit_bindgen::rt::async_support::FutureReader; + +impl Guest for Component { + fn f() -> wit::FutureReader { + let (wr,rd) = wit_future::new(); + async_support::spawn(move || async { + wr.write(String::from("Hello")).await; + }); + rd + } +} diff --git a/tests/runtime-async/async/future-string/test.wit b/tests/runtime-async/async/future-string/test.wit new file mode 100644 index 000000000..174f61646 --- /dev/null +++ b/tests/runtime-async/async/future-string/test.wit @@ -0,0 +1,12 @@ +package a:b; + +interface the-test { + f: func() -> future; +} + +world test { + export the-test; +} +world runner { + import the-test; +} From 947f2365f50c0969b7dd0bc844ab31a42d110149 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 09:41:22 +0200 Subject: [PATCH 551/672] example with stream lifting and lowering --- .../tests/symmetric_stream_string/Cargo.toml | 9 +++++++++ .../tests/symmetric_stream_string/runner.rs | 14 ++++++++++++++ .../tests/symmetric_stream_string/src/main.rs | 4 ++++ .../cpp/tests/symmetric_stream_string/test.rs | 19 +++++++++++++++++++ .../tests/symmetric_stream_string/test.wit | 12 ++++++++++++ 5 files changed, 58 insertions(+) create mode 100644 crates/cpp/tests/symmetric_stream_string/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_stream_string/runner.rs create mode 100644 crates/cpp/tests/symmetric_stream_string/src/main.rs create mode 100644 crates/cpp/tests/symmetric_stream_string/test.rs create mode 100644 crates/cpp/tests/symmetric_stream_string/test.wit diff --git a/crates/cpp/tests/symmetric_stream_string/Cargo.toml b/crates/cpp/tests/symmetric_stream_string/Cargo.toml new file mode 100644 index 000000000..c33cbc776 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "stream-string" +version = "0.1.0" +edition = "2021" + +[dependencies] +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_string/runner.rs b/crates/cpp/tests/symmetric_stream_string/runner.rs new file mode 100644 index 000000000..fa7f23b12 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/runner.rs @@ -0,0 +1,14 @@ +//@ args = '--async=-none' + +include!(env!("BINDINGS")); + +use wit_bindgen::rt::async_support; + +use crate::a::b::the_test::f; + +fn main() { + async_support::block_on(async { + let result = f().await; + assert_eq!(result, String::from("Hello")); + }); +} diff --git a/crates/cpp/tests/symmetric_stream_string/src/main.rs b/crates/cpp/tests/symmetric_stream_string/src/main.rs new file mode 100644 index 000000000..7baf10466 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/src/main.rs @@ -0,0 +1,4 @@ +mod test; + +include!("../runner.rs"); + diff --git a/crates/cpp/tests/symmetric_stream_string/test.rs b/crates/cpp/tests/symmetric_stream_string/test.rs new file mode 100644 index 000000000..bcdb8cd02 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/test.rs @@ -0,0 +1,19 @@ +include!(env!("BINDINGS")); + +struct Component; + +export!(Component); + +use crate::exports::a::b::the_test::Guest; + +use wit_bindgen::rt::async_support::FutureReader; + +impl Guest for Component { + fn f() -> wit::FutureReader { + let (wr,rd) = wit_future::new(); + async_support::spawn(move || async { + wr.write(String::from("Hello")).await; + }); + rd + } +} diff --git a/crates/cpp/tests/symmetric_stream_string/test.wit b/crates/cpp/tests/symmetric_stream_string/test.wit new file mode 100644 index 000000000..174f61646 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/test.wit @@ -0,0 +1,12 @@ +package a:b; + +interface the-test { + f: func() -> future; +} + +world test { + export the-test; +} +world runner { + import the-test; +} From 5d5dcd14b353046b4cf9b97d72a42112f2dcbf94 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 10:09:57 +0200 Subject: [PATCH 552/672] empty skeleton --- .../bindings/runner.rs | 160 ++++++++++++++ .../symmetric_stream_string/bindings/test.rs | 199 ++++++++++++++++++ .../tests/symmetric_stream_string/generate.sh | 5 + .../tests/symmetric_stream_string/src/test.rs | 1 + 4 files changed, 365 insertions(+) create mode 100644 crates/cpp/tests/symmetric_stream_string/bindings/runner.rs create mode 100644 crates/cpp/tests/symmetric_stream_string/bindings/test.rs create mode 100755 crates/cpp/tests/symmetric_stream_string/generate.sh create mode 100644 crates/cpp/tests/symmetric_stream_string/src/test.rs diff --git a/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs b/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs new file mode 100644 index 000000000..18d2dcd4f --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs @@ -0,0 +1,160 @@ +// Generated by `wit-bindgen` 0.41.0. DO NOT EDIT! +// Options used: +#[allow(dead_code, clippy::all)] +pub mod a { + pub mod b { + + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod the_test { + #[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)] + #[allow(async_fn_in_trait)] + pub fn f() -> wit_bindgen_symmetric_rt::async_support::FutureReader<_rt::String>{ + unsafe { + + #[link(wasm_import_module = "a:b/the-test")] + unsafe extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "f")] + fn aX3AbX2Fthe_testX00f() -> *mut u8; + } + let ret = aX3AbX2Fthe_testX00f(); + wit_bindgen_symmetric_rt::async_support::FutureReader::from_handle(ret) + } + } + + } + + } +} +mod _rt { + #![allow(dead_code, clippy::all)] + pub use alloc_crate::string::String; + pub use alloc_crate::vec::Vec; + pub unsafe fn string_lift(bytes: Vec) -> String { + if cfg!(debug_assertions) { + String::from_utf8(bytes).unwrap() + } else { + unsafe { String::from_utf8_unchecked(bytes) } + } + } + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + unsafe { + 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; +} +pub mod wit_future { + #![allow(dead_code, unused_variables, clippy::all)] + + #[doc(hidden)] + pub trait FuturePayload: Unpin + Sized + 'static {} + #[doc(hidden)] + #[allow(unused_unsafe)] + pub mod vtable0 { + + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn cancel_write(_: u32) -> u32 { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn cancel_read(_: u32) -> u32 { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn close_writable(_: u32) { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn close_readable(_: u32) { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn new() -> u64 { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn start_read(_: u32, _: *mut u8) -> u32 { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn start_write(_: u32, _: *const u8) -> u32 { unreachable!() } + + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "a:b/the-test")] + unsafe extern "C" { + #[link_name = "[future-new-0]f"] + fn new() -> u64; + #[link_name = "[future-cancel-write-0]f"] + fn cancel_write(_: u32) -> u32; + #[link_name = "[future-cancel-read-0]f"] + fn cancel_read(_: u32) -> u32; + #[link_name = "[future-close-writable-0]f"] + fn close_writable(_: u32); + #[link_name = "[future-close-readable-0]f"] + fn close_readable(_: u32); + #[link_name = "[async-lower][future-read-0]f"] + fn start_read(_: u32, _: *mut u8) -> u32; + #[link_name = "[async-lower][future-write-0]f"] + fn start_write(_: u32, _: *const u8) -> u32; + } + + unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + let len2 = l1; + let bytes2 = if len2>0 { + super::super::_rt::Vec::from_raw_parts(l0.cast(), len2, len2) + } else { Default::default() }; + + super::super::_rt::string_lift(bytes2) } } + unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { unsafe { let vec0 = value; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + *ptr.add(::core::mem::size_of::<*const u8>()).cast::() = len0; + *ptr.add(0).cast::<*mut u8>() = ptr0.cast_mut(); + } } + unsafe fn dealloc_lists(ptr: *mut u8) { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + super::super::_rt::cabi_dealloc(l0, l1, 1); +} } + +pub static VTABLE: wit_bindgen_symmetric_rt::async_support::FutureVtable = wit_bindgen_symmetric_rt::async_support::FutureVtable:: { + cancel_write, + cancel_read, + close_writable, + close_readable, + dealloc_lists, + layout: unsafe { + ::std::alloc::Layout::from_size_align_unchecked(8, 4) + }, + lift, + lower, + new, + start_read, + start_write, +}; + +impl super::FuturePayload for super::super::_rt::String { + const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::FutureVtable = &VTABLE; +} +} +/// Creates a new Component Model `future` with the specified payload type. +pub fn new() -> (wit_bindgen_symmetric_rt::async_support::FutureWriter, wit_bindgen_symmetric_rt::async_support::FutureReader) { + wit_bindgen_symmetric_rt::async_support::future_support::new_future() +} +} + +#[cfg(target_arch = "wasm32")] +#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:a:b:runner:encoded world")] +#[doc(hidden)] +#[allow(clippy::octal_escapes)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 180] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x078\x01A\x02\x01A\x02\x01\ +B\x03\x01e\x01s\x01@\0\0\0\x04\0\x01f\x01\x01\x03\0\x0ca:b/the-test\x05\0\x04\0\x0a\ +a:b/runner\x04\0\x0b\x0c\x01\0\x06runner\x03\0\0\0G\x09producers\x01\x0cprocesse\ +d-by\x02\x0dwit-component\x070.229.0\x10wit-bindgen-rust\x060.41.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_string/bindings/test.rs b/crates/cpp/tests/symmetric_stream_string/bindings/test.rs new file mode 100644 index 000000000..324a82c27 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/bindings/test.rs @@ -0,0 +1,199 @@ +// Generated by `wit-bindgen` 0.41.0. DO NOT EDIT! +// Options used: +#[allow(dead_code, clippy::all)] +pub mod exports { + pub mod a { + pub mod b { + + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod the_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, unused_unsafe)] + pub unsafe fn _export_f_cabi() -> *mut u8 { unsafe {#[cfg(target_arch="wasm32")] + _rt::run_ctors_once();let result0 = { + T::f() + }; + (result0).take_handle() as *mut u8 + } } + pub trait Guest { + #[allow(async_fn_in_trait)] + fn f() -> wit_bindgen_symmetric_rt::async_support::FutureReader<_rt::String>; + } + #[doc(hidden)] + + macro_rules! __export_a_b_the_test_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 aX3AbX2Fthe_testX00f() -> *mut u8 { + unsafe { $($path_to_types)*::_export_f_cabi::<$ty>() } + } + };); + } + #[doc(hidden)] + pub(crate) use __export_a_b_the_test_cabi; + + } + + } +} +} +mod _rt { + #![allow(dead_code, clippy::all)] + pub use alloc_crate::string::String; + pub use alloc_crate::vec::Vec; + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + unsafe { + let layout = alloc::Layout::from_size_align_unchecked(size, align); + alloc::dealloc(ptr, layout); + } + } + + #[cfg(target_arch = "wasm32")] + pub fn run_ctors_once() { + wit_bindgen::rt::run_ctors_once(); + } + extern crate alloc as alloc_crate; + pub use alloc_crate::alloc; +} +pub mod wit_future { + #![allow(dead_code, unused_variables, clippy::all)] + + #[doc(hidden)] + pub trait FuturePayload: Unpin + Sized + 'static {} + #[doc(hidden)] + #[allow(unused_unsafe)] + pub mod vtable0 { + + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn cancel_write(_: u32) -> u32 { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn cancel_read(_: u32) -> u32 { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn close_writable(_: u32) { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn close_readable(_: u32) { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn new() -> u64 { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn start_read(_: u32, _: *mut u8) -> u32 { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn start_write(_: u32, _: *const u8) -> u32 { unreachable!() } + + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "[export]a:b/the-test")] + unsafe extern "C" { + #[link_name = "[future-new-0]f"] + fn new() -> u64; + #[link_name = "[future-cancel-write-0]f"] + fn cancel_write(_: u32) -> u32; + #[link_name = "[future-cancel-read-0]f"] + fn cancel_read(_: u32) -> u32; + #[link_name = "[future-close-writable-0]f"] + fn close_writable(_: u32); + #[link_name = "[future-close-readable-0]f"] + fn close_readable(_: u32); + #[link_name = "[async-lower][future-read-0]f"] + fn start_read(_: u32, _: *mut u8) -> u32; + #[link_name = "[async-lower][future-write-0]f"] + fn start_write(_: u32, _: *const u8) -> u32; + } + + unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + let len2 = l1; + let string2 = String::from(std::str::from_utf8(std::slice::from_raw_parts(l0, len2)).unwrap()); + + string2 } } + unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { unsafe { let vec0 = (value.into_bytes()).into_boxed_slice(); + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + ::core::mem::forget(vec0); + *ptr.add(::core::mem::size_of::<*const u8>()).cast::() = len0; + *ptr.add(0).cast::<*mut u8>() = ptr0.cast_mut(); + } } + unsafe fn dealloc_lists(ptr: *mut u8) { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + super::super::_rt::cabi_dealloc(l0, l1, 1); +} } + +pub static VTABLE: wit_bindgen_symmetric_rt::async_support::FutureVtable = wit_bindgen_symmetric_rt::async_support::FutureVtable:: { + cancel_write, + cancel_read, + close_writable, + close_readable, + dealloc_lists, + layout: unsafe { + ::std::alloc::Layout::from_size_align_unchecked(8, 4) + }, + lift, + lower, + new, + start_read, + start_write, +}; + +impl super::FuturePayload for super::super::_rt::String { + const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::FutureVtable = &VTABLE; +} +} +/// Creates a new Component Model `future` with the specified payload type. +pub fn new() -> (wit_bindgen_symmetric_rt::async_support::FutureWriter, wit_bindgen_symmetric_rt::async_support::FutureReader) { + 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_test_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::a::b::the_test::__export_a_b_the_test_cabi!($ty with_types_in $($path_to_types_root)*::exports::a::b::the_test); + ) +} +#[doc(inline)] +pub(crate) use __export_test_impl as export; + +#[cfg(target_arch = "wasm32")] +#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:a:b:test:encoded world")] +#[doc(hidden)] +#[allow(clippy::octal_escapes)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 176] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x076\x01A\x02\x01A\x02\x01\ +B\x03\x01e\x01s\x01@\0\0\0\x04\0\x01f\x01\x01\x04\0\x0ca:b/the-test\x05\0\x04\0\x08\ +a:b/test\x04\0\x0b\x0a\x01\0\x04test\x03\0\0\0G\x09producers\x01\x0cprocessed-by\ +\x02\x0dwit-component\x070.229.0\x10wit-bindgen-rust\x060.41.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_string/generate.sh b/crates/cpp/tests/symmetric_stream_string/generate.sh new file mode 100755 index 000000000..31888752d --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/generate.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +cd bindings +../../../../../target/debug/wit-bindgen rust ../test.wit --symmetric -w test +../../../../../target/debug/wit-bindgen rust ../test.wit --symmetric -w runner diff --git a/crates/cpp/tests/symmetric_stream_string/src/test.rs b/crates/cpp/tests/symmetric_stream_string/src/test.rs new file mode 100644 index 000000000..ff89ea8be --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/src/test.rs @@ -0,0 +1 @@ +include!("../test.rs"); \ No newline at end of file From a082c1bf94c7c1ca4fa00826d0b991801b1c00ae Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 10:14:24 +0200 Subject: [PATCH 553/672] cargo fmt, finish include --- crates/cpp/tests/symmetric_stream_string/Cargo.toml | 8 +++++--- crates/cpp/tests/symmetric_stream_string/runner.rs | 3 ++- crates/cpp/tests/symmetric_stream_string/src/main.rs | 1 - crates/cpp/tests/symmetric_stream_string/src/test.rs | 2 +- crates/cpp/tests/symmetric_stream_string/test.rs | 3 ++- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream_string/Cargo.toml b/crates/cpp/tests/symmetric_stream_string/Cargo.toml index c33cbc776..ca314be52 100644 --- a/crates/cpp/tests/symmetric_stream_string/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream_string/Cargo.toml @@ -1,9 +1,11 @@ +[workspace] + [package] name = "stream-string" version = "0.1.0" edition = "2021" [dependencies] -symmetric_executor = { path = "../../../../symmetric_executor", features = ["trace"]} -symmetric_stream = { path = "../../../../symmetric_executor/symmetric_stream" } -wit-bindgen-symmetric-rt = { path = "../../../../symmetric_executor/rust-client" } +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_string/runner.rs b/crates/cpp/tests/symmetric_stream_string/runner.rs index fa7f23b12..815647e47 100644 --- a/crates/cpp/tests/symmetric_stream_string/runner.rs +++ b/crates/cpp/tests/symmetric_stream_string/runner.rs @@ -1,6 +1,7 @@ //@ args = '--async=-none' -include!(env!("BINDINGS")); +//include!(env!("BINDINGS")); +include!("bindings/runner.rs"); use wit_bindgen::rt::async_support; diff --git a/crates/cpp/tests/symmetric_stream_string/src/main.rs b/crates/cpp/tests/symmetric_stream_string/src/main.rs index 7baf10466..8eac55a1f 100644 --- a/crates/cpp/tests/symmetric_stream_string/src/main.rs +++ b/crates/cpp/tests/symmetric_stream_string/src/main.rs @@ -1,4 +1,3 @@ mod test; include!("../runner.rs"); - diff --git a/crates/cpp/tests/symmetric_stream_string/src/test.rs b/crates/cpp/tests/symmetric_stream_string/src/test.rs index ff89ea8be..b61c677fc 100644 --- a/crates/cpp/tests/symmetric_stream_string/src/test.rs +++ b/crates/cpp/tests/symmetric_stream_string/src/test.rs @@ -1 +1 @@ -include!("../test.rs"); \ No newline at end of file +include!("../test.rs"); diff --git a/crates/cpp/tests/symmetric_stream_string/test.rs b/crates/cpp/tests/symmetric_stream_string/test.rs index bcdb8cd02..18a945982 100644 --- a/crates/cpp/tests/symmetric_stream_string/test.rs +++ b/crates/cpp/tests/symmetric_stream_string/test.rs @@ -1,4 +1,5 @@ -include!(env!("BINDINGS")); +// include!(env!("BINDINGS")); +include!("bindings/test.rs"); struct Component; From 334e93757be33e48f048724b8f3077cf6aea71e8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 10:51:42 +0200 Subject: [PATCH 554/672] stream string example --- .../tests/symmetric_stream_string/Cargo.lock | 214 ++++++++++++++++++ .../tests/symmetric_stream_string/Cargo.toml | 1 + .../tests/symmetric_stream_string/format.sh | 3 + .../tests/symmetric_stream_string/runner.rs | 2 +- .../cpp/tests/symmetric_stream_string/test.rs | 4 +- .../dummy-bindgen/Cargo.toml | 8 + .../dummy-bindgen/src/lib.rs | 6 + 7 files changed, 235 insertions(+), 3 deletions(-) create mode 100644 crates/cpp/tests/symmetric_stream_string/Cargo.lock create mode 100755 crates/cpp/tests/symmetric_stream_string/format.sh create mode 100644 crates/symmetric_executor/dummy-bindgen/Cargo.toml create mode 100644 crates/symmetric_executor/dummy-bindgen/src/lib.rs diff --git a/crates/cpp/tests/symmetric_stream_string/Cargo.lock b/crates/cpp/tests/symmetric_stream_string/Cargo.lock new file mode 100644 index 000000000..38c0682a8 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/Cargo.lock @@ -0,0 +1,214 @@ +# 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.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mini-bindgen" +version = "0.1.0" +dependencies = [ + "dummy-rt", + "wit-bindgen-symmetric-rt", +] + +[[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.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +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 = "stream-string" +version = "0.1.0" +dependencies = [ + "mini-bindgen", + "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.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "wit-bindgen-symmetric-rt" +version = "0.36.0" +dependencies = [ + "dummy-rt", + "futures", +] diff --git a/crates/cpp/tests/symmetric_stream_string/Cargo.toml b/crates/cpp/tests/symmetric_stream_string/Cargo.toml index ca314be52..3e7228443 100644 --- a/crates/cpp/tests/symmetric_stream_string/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream_string/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" symmetric_executor = { path = "../../../symmetric_executor", features = ["trace"]} symmetric_stream = { path = "../../../symmetric_executor/symmetric_stream" } wit-bindgen-symmetric-rt = { path = "../../../symmetric_executor/rust-client" } +wit-bindgen = { path = "../../../symmetric_executor/dummy-bindgen", package = "mini-bindgen" } diff --git a/crates/cpp/tests/symmetric_stream_string/format.sh b/crates/cpp/tests/symmetric_stream_string/format.sh new file mode 100755 index 000000000..361520641 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/format.sh @@ -0,0 +1,3 @@ +#!/bin/sh +rustfmt bindings/*.rs +rustfmt *.rs diff --git a/crates/cpp/tests/symmetric_stream_string/runner.rs b/crates/cpp/tests/symmetric_stream_string/runner.rs index 815647e47..f67ac0a49 100644 --- a/crates/cpp/tests/symmetric_stream_string/runner.rs +++ b/crates/cpp/tests/symmetric_stream_string/runner.rs @@ -10,6 +10,6 @@ use crate::a::b::the_test::f; fn main() { async_support::block_on(async { let result = f().await; - assert_eq!(result, String::from("Hello")); + assert_eq!(result, Some(String::from("Hello"))); }); } diff --git a/crates/cpp/tests/symmetric_stream_string/test.rs b/crates/cpp/tests/symmetric_stream_string/test.rs index 18a945982..4216f3282 100644 --- a/crates/cpp/tests/symmetric_stream_string/test.rs +++ b/crates/cpp/tests/symmetric_stream_string/test.rs @@ -5,12 +5,12 @@ struct Component; export!(Component); -use crate::exports::a::b::the_test::Guest; +use exports::a::b::the_test::Guest; use wit_bindgen::rt::async_support::FutureReader; impl Guest for Component { - fn f() -> wit::FutureReader { + fn f() -> FutureReader { let (wr,rd) = wit_future::new(); async_support::spawn(move || async { wr.write(String::from("Hello")).await; diff --git a/crates/symmetric_executor/dummy-bindgen/Cargo.toml b/crates/symmetric_executor/dummy-bindgen/Cargo.toml new file mode 100644 index 000000000..30173c5a7 --- /dev/null +++ b/crates/symmetric_executor/dummy-bindgen/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "mini-bindgen" +version.workspace = true +edition.workspace = true + +[dependencies] +wit-bindgen-symmetric-rt = { path = "../rust-client" } +dummy-rt = { path = "../dummy-rt" } diff --git a/crates/symmetric_executor/dummy-bindgen/src/lib.rs b/crates/symmetric_executor/dummy-bindgen/src/lib.rs new file mode 100644 index 000000000..72695811a --- /dev/null +++ b/crates/symmetric_executor/dummy-bindgen/src/lib.rs @@ -0,0 +1,6 @@ +// this crate tries to minimize dependencies for symmetric bindings + +pub mod rt { + pub use dummy_rt::rt::maybe_link_cabi_realloc; + pub use wit_bindgen_symmetric_rt::async_support; +} From 5b21e37e9df63c99db0fc61e33c4cb8aa74c9c11 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 11:03:14 +0200 Subject: [PATCH 555/672] rustfmt --- .../bindings/runner.rs | 216 ++++++------- .../symmetric_stream_string/bindings/test.rs | 295 ++++++++++-------- .../tests/symmetric_stream_string/format.sh | 4 +- .../cpp/tests/symmetric_stream_string/test.rs | 2 +- 4 files changed, 256 insertions(+), 261 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs b/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs index 18d2dcd4f..f289004b0 100644 --- a/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs +++ b/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs @@ -2,144 +2,111 @@ // Options used: #[allow(dead_code, clippy::all)] pub mod a { - pub mod b { + pub mod b { - #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] - pub mod the_test { - #[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)] - #[allow(async_fn_in_trait)] - pub fn f() -> wit_bindgen_symmetric_rt::async_support::FutureReader<_rt::String>{ - unsafe { + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod the_test { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = + super::super::super::__link_custom_section_describing_imports; - #[link(wasm_import_module = "a:b/the-test")] - unsafe extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "f")] - fn aX3AbX2Fthe_testX00f() -> *mut u8; - } - let ret = aX3AbX2Fthe_testX00f(); - wit_bindgen_symmetric_rt::async_support::FutureReader::from_handle(ret) + use super::super::super::_rt; + #[allow(unused_unsafe, clippy::all)] + #[allow(async_fn_in_trait)] + pub fn f() -> wit_bindgen_symmetric_rt::async_support::FutureReader<_rt::String> { + unsafe { + #[link(wasm_import_module = "a:b/the-test")] + unsafe extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "f")] + fn aX3AbX2Fthe_testX00f() -> *mut u8; + } + let ret = aX3AbX2Fthe_testX00f(); + wit_bindgen_symmetric_rt::async_support::FutureReader::from_handle(ret) + } + } } - } - } - - } } mod _rt { - #![allow(dead_code, clippy::all)] - pub use alloc_crate::string::String; - pub use alloc_crate::vec::Vec; - pub unsafe fn string_lift(bytes: Vec) -> String { - if cfg!(debug_assertions) { - String::from_utf8(bytes).unwrap() - } else { - unsafe { String::from_utf8_unchecked(bytes) } - } - } - pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { - if size == 0 { - return; + #![allow(dead_code, clippy::all)] + pub use alloc_crate::string::String; + pub use alloc_crate::vec::Vec; + pub unsafe fn string_lift(bytes: Vec) -> String { + if cfg!(debug_assertions) { + String::from_utf8(bytes).unwrap() + } else { + unsafe { String::from_utf8_unchecked(bytes) } + } } - unsafe { - let layout = alloc::Layout::from_size_align_unchecked(size, align); - alloc::dealloc(ptr, layout); + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + unsafe { + 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; + extern crate alloc as alloc_crate; + pub use alloc_crate::alloc; } pub mod wit_future { - #![allow(dead_code, unused_variables, clippy::all)] - - #[doc(hidden)] - pub trait FuturePayload: Unpin + Sized + 'static {} - #[doc(hidden)] - #[allow(unused_unsafe)] - pub mod vtable0 { + #![allow(dead_code, unused_variables, clippy::all)] - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn cancel_write(_: u32) -> u32 { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn cancel_read(_: u32) -> u32 { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn close_writable(_: u32) { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn close_readable(_: u32) { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn new() -> u64 { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn start_read(_: u32, _: *mut u8) -> u32 { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn start_write(_: u32, _: *const u8) -> u32 { unreachable!() } - - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "a:b/the-test")] - unsafe extern "C" { - #[link_name = "[future-new-0]f"] - fn new() -> u64; - #[link_name = "[future-cancel-write-0]f"] - fn cancel_write(_: u32) -> u32; - #[link_name = "[future-cancel-read-0]f"] - fn cancel_read(_: u32) -> u32; - #[link_name = "[future-close-writable-0]f"] - fn close_writable(_: u32); - #[link_name = "[future-close-readable-0]f"] - fn close_readable(_: u32); - #[link_name = "[async-lower][future-read-0]f"] - fn start_read(_: u32, _: *mut u8) -> u32; - #[link_name = "[async-lower][future-write-0]f"] - fn start_write(_: u32, _: *const u8) -> u32; + #[doc(hidden)] + pub trait FuturePayload: Unpin + Sized + 'static { + unsafe fn lower(value: Self, dst: *mut u8) { + todo!() + } + unsafe fn lift(src: *const u8) -> Self { + todo!() + } } + #[doc(hidden)] + #[allow(unused_unsafe)] + pub mod vtable0 { - unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); - let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); - let len2 = l1; - let bytes2 = if len2>0 { - super::super::_rt::Vec::from_raw_parts(l0.cast(), len2, len2) - } else { Default::default() }; - - super::super::_rt::string_lift(bytes2) } } - unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { unsafe { let vec0 = value; - let ptr0 = vec0.as_ptr().cast::(); - let len0 = vec0.len(); - *ptr.add(::core::mem::size_of::<*const u8>()).cast::() = len0; - *ptr.add(0).cast::<*mut u8>() = ptr0.cast_mut(); - } } - unsafe fn dealloc_lists(ptr: *mut u8) { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); - let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); - super::super::_rt::cabi_dealloc(l0, l1, 1); -} } + unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { + unsafe { + let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + let len2 = l1; + let bytes2 = if len2 > 0 { + super::super::_rt::Vec::from_raw_parts(l0.cast(), len2, len2) + } else { + Default::default() + }; -pub static VTABLE: wit_bindgen_symmetric_rt::async_support::FutureVtable = wit_bindgen_symmetric_rt::async_support::FutureVtable:: { - cancel_write, - cancel_read, - close_writable, - close_readable, - dealloc_lists, - layout: unsafe { - ::std::alloc::Layout::from_size_align_unchecked(8, 4) - }, - lift, - lower, - new, - start_read, - start_write, -}; + super::super::_rt::string_lift(bytes2) + } + } + unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { + unsafe { + let vec0 = value; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + *ptr.add(::core::mem::size_of::<*const u8>()).cast::() = len0; + *ptr.add(0).cast::<*mut u8>() = ptr0.cast_mut(); + } + } + unsafe fn dealloc_lists(ptr: *mut u8) { + unsafe { + let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + super::super::_rt::cabi_dealloc(l0, l1, 1); + } + } -impl super::FuturePayload for super::super::_rt::String { - const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::FutureVtable = &VTABLE; -} -} -/// Creates a new Component Model `future` with the specified payload type. -pub fn new() -> (wit_bindgen_symmetric_rt::async_support::FutureWriter, wit_bindgen_symmetric_rt::async_support::FutureReader) { - wit_bindgen_symmetric_rt::async_support::future_support::new_future() -} + impl super::FuturePayload for super::super::_rt::String {} + } + /// Creates a new Component Model `future` with the specified payload type. + pub fn new() -> ( + wit_bindgen_symmetric_rt::async_support::FutureWriter, + wit_bindgen_symmetric_rt::async_support::FutureReader, + ) { + wit_bindgen_symmetric_rt::async_support::future_support::new_future(T::lower, T::lift) + } } #[cfg(target_arch = "wasm32")] @@ -155,6 +122,5 @@ d-by\x02\x0dwit-component\x070.229.0\x10wit-bindgen-rust\x060.41.0"; #[inline(never)] #[doc(hidden)] pub fn __link_custom_section_describing_imports() { - wit_bindgen::rt::maybe_link_cabi_realloc(); + wit_bindgen::rt::maybe_link_cabi_realloc(); } - diff --git a/crates/cpp/tests/symmetric_stream_string/bindings/test.rs b/crates/cpp/tests/symmetric_stream_string/bindings/test.rs index 324a82c27..86080c397 100644 --- a/crates/cpp/tests/symmetric_stream_string/bindings/test.rs +++ b/crates/cpp/tests/symmetric_stream_string/bindings/test.rs @@ -2,32 +2,34 @@ // Options used: #[allow(dead_code, clippy::all)] pub mod exports { - pub mod a { - pub mod b { - - #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] - pub mod the_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, unused_unsafe)] - pub unsafe fn _export_f_cabi() -> *mut u8 { unsafe {#[cfg(target_arch="wasm32")] - _rt::run_ctors_once();let result0 = { - T::f() - }; - (result0).take_handle() as *mut u8 - } } - pub trait Guest { - #[allow(async_fn_in_trait)] - fn f() -> wit_bindgen_symmetric_rt::async_support::FutureReader<_rt::String>; - } - #[doc(hidden)] - - macro_rules! __export_a_b_the_test_cabi{ + pub mod a { + pub mod b { + + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod the_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, unused_unsafe)] + pub unsafe fn _export_f_cabi() -> *mut u8 { + unsafe { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + let result0 = { T::f() }; + (result0).take_handle() as *mut u8 + } + } + pub trait Guest { + #[allow(async_fn_in_trait)] + fn f() -> wit_bindgen_symmetric_rt::async_support::FutureReader<_rt::String>; + } + #[doc(hidden)] + + macro_rules! __export_a_b_the_test_cabi{ ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = { #[cfg_attr(target_arch = "wasm32", export_name = "f")] @@ -37,120 +39,148 @@ pub mod exports { } };); } - #[doc(hidden)] - pub(crate) use __export_a_b_the_test_cabi; - + #[doc(hidden)] + pub(crate) use __export_a_b_the_test_cabi; + } + } } - - } -} } mod _rt { - #![allow(dead_code, clippy::all)] - pub use alloc_crate::string::String; - pub use alloc_crate::vec::Vec; - pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { - if size == 0 { - return; + #![allow(dead_code, clippy::all)] + pub use alloc_crate::string::String; + pub use alloc_crate::vec::Vec; + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + unsafe { + let layout = alloc::Layout::from_size_align_unchecked(size, align); + alloc::dealloc(ptr, layout); + } } - unsafe { - let layout = alloc::Layout::from_size_align_unchecked(size, align); - alloc::dealloc(ptr, layout); + + #[cfg(target_arch = "wasm32")] + pub fn run_ctors_once() { + wit_bindgen::rt::run_ctors_once(); } - } - - #[cfg(target_arch = "wasm32")] - pub fn run_ctors_once() { - wit_bindgen::rt::run_ctors_once(); - } - extern crate alloc as alloc_crate; - pub use alloc_crate::alloc; + extern crate alloc as alloc_crate; + pub use alloc_crate::alloc; } pub mod wit_future { - #![allow(dead_code, unused_variables, clippy::all)] - - #[doc(hidden)] - pub trait FuturePayload: Unpin + Sized + 'static {} - #[doc(hidden)] - #[allow(unused_unsafe)] - pub mod vtable0 { - - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn cancel_write(_: u32) -> u32 { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn cancel_read(_: u32) -> u32 { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn close_writable(_: u32) { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn close_readable(_: u32) { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn new() -> u64 { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn start_read(_: u32, _: *mut u8) -> u32 { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn start_write(_: u32, _: *const u8) -> u32 { unreachable!() } + #![allow(dead_code, unused_variables, clippy::all)] + + #[doc(hidden)] + pub trait FuturePayload: Unpin + Sized + 'static {} + #[doc(hidden)] + #[allow(unused_unsafe)] + pub mod vtable0 { + + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn cancel_write(_: u32) -> u32 { + unreachable!() + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn cancel_read(_: u32) -> u32 { + unreachable!() + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn close_writable(_: u32) { + unreachable!() + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn close_readable(_: u32) { + unreachable!() + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn new() -> u64 { + unreachable!() + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn start_read(_: u32, _: *mut u8) -> u32 { + unreachable!() + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn start_write(_: u32, _: *const u8) -> u32 { + unreachable!() + } + + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "[export]a:b/the-test")] + unsafe extern "C" { + #[link_name = "[future-new-0]f"] + fn new() -> u64; + #[link_name = "[future-cancel-write-0]f"] + fn cancel_write(_: u32) -> u32; + #[link_name = "[future-cancel-read-0]f"] + fn cancel_read(_: u32) -> u32; + #[link_name = "[future-close-writable-0]f"] + fn close_writable(_: u32); + #[link_name = "[future-close-readable-0]f"] + fn close_readable(_: u32); + #[link_name = "[async-lower][future-read-0]f"] + fn start_read(_: u32, _: *mut u8) -> u32; + #[link_name = "[async-lower][future-write-0]f"] + fn start_write(_: u32, _: *const u8) -> u32; + } + + unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { + unsafe { + let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + let len2 = l1; + let string2 = String::from( + std::str::from_utf8(std::slice::from_raw_parts(l0, len2)).unwrap(), + ); + + string2 + } + } + unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { + unsafe { + let vec0 = (value.into_bytes()).into_boxed_slice(); + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + ::core::mem::forget(vec0); + *ptr.add(::core::mem::size_of::<*const u8>()).cast::() = len0; + *ptr.add(0).cast::<*mut u8>() = ptr0.cast_mut(); + } + } + unsafe fn dealloc_lists(ptr: *mut u8) { + unsafe { + let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + super::super::_rt::cabi_dealloc(l0, l1, 1); + } + } + + pub static VTABLE: wit_bindgen_symmetric_rt::async_support::FutureVtable< + super::super::_rt::String, + > = wit_bindgen_symmetric_rt::async_support::FutureVtable:: { + cancel_write, + cancel_read, + close_writable, + close_readable, + dealloc_lists, + layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(8, 4) }, + lift, + lower, + new, + start_read, + start_write, + }; - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "[export]a:b/the-test")] - unsafe extern "C" { - #[link_name = "[future-new-0]f"] - fn new() -> u64; - #[link_name = "[future-cancel-write-0]f"] - fn cancel_write(_: u32) -> u32; - #[link_name = "[future-cancel-read-0]f"] - fn cancel_read(_: u32) -> u32; - #[link_name = "[future-close-writable-0]f"] - fn close_writable(_: u32); - #[link_name = "[future-close-readable-0]f"] - fn close_readable(_: u32); - #[link_name = "[async-lower][future-read-0]f"] - fn start_read(_: u32, _: *mut u8) -> u32; - #[link_name = "[async-lower][future-write-0]f"] - fn start_write(_: u32, _: *const u8) -> u32; + impl super::FuturePayload for super::super::_rt::String { + const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::FutureVtable = + &VTABLE; + } + } + /// Creates a new Component Model `future` with the specified payload type. + pub fn new() -> ( + wit_bindgen_symmetric_rt::async_support::FutureWriter, + wit_bindgen_symmetric_rt::async_support::FutureReader, + ) { + wit_bindgen_symmetric_rt::async_support::future_support::new_future() } - - unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); - let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); - let len2 = l1; - let string2 = String::from(std::str::from_utf8(std::slice::from_raw_parts(l0, len2)).unwrap()); - - string2 } } - unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { unsafe { let vec0 = (value.into_bytes()).into_boxed_slice(); - let ptr0 = vec0.as_ptr().cast::(); - let len0 = vec0.len(); - ::core::mem::forget(vec0); - *ptr.add(::core::mem::size_of::<*const u8>()).cast::() = len0; - *ptr.add(0).cast::<*mut u8>() = ptr0.cast_mut(); - } } - unsafe fn dealloc_lists(ptr: *mut u8) { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); - let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); - super::super::_rt::cabi_dealloc(l0, l1, 1); -} } - -pub static VTABLE: wit_bindgen_symmetric_rt::async_support::FutureVtable = wit_bindgen_symmetric_rt::async_support::FutureVtable:: { - cancel_write, - cancel_read, - close_writable, - close_readable, - dealloc_lists, - layout: unsafe { - ::std::alloc::Layout::from_size_align_unchecked(8, 4) - }, - lift, - lower, - new, - start_read, - start_write, -}; - -impl super::FuturePayload for super::super::_rt::String { - const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::FutureVtable = &VTABLE; -} -} -/// Creates a new Component Model `future` with the specified payload type. -pub fn new() -> (wit_bindgen_symmetric_rt::async_support::FutureWriter, wit_bindgen_symmetric_rt::async_support::FutureReader) { - wit_bindgen_symmetric_rt::async_support::future_support::new_future() -} } /// Generates `#[unsafe(no_mangle)]` functions to export the specified type as @@ -194,6 +224,5 @@ a:b/test\x04\0\x0b\x0a\x01\0\x04test\x03\0\0\0G\x09producers\x01\x0cprocessed-by #[inline(never)] #[doc(hidden)] pub fn __link_custom_section_describing_imports() { - wit_bindgen::rt::maybe_link_cabi_realloc(); + wit_bindgen::rt::maybe_link_cabi_realloc(); } - diff --git a/crates/cpp/tests/symmetric_stream_string/format.sh b/crates/cpp/tests/symmetric_stream_string/format.sh index 361520641..e0e74c7c8 100755 --- a/crates/cpp/tests/symmetric_stream_string/format.sh +++ b/crates/cpp/tests/symmetric_stream_string/format.sh @@ -1,3 +1,3 @@ #!/bin/sh -rustfmt bindings/*.rs -rustfmt *.rs +rustfmt --edition=2024 bindings/*.rs +rustfmt --edition=2024 *.rs diff --git a/crates/cpp/tests/symmetric_stream_string/test.rs b/crates/cpp/tests/symmetric_stream_string/test.rs index 4216f3282..8b7b88b9e 100644 --- a/crates/cpp/tests/symmetric_stream_string/test.rs +++ b/crates/cpp/tests/symmetric_stream_string/test.rs @@ -11,7 +11,7 @@ use wit_bindgen::rt::async_support::FutureReader; impl Guest for Component { fn f() -> FutureReader { - let (wr,rd) = wit_future::new(); + let (wr, rd) = wit_future::new(); async_support::spawn(move || async { wr.write(String::from("Hello")).await; }); From 976376d53f2a5c369feefaefc96fe0521280fbdb Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 11:36:35 +0200 Subject: [PATCH 556/672] fully linking future test --- .../bindings/runner.rs | 5 +- .../symmetric_stream_string/bindings/test.rs | 81 +++---------------- .../tests/symmetric_stream_string/build.rs | 9 +++ .../cpp/tests/symmetric_stream_string/test.rs | 6 +- .../rust-client/src/async_support.rs | 18 ++++- 5 files changed, 40 insertions(+), 79 deletions(-) create mode 100644 crates/cpp/tests/symmetric_stream_string/build.rs diff --git a/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs b/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs index f289004b0..781a714b2 100644 --- a/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs +++ b/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs @@ -22,7 +22,10 @@ pub mod a { fn aX3AbX2Fthe_testX00f() -> *mut u8; } let ret = aX3AbX2Fthe_testX00f(); - wit_bindgen_symmetric_rt::async_support::FutureReader::from_handle(ret) + wit_bindgen_symmetric_rt::async_support::FutureReader::from_handle( + ret, + <_rt::String as super::super::super::wit_future::FuturePayload>::lift, + ) } } } diff --git a/crates/cpp/tests/symmetric_stream_string/bindings/test.rs b/crates/cpp/tests/symmetric_stream_string/bindings/test.rs index 86080c397..13e70e371 100644 --- a/crates/cpp/tests/symmetric_stream_string/bindings/test.rs +++ b/crates/cpp/tests/symmetric_stream_string/bindings/test.rs @@ -70,59 +70,17 @@ pub mod wit_future { #![allow(dead_code, unused_variables, clippy::all)] #[doc(hidden)] - pub trait FuturePayload: Unpin + Sized + 'static {} + pub trait FuturePayload: Unpin + Sized + 'static { + unsafe fn lower(value: Self, dst: *mut u8) { + todo!() + } + unsafe fn lift(src: *const u8) -> Self { + todo!() + } + } #[doc(hidden)] #[allow(unused_unsafe)] pub mod vtable0 { - - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn cancel_write(_: u32) -> u32 { - unreachable!() - } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn cancel_read(_: u32) -> u32 { - unreachable!() - } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn close_writable(_: u32) { - unreachable!() - } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn close_readable(_: u32) { - unreachable!() - } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn new() -> u64 { - unreachable!() - } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn start_read(_: u32, _: *mut u8) -> u32 { - unreachable!() - } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn start_write(_: u32, _: *const u8) -> u32 { - unreachable!() - } - - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "[export]a:b/the-test")] - unsafe extern "C" { - #[link_name = "[future-new-0]f"] - fn new() -> u64; - #[link_name = "[future-cancel-write-0]f"] - fn cancel_write(_: u32) -> u32; - #[link_name = "[future-cancel-read-0]f"] - fn cancel_read(_: u32) -> u32; - #[link_name = "[future-close-writable-0]f"] - fn close_writable(_: u32); - #[link_name = "[future-close-readable-0]f"] - fn close_readable(_: u32); - #[link_name = "[async-lower][future-read-0]f"] - fn start_read(_: u32, _: *mut u8) -> u32; - #[link_name = "[async-lower][future-write-0]f"] - fn start_write(_: u32, _: *const u8) -> u32; - } - unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); @@ -153,33 +111,14 @@ pub mod wit_future { } } - pub static VTABLE: wit_bindgen_symmetric_rt::async_support::FutureVtable< - super::super::_rt::String, - > = wit_bindgen_symmetric_rt::async_support::FutureVtable:: { - cancel_write, - cancel_read, - close_writable, - close_readable, - dealloc_lists, - layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(8, 4) }, - lift, - lower, - new, - start_read, - start_write, - }; - - impl super::FuturePayload for super::super::_rt::String { - const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::FutureVtable = - &VTABLE; - } + impl super::FuturePayload for super::super::_rt::String {} } /// Creates a new Component Model `future` with the specified payload type. pub fn new() -> ( wit_bindgen_symmetric_rt::async_support::FutureWriter, wit_bindgen_symmetric_rt::async_support::FutureReader, ) { - wit_bindgen_symmetric_rt::async_support::future_support::new_future() + wit_bindgen_symmetric_rt::async_support::future_support::new_future(T::lower, T::lift) } } diff --git a/crates/cpp/tests/symmetric_stream_string/build.rs b/crates/cpp/tests/symmetric_stream_string/build.rs new file mode 100644 index 000000000..3096c1795 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/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_string/test.rs b/crates/cpp/tests/symmetric_stream_string/test.rs index 8b7b88b9e..a071813f9 100644 --- a/crates/cpp/tests/symmetric_stream_string/test.rs +++ b/crates/cpp/tests/symmetric_stream_string/test.rs @@ -7,12 +7,12 @@ export!(Component); use exports::a::b::the_test::Guest; -use wit_bindgen::rt::async_support::FutureReader; +use wit_bindgen::rt::async_support::{self, FutureReader}; impl Guest for Component { - fn f() -> FutureReader { + fn f() -> FutureReader { let (wr, rd) = wit_future::new(); - async_support::spawn(move || async { + async_support::spawn(async move { wr.write(String::from("Hello")).await; }); rd diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index d5f72236b..04ba69c55 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -1,12 +1,10 @@ use futures::{task::Waker, FutureExt}; use std::{ - future::Future, - pin::Pin, - task::{Context, Poll, RawWaker, RawWakerVTable}, + future::Future, mem::MaybeUninit, pin::Pin, sync::{Arc, RwLock}, task::{Context, Poll, RawWaker, RawWakerVTable} }; use crate::module::symmetric::runtime::symmetric_executor::{ - CallbackState, EventGenerator, EventSubscription, + self, CallbackState, EventGenerator, EventSubscription }; pub use future_support::{FutureReader, FutureWriter}; @@ -155,3 +153,15 @@ pub unsafe fn spawn_unchecked(future: impl Future) { drop(wait_for); } } + +pub fn block_on(future: impl Future + 'static) -> T { + // ugly but might do the trick + let result: Arc>> = Arc::new(RwLock::new(MaybeUninit::uninit())); + let result2 = Arc::clone(&result); + let future2 = async move { + result2.write().unwrap().write(future.await); + }; + unsafe { spawn_unchecked(future2) }; + symmetric_executor::run(); + return unsafe { result.to_owned().write().unwrap().assume_init_read() } +} From 46c3931106c67fe956b21afd404de911bdc72aeb Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 11:43:21 +0200 Subject: [PATCH 557/672] fully working string future test --- .../symmetric_stream_string/bindings/runner.rs | 13 ++++++++++--- .../tests/symmetric_stream_string/bindings/test.rs | 13 ++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs b/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs index 781a714b2..72c9506b1 100644 --- a/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs +++ b/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs @@ -70,7 +70,7 @@ pub mod wit_future { #[allow(unused_unsafe)] pub mod vtable0 { - unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { + unsafe fn lift2(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); @@ -84,7 +84,7 @@ pub mod wit_future { super::super::_rt::string_lift(bytes2) } } - unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { + unsafe fn lower2(value: super::super::_rt::String, ptr: *mut u8) { unsafe { let vec0 = value; let ptr0 = vec0.as_ptr().cast::(); @@ -101,7 +101,14 @@ pub mod wit_future { } } - impl super::FuturePayload for super::super::_rt::String {} + impl super::FuturePayload for super::super::_rt::String { + unsafe fn lower(value: Self, dst: *mut u8) { + lower2(value, dst); + } + unsafe fn lift(src: *const u8) -> Self { + lift2(src.cast_mut()) + } + } } /// Creates a new Component Model `future` with the specified payload type. pub fn new() -> ( diff --git a/crates/cpp/tests/symmetric_stream_string/bindings/test.rs b/crates/cpp/tests/symmetric_stream_string/bindings/test.rs index 13e70e371..1de873056 100644 --- a/crates/cpp/tests/symmetric_stream_string/bindings/test.rs +++ b/crates/cpp/tests/symmetric_stream_string/bindings/test.rs @@ -81,7 +81,7 @@ pub mod wit_future { #[doc(hidden)] #[allow(unused_unsafe)] pub mod vtable0 { - unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { + unsafe fn lift2(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); @@ -93,7 +93,7 @@ pub mod wit_future { string2 } } - unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { + unsafe fn lower2(value: super::super::_rt::String, ptr: *mut u8) { unsafe { let vec0 = (value.into_bytes()).into_boxed_slice(); let ptr0 = vec0.as_ptr().cast::(); @@ -111,7 +111,14 @@ pub mod wit_future { } } - impl super::FuturePayload for super::super::_rt::String {} + impl super::FuturePayload for super::super::_rt::String { + unsafe fn lower(value: Self, dst: *mut u8) { + lower2(value, dst); + } + unsafe fn lift(src: *const u8) -> Self { + lift2(src.cast_mut()) + } + } } /// Creates a new Component Model `future` with the specified payload type. pub fn new() -> ( From 4782f3cc30456905abdcc35612117e077ff03147 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 16:28:55 +0200 Subject: [PATCH 558/672] use vtables like the original --- .../bindings/runner.rs | 31 +++--- .../symmetric_stream_string/bindings/test.rs | 27 +++--- .../rust-client/src/async_support.rs | 14 ++- .../src/async_support/future_support.rs | 96 ++++++++++++------- .../src/async_support/stream_support.rs | 2 + 5 files changed, 102 insertions(+), 68 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs b/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs index 72c9506b1..4d7969cac 100644 --- a/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs +++ b/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs @@ -11,6 +11,8 @@ pub mod a { static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; + use crate::wit_future::vtable0::VTABLE; + use super::super::super::_rt; #[allow(unused_unsafe, clippy::all)] #[allow(async_fn_in_trait)] @@ -24,7 +26,7 @@ pub mod a { let ret = aX3AbX2Fthe_testX00f(); wit_bindgen_symmetric_rt::async_support::FutureReader::from_handle( ret, - <_rt::String as super::super::super::wit_future::FuturePayload>::lift, + <_rt::String as super::super::super::wit_future::FuturePayload>::VTABLE, ) } } @@ -59,18 +61,13 @@ pub mod wit_future { #[doc(hidden)] pub trait FuturePayload: Unpin + Sized + 'static { - unsafe fn lower(value: Self, dst: *mut u8) { - todo!() - } - unsafe fn lift(src: *const u8) -> Self { - todo!() - } + const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable; } #[doc(hidden)] #[allow(unused_unsafe)] pub mod vtable0 { - unsafe fn lift2(ptr: *mut u8) -> super::super::_rt::String { + unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); @@ -84,7 +81,7 @@ pub mod wit_future { super::super::_rt::string_lift(bytes2) } } - unsafe fn lower2(value: super::super::_rt::String, ptr: *mut u8) { + unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { unsafe { let vec0 = value; let ptr0 = vec0.as_ptr().cast::(); @@ -101,13 +98,15 @@ pub mod wit_future { } } + pub static VTABLE: wit_bindgen::rt::async_support::FutureVtable = + wit_bindgen::rt::async_support::FutureVtable:: { + layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(8, 4) }, + lift, + lower, + }; + impl super::FuturePayload for super::super::_rt::String { - unsafe fn lower(value: Self, dst: *mut u8) { - lower2(value, dst); - } - unsafe fn lift(src: *const u8) -> Self { - lift2(src.cast_mut()) - } + const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable = &VTABLE; } } /// Creates a new Component Model `future` with the specified payload type. @@ -115,7 +114,7 @@ pub mod wit_future { wit_bindgen_symmetric_rt::async_support::FutureWriter, wit_bindgen_symmetric_rt::async_support::FutureReader, ) { - wit_bindgen_symmetric_rt::async_support::future_support::new_future(T::lower, T::lift) + wit_bindgen_symmetric_rt::async_support::future_support::new_future(T::VTABLE) } } diff --git a/crates/cpp/tests/symmetric_stream_string/bindings/test.rs b/crates/cpp/tests/symmetric_stream_string/bindings/test.rs index 1de873056..ea9307c79 100644 --- a/crates/cpp/tests/symmetric_stream_string/bindings/test.rs +++ b/crates/cpp/tests/symmetric_stream_string/bindings/test.rs @@ -71,17 +71,12 @@ pub mod wit_future { #[doc(hidden)] pub trait FuturePayload: Unpin + Sized + 'static { - unsafe fn lower(value: Self, dst: *mut u8) { - todo!() - } - unsafe fn lift(src: *const u8) -> Self { - todo!() - } + const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable; } #[doc(hidden)] #[allow(unused_unsafe)] pub mod vtable0 { - unsafe fn lift2(ptr: *mut u8) -> super::super::_rt::String { + unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); @@ -93,7 +88,7 @@ pub mod wit_future { string2 } } - unsafe fn lower2(value: super::super::_rt::String, ptr: *mut u8) { + unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { unsafe { let vec0 = (value.into_bytes()).into_boxed_slice(); let ptr0 = vec0.as_ptr().cast::(); @@ -111,13 +106,15 @@ pub mod wit_future { } } + pub static VTABLE: wit_bindgen::rt::async_support::FutureVtable = + wit_bindgen::rt::async_support::FutureVtable:: { + layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(8, 4) }, + lift, + lower, + }; + impl super::FuturePayload for super::super::_rt::String { - unsafe fn lower(value: Self, dst: *mut u8) { - lower2(value, dst); - } - unsafe fn lift(src: *const u8) -> Self { - lift2(src.cast_mut()) - } + const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable = &VTABLE; } } /// Creates a new Component Model `future` with the specified payload type. @@ -125,7 +122,7 @@ pub mod wit_future { wit_bindgen_symmetric_rt::async_support::FutureWriter, wit_bindgen_symmetric_rt::async_support::FutureReader, ) { - wit_bindgen_symmetric_rt::async_support::future_support::new_future(T::lower, T::lift) + wit_bindgen_symmetric_rt::async_support::future_support::new_future(T::VTABLE) } } diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 04ba69c55..590a3f5ad 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -1,14 +1,18 @@ use futures::{task::Waker, FutureExt}; use std::{ - future::Future, mem::MaybeUninit, pin::Pin, sync::{Arc, RwLock}, task::{Context, Poll, RawWaker, RawWakerVTable} + future::Future, + mem::MaybeUninit, + pin::Pin, + sync::{Arc, RwLock}, + task::{Context, Poll, RawWaker, RawWakerVTable}, }; use crate::module::symmetric::runtime::symmetric_executor::{ - self, CallbackState, EventGenerator, EventSubscription + self, CallbackState, EventGenerator, EventSubscription, }; -pub use future_support::{FutureReader, FutureWriter}; -pub use stream_support::{results, Stream, StreamReader, StreamWriter}; +pub use future_support::{FutureReader, FutureVtable, FutureWriter}; +pub use stream_support::{results, Stream, StreamReader, StreamVtable, StreamWriter}; pub mod future_support; // later make it non-pub @@ -163,5 +167,5 @@ pub fn block_on(future: impl Future + 'static) -> T { }; unsafe { spawn_unchecked(future2) }; symmetric_executor::run(); - return unsafe { result.to_owned().write().unwrap().assume_init_read() } + return unsafe { result.to_owned().write().unwrap().assume_init_read() }; } 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 index 02aafe269..f68e1de3c 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/future_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs @@ -1,6 +1,7 @@ +use core::ptr::{self, NonNull}; use std::{ + alloc::{self, Layout}, future::{Future, IntoFuture}, - marker::PhantomData, mem::MaybeUninit, pin::Pin, task::{Context, Poll}, @@ -12,21 +13,55 @@ use crate::symmetric_stream::{Address, Buffer}; use super::{wait_on, Stream}; +#[doc(hidden)] +pub struct FutureVtable { + pub layout: Layout, + pub lower: unsafe fn(value: T, dst: *mut u8), + pub lift: unsafe fn(dst: *mut u8) -> T, +} + +// stolen from guest-rust/rt/src/lib.rs +pub struct Cleanup { + ptr: NonNull, + layout: Layout, +} + +// Usage of the returned pointer is always unsafe and must abide by these +// conventions, but this structure itself has no inherent reason to not be +// send/sync. +unsafe impl Send for Cleanup {} +unsafe impl Sync for Cleanup {} + +impl Cleanup { + pub fn new(layout: Layout) -> (*mut u8, Option) { + if layout.size() == 0 { + return (ptr::null_mut(), None); + } + let ptr = unsafe { alloc::alloc(layout) }; + let ptr = match NonNull::new(ptr) { + Some(ptr) => ptr, + None => alloc::handle_alloc_error(layout), + }; + (ptr.as_ptr(), Some(Cleanup { ptr, layout })) + } +} + +impl Drop for Cleanup { + fn drop(&mut self) { + unsafe { + alloc::dealloc(self.ptr.as_ptr(), self.layout); + } + } +} + pub struct FutureWriter { handle: Stream, - // future: Option + 'static + Send>>>, - _phantom: PhantomData, - lower: unsafe fn(value: T, dst: *mut u8), + vtable: &'static FutureVtable, } impl FutureWriter { - pub fn new(handle: Stream, lower: unsafe fn(value: T, dst: *mut u8)) -> Self { - Self { - handle, - // future: None, - _phantom: PhantomData, - lower, - } + pub fn new(handle: Stream, vtable: &'static FutureVtable) -> Self { + Self { handle, vtable } } pub fn write(self, data: T) -> CancelableWrite { @@ -54,7 +89,7 @@ impl Future for CancelableWrite { if me.future.is_none() { let handle = me.writer.handle.clone(); let data = me.data.take().unwrap(); - let lower = me.writer.lower; + let lower = me.writer.vtable.lower; me.future = Some(Box::pin(async move { if !handle.is_ready_to_write() { let subsc = handle.write_ready_subscribe(); @@ -79,19 +114,12 @@ pub struct CancelableRead { pub struct FutureReader { handle: Stream, - // future: Option>> + 'static + Send>>>, - _phantom: PhantomData, - lift: unsafe fn(src: *const u8) -> T, + vtable: &'static FutureVtable, } impl FutureReader { - pub fn new(handle: Stream, lift: unsafe fn(src: *const u8) -> T) -> Self { - Self { - handle, - // future: None, - _phantom: PhantomData, - lift, - } + pub fn new(handle: Stream, vtable: &'static FutureVtable) -> Self { + Self { handle, vtable } } pub fn read(self) -> CancelableRead { @@ -101,8 +129,8 @@ impl FutureReader { } } - pub unsafe fn from_handle(handle: *mut u8, lift: unsafe fn(src: *const u8) -> T) -> Self { - Self::new(unsafe { Stream::from_handle(handle as usize) }, lift) + pub unsafe fn from_handle(handle: *mut u8, vtable: &'static FutureVtable) -> Self { + Self::new(unsafe { Stream::from_handle(handle as usize) }, vtable) } pub fn take_handle(&self) -> *mut () { @@ -118,10 +146,11 @@ impl Future for CancelableRead { if me.future.is_none() { let handle = me.reader.handle.clone(); - let lift = me.reader.lift; + let vtable = me.reader.vtable; me.future = Some(Box::pin(async move { - let mut buffer0 = MaybeUninit::::uninit(); - let address = unsafe { Address::from_handle(&mut buffer0 as *mut _ as usize) }; + // sadly there is no easy way to embed this in the future as the size is not accessible at compile time + let (buffer0, cleanup) = Cleanup::new(vtable.layout); + let address = unsafe { Address::from_handle(buffer0 as usize) }; let buffer = Buffer::new(address, 1); handle.start_reading(buffer); let subsc = handle.read_ready_subscribe(); @@ -131,8 +160,12 @@ impl Future for CancelableRead { if let Some(buffer2) = buffer2 { let count = buffer2.get_size(); if count > 0 { - Some(unsafe { (lift)(buffer2.get_address().take_handle() as *const u8) }) + Some(unsafe { + (vtable.lift)(buffer2.get_address().take_handle() as *mut u8) + }) } else { + // make sure it lives long enough + drop(cleanup); None } } else { @@ -174,13 +207,12 @@ impl IntoFuture for FutureReader { } pub fn new_future( - lower: unsafe fn(value: T, dst: *mut u8), - lift: unsafe fn(src: *const u8) -> T, + vtable: &'static FutureVtable, ) -> (FutureWriter, FutureReader) { let handle = Stream::new(); let handle2 = handle.clone(); ( - FutureWriter::new(handle, lower), - FutureReader::new(handle2, lift), + FutureWriter::new(handle, vtable), + FutureReader::new(handle2, vtable), ) } 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 index a4c15d786..82e1ee782 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -17,6 +17,8 @@ use { }, }; +pub use super::future_support::FutureVtable as StreamVtable; + fn ceiling(x: usize, y: usize) -> usize { (x / y) + if x % y == 0 { 0 } else { 1 } } From df4220d3ac573e8b0c8b931e2e639f404d8a581d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 16:45:33 +0200 Subject: [PATCH 559/672] adapt to new layout --- .../future/src/future_world.rs | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/crates/cpp/tests/symmetric_future/future/src/future_world.rs b/crates/cpp/tests/symmetric_future/future/src/future_world.rs index 7465f66ae..b4a38afdc 100644 --- a/crates/cpp/tests/symmetric_future/future/src/future_world.rs +++ b/crates/cpp/tests/symmetric_future/future/src/future_world.rs @@ -24,7 +24,7 @@ pub mod test { let ret = testX3AtestX2Ffuture_sourceX00create(); wit_bindgen_symmetric_rt::async_support::FutureReader::new( wit_bindgen_symmetric_rt::async_support::Stream::from_handle(ret), - ::lift + ::VTABLE, ) } } @@ -158,15 +158,33 @@ pub mod wit_future { #[doc(hidden)] pub trait FuturePayload: Unpin + Sized + 'static { - unsafe fn lower(value: Self, dst: *mut u8) { todo!() } - unsafe fn lift(src: *const u8) -> Self { todo!() } + const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::FutureVtable; } - impl FuturePayload for u32 { - unsafe fn lower(value: Self, dst: *mut u8) { - *dst.cast() = value; + #[doc(hidden)] + #[allow(unused_unsafe)] + pub mod vtable0 { + unsafe fn lift(ptr: *mut u8) -> u32 { + unsafe { + let l0 = *ptr.add(0).cast::(); + + l0 as u32 + } + } + unsafe fn lower(value: u32, ptr: *mut u8) { + unsafe { + *ptr.add(0).cast::() = super::super::_rt::as_i32(value); + } } - unsafe fn lift(src: *const u8) -> Self { - *src.cast() + pub static VTABLE: wit_bindgen_symmetric_rt::async_support::FutureVtable = + wit_bindgen_symmetric_rt::async_support::FutureVtable:: { + layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(4, 4) }, + lift, + lower, + }; + + impl super::FuturePayload for u32 { + const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::FutureVtable = + &VTABLE; } } /// Creates a new Component Model `future` with the specified payload type. @@ -174,7 +192,7 @@ pub mod wit_future { wit_bindgen_symmetric_rt::async_support::FutureWriter, wit_bindgen_symmetric_rt::async_support::FutureReader, ) { - wit_bindgen_symmetric_rt::async_support::future_support::new_future(T::lower, T::lift) + wit_bindgen_symmetric_rt::async_support::future_support::new_future(T::VTABLE) } } From 19fa632711348385f1a0e4569b962567dc533b9b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 16:48:59 +0200 Subject: [PATCH 560/672] rename to proper name --- .../Cargo.lock | 0 .../Cargo.toml | 0 .../bindings/runner.rs | 0 .../bindings/test.rs | 0 .../{symmetric_stream_string => symmetric_future_string}/build.rs | 0 .../format.sh | 0 .../generate.sh | 0 .../runner.rs | 0 .../src/main.rs | 0 .../src/test.rs | 0 .../{symmetric_stream_string => symmetric_future_string}/test.rs | 0 .../{symmetric_stream_string => symmetric_future_string}/test.wit | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename crates/cpp/tests/{symmetric_stream_string => symmetric_future_string}/Cargo.lock (100%) rename crates/cpp/tests/{symmetric_stream_string => symmetric_future_string}/Cargo.toml (100%) rename crates/cpp/tests/{symmetric_stream_string => symmetric_future_string}/bindings/runner.rs (100%) rename crates/cpp/tests/{symmetric_stream_string => symmetric_future_string}/bindings/test.rs (100%) rename crates/cpp/tests/{symmetric_stream_string => symmetric_future_string}/build.rs (100%) rename crates/cpp/tests/{symmetric_stream_string => symmetric_future_string}/format.sh (100%) rename crates/cpp/tests/{symmetric_stream_string => symmetric_future_string}/generate.sh (100%) rename crates/cpp/tests/{symmetric_stream_string => symmetric_future_string}/runner.rs (100%) rename crates/cpp/tests/{symmetric_stream_string => symmetric_future_string}/src/main.rs (100%) rename crates/cpp/tests/{symmetric_stream_string => symmetric_future_string}/src/test.rs (100%) rename crates/cpp/tests/{symmetric_stream_string => symmetric_future_string}/test.rs (100%) rename crates/cpp/tests/{symmetric_stream_string => symmetric_future_string}/test.wit (100%) diff --git a/crates/cpp/tests/symmetric_stream_string/Cargo.lock b/crates/cpp/tests/symmetric_future_string/Cargo.lock similarity index 100% rename from crates/cpp/tests/symmetric_stream_string/Cargo.lock rename to crates/cpp/tests/symmetric_future_string/Cargo.lock diff --git a/crates/cpp/tests/symmetric_stream_string/Cargo.toml b/crates/cpp/tests/symmetric_future_string/Cargo.toml similarity index 100% rename from crates/cpp/tests/symmetric_stream_string/Cargo.toml rename to crates/cpp/tests/symmetric_future_string/Cargo.toml diff --git a/crates/cpp/tests/symmetric_stream_string/bindings/runner.rs b/crates/cpp/tests/symmetric_future_string/bindings/runner.rs similarity index 100% rename from crates/cpp/tests/symmetric_stream_string/bindings/runner.rs rename to crates/cpp/tests/symmetric_future_string/bindings/runner.rs diff --git a/crates/cpp/tests/symmetric_stream_string/bindings/test.rs b/crates/cpp/tests/symmetric_future_string/bindings/test.rs similarity index 100% rename from crates/cpp/tests/symmetric_stream_string/bindings/test.rs rename to crates/cpp/tests/symmetric_future_string/bindings/test.rs diff --git a/crates/cpp/tests/symmetric_stream_string/build.rs b/crates/cpp/tests/symmetric_future_string/build.rs similarity index 100% rename from crates/cpp/tests/symmetric_stream_string/build.rs rename to crates/cpp/tests/symmetric_future_string/build.rs diff --git a/crates/cpp/tests/symmetric_stream_string/format.sh b/crates/cpp/tests/symmetric_future_string/format.sh similarity index 100% rename from crates/cpp/tests/symmetric_stream_string/format.sh rename to crates/cpp/tests/symmetric_future_string/format.sh diff --git a/crates/cpp/tests/symmetric_stream_string/generate.sh b/crates/cpp/tests/symmetric_future_string/generate.sh similarity index 100% rename from crates/cpp/tests/symmetric_stream_string/generate.sh rename to crates/cpp/tests/symmetric_future_string/generate.sh diff --git a/crates/cpp/tests/symmetric_stream_string/runner.rs b/crates/cpp/tests/symmetric_future_string/runner.rs similarity index 100% rename from crates/cpp/tests/symmetric_stream_string/runner.rs rename to crates/cpp/tests/symmetric_future_string/runner.rs diff --git a/crates/cpp/tests/symmetric_stream_string/src/main.rs b/crates/cpp/tests/symmetric_future_string/src/main.rs similarity index 100% rename from crates/cpp/tests/symmetric_stream_string/src/main.rs rename to crates/cpp/tests/symmetric_future_string/src/main.rs diff --git a/crates/cpp/tests/symmetric_stream_string/src/test.rs b/crates/cpp/tests/symmetric_future_string/src/test.rs similarity index 100% rename from crates/cpp/tests/symmetric_stream_string/src/test.rs rename to crates/cpp/tests/symmetric_future_string/src/test.rs diff --git a/crates/cpp/tests/symmetric_stream_string/test.rs b/crates/cpp/tests/symmetric_future_string/test.rs similarity index 100% rename from crates/cpp/tests/symmetric_stream_string/test.rs rename to crates/cpp/tests/symmetric_future_string/test.rs diff --git a/crates/cpp/tests/symmetric_stream_string/test.wit b/crates/cpp/tests/symmetric_future_string/test.wit similarity index 100% rename from crates/cpp/tests/symmetric_stream_string/test.wit rename to crates/cpp/tests/symmetric_future_string/test.wit From 09721dfe5b1704c596b53a2aa6b4b396fe7c2bf2 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 18:42:55 +0200 Subject: [PATCH 561/672] stream of strings --- .../tests/symmetric_stream_string/Cargo.lock | 195 ++++++++++++++++++ .../tests/symmetric_stream_string/Cargo.toml | 14 ++ .../tests/symmetric_stream_string/build.rs | 14 ++ .../tests/symmetric_stream_string/format.sh | 3 + .../tests/symmetric_stream_string/generate.sh | 7 + .../tests/symmetric_stream_string/src/main.rs | 1 + .../symmetric_stream_string/src/runner.rs | 159 ++++++++++++++ .../symmetric_stream_string/test/Cargo.toml | 8 + .../symmetric_stream_string/test/build.rs | 9 + .../symmetric_stream_string/test/src/lib.rs | 1 + .../symmetric_stream_string/test/src/test.rs | 158 ++++++++++++++ .../async/future-string/runner.rs | 2 +- .../runtime-async/async/future-string/test.rs | 8 +- .../async/stream-string/runner.rs | 25 +++ .../runtime-async/async/stream-string/test.rs | 23 +++ .../async/stream-string/test.wit | 12 ++ 16 files changed, 634 insertions(+), 5 deletions(-) create mode 100644 crates/cpp/tests/symmetric_stream_string/Cargo.lock create mode 100644 crates/cpp/tests/symmetric_stream_string/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_stream_string/build.rs create mode 100755 crates/cpp/tests/symmetric_stream_string/format.sh create mode 100755 crates/cpp/tests/symmetric_stream_string/generate.sh create mode 120000 crates/cpp/tests/symmetric_stream_string/src/main.rs create mode 100644 crates/cpp/tests/symmetric_stream_string/src/runner.rs create mode 100644 crates/cpp/tests/symmetric_stream_string/test/Cargo.toml create mode 100644 crates/cpp/tests/symmetric_stream_string/test/build.rs create mode 120000 crates/cpp/tests/symmetric_stream_string/test/src/lib.rs create mode 100644 crates/cpp/tests/symmetric_stream_string/test/src/test.rs create mode 100644 tests/runtime-async/async/stream-string/runner.rs create mode 100644 tests/runtime-async/async/stream-string/test.rs create mode 100644 tests/runtime-async/async/stream-string/test.wit diff --git a/crates/cpp/tests/symmetric_stream_string/Cargo.lock b/crates/cpp/tests/symmetric_stream_string/Cargo.lock new file mode 100644 index 000000000..bd45ae91d --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/Cargo.lock @@ -0,0 +1,195 @@ +# 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 = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mini-bindgen" +version = "0.1.0" +dependencies = [ + "dummy-rt", + "wit-bindgen-symmetric-rt", +] + +[[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.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +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_stream_string" +version = "0.1.0" +dependencies = [ + "test", +] + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "test" +version = "0.1.0" +dependencies = [ + "mini-bindgen", + "wit-bindgen-symmetric-rt", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "wit-bindgen-symmetric-rt" +version = "0.36.0" +dependencies = [ + "dummy-rt", + "futures", +] diff --git a/crates/cpp/tests/symmetric_stream_string/Cargo.toml b/crates/cpp/tests/symmetric_stream_string/Cargo.toml new file mode 100644 index 000000000..feb2bdb46 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/Cargo.toml @@ -0,0 +1,14 @@ +[workspace] +members = ["test"] + +[package] +name = "symmetric_stream_string" +version = "0.1.0" +edition = "2024" + +[dependencies] +test = { path = "test" } +symmetric_executor = { path = "../../../symmetric_executor", features = ["trace"]} +symmetric_stream = { path = "../../../symmetric_executor/symmetric_stream" } +wit-bindgen-symmetric-rt = { path = "../../../symmetric_executor/rust-client" } +wit-bindgen = { path = "../../../symmetric_executor/dummy-bindgen", package = "mini-bindgen" } diff --git a/crates/cpp/tests/symmetric_stream_string/build.rs b/crates/cpp/tests/symmetric_stream_string/build.rs new file mode 100644 index 000000000..870a9b45f --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/build.rs @@ -0,0 +1,14 @@ +use std::env; + +fn main() { + let out = env::var_os("OUT_DIR").unwrap(); + println!( + r"cargo:rustc-link-search={}/../../../deps", + out.into_string().unwrap() + ); + let manifest = env::var_os("CARGO_MANIFEST_DIR").unwrap(); + println!( + r"cargo:rustc-env=BINDINGS={}/src/runner.rs", + manifest.into_string().unwrap() + ); +} diff --git a/crates/cpp/tests/symmetric_stream_string/format.sh b/crates/cpp/tests/symmetric_stream_string/format.sh new file mode 100755 index 000000000..4ca03aab6 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/format.sh @@ -0,0 +1,3 @@ +#!/bin/sh +rustfmt --edition=2024 test/src/*.rs +rustfmt --edition=2024 src/*.rs diff --git a/crates/cpp/tests/symmetric_stream_string/generate.sh b/crates/cpp/tests/symmetric_stream_string/generate.sh new file mode 100755 index 000000000..879f4d5f8 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/generate.sh @@ -0,0 +1,7 @@ +#!/bin/sh +cd src +export TOPLEVEL=../../../../.. +${TOPLEVEL}/target/debug/wit-bindgen rust ${TOPLEVEL}/tests/runtime-async/async/stream-string/test.wit --symmetric -w runner +cd ../test/src +export TOPLEVEL=../../../../../.. +${TOPLEVEL}/target/debug/wit-bindgen rust ${TOPLEVEL}/tests/runtime-async/async/stream-string/test.wit --symmetric -w test diff --git a/crates/cpp/tests/symmetric_stream_string/src/main.rs b/crates/cpp/tests/symmetric_stream_string/src/main.rs new file mode 120000 index 000000000..de4297e7e --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/src/main.rs @@ -0,0 +1 @@ +../../../../../tests/runtime-async/async/stream-string/runner.rs \ No newline at end of file diff --git a/crates/cpp/tests/symmetric_stream_string/src/runner.rs b/crates/cpp/tests/symmetric_stream_string/src/runner.rs new file mode 100644 index 000000000..7466e2f39 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/src/runner.rs @@ -0,0 +1,159 @@ +// Generated by `wit-bindgen` 0.41.0. DO NOT EDIT! +// Options used: +#[allow(dead_code, clippy::all)] +pub mod a { + pub mod b { + + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod the_test { + #[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)] + #[allow(async_fn_in_trait)] + pub fn f() -> wit_bindgen_symmetric_rt::async_support::StreamReader<_rt::String>{ + unsafe { + + #[link(wasm_import_module = "a:b/the-test")] + unsafe extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "f")] + fn aX3AbX2Fthe_testX00f() -> *mut u8; + } + let ret = aX3AbX2Fthe_testX00f(); + wit_bindgen_symmetric_rt::async_support::StreamReader::from_handle(ret) + } + } + + } + + } +} +mod _rt { + #![allow(dead_code, clippy::all)] + pub use alloc_crate::string::String; + pub use alloc_crate::vec::Vec; + pub unsafe fn string_lift(bytes: Vec) -> String { + if cfg!(debug_assertions) { + String::from_utf8(bytes).unwrap() + } else { + unsafe { String::from_utf8_unchecked(bytes) } + } + } + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + unsafe { + 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; +} +pub mod wit_stream { + #![allow(dead_code, unused_variables, clippy::all)] + + pub trait StreamPayload: Unpin + Sized + 'static {} + #[doc(hidden)] + #[allow(unused_unsafe)] + pub mod vtable0 { + + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn cancel_write(_: u32) -> u32 { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn cancel_read(_: u32) -> u32 { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn close_writable(_: u32) { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn close_readable(_: u32) { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn new() -> u64 { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn start_read(_: u32, _: *mut u8, _: usize) -> u32 { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn start_write(_: u32, _: *const u8, _: usize) -> u32 { unreachable!() } + + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "a:b/the-test")] + unsafe extern "C" { + #[link_name = "[stream-new-0]f"] + fn new() -> u64; + #[link_name = "[stream-cancel-write-0]f"] + fn cancel_write(_: u32) -> u32; + #[link_name = "[stream-cancel-read-0]f"] + fn cancel_read(_: u32) -> u32; + #[link_name = "[stream-close-writable-0]f"] + fn close_writable(_: u32); + #[link_name = "[stream-close-readable-0]f"] + fn close_readable(_: u32); + #[link_name = "[async-lower][stream-read-0]f"] + fn start_read(_: u32, _: *mut u8, _: usize) -> u32; + #[link_name = "[async-lower][stream-write-0]f"] + fn start_write(_: u32, _: *const u8, _: usize) -> u32; + } + + unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + let len2 = l1; + let bytes2 = if len2>0 { + super::super::_rt::Vec::from_raw_parts(l0.cast(), len2, len2) + } else { Default::default() }; + + super::super::_rt::string_lift(bytes2) } } + unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { unsafe { let vec0 = value; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + *ptr.add(::core::mem::size_of::<*const u8>()).cast::() = len0; + *ptr.add(0).cast::<*mut u8>() = ptr0.cast_mut(); + } } + unsafe fn dealloc_lists(ptr: *mut u8) { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + super::super::_rt::cabi_dealloc(l0, l1, 1); +} } + +pub static VTABLE: wit_bindgen_symmetric_rt::async_support::StreamVtable = wit_bindgen_symmetric_rt::async_support::StreamVtable:: { + cancel_write, + cancel_read, + close_writable, + close_readable, + dealloc_lists: Some(dealloc_lists), + layout: unsafe { + ::std::alloc::Layout::from_size_align_unchecked(8, 4) + }, + lift: Some(lift), + lower: Some(lower), + new, + start_read, + start_write, +}; + +impl super::StreamPayload for super::super::_rt::String { + const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::StreamVtable = &VTABLE; +} +} +/// Creates a new Component Model `stream` with the specified payload type. +pub fn new() -> (wit_bindgen_symmetric_rt::async_support::StreamWriter, wit_bindgen_symmetric_rt::async_support::StreamReader) { + wit_bindgen_symmetric_rt::async_support::stream_support::new_stream() +} +} + +#[cfg(target_arch = "wasm32")] +#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:a:b:runner:encoded world")] +#[doc(hidden)] +#[allow(clippy::octal_escapes)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 180] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x078\x01A\x02\x01A\x02\x01\ +B\x03\x01f\x01s\x01@\0\0\0\x04\0\x01f\x01\x01\x03\0\x0ca:b/the-test\x05\0\x04\0\x0a\ +a:b/runner\x04\0\x0b\x0c\x01\0\x06runner\x03\0\0\0G\x09producers\x01\x0cprocesse\ +d-by\x02\x0dwit-component\x070.229.0\x10wit-bindgen-rust\x060.41.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_string/test/Cargo.toml b/crates/cpp/tests/symmetric_stream_string/test/Cargo.toml new file mode 100644 index 000000000..a91624c3f --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/test/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "test" +version = "0.1.0" +edition = "2021" + +[dependencies] +wit-bindgen-symmetric-rt = { path = "../../../../symmetric_executor/rust-client" } +wit-bindgen = { path = "../../../../symmetric_executor/dummy-bindgen", package = "mini-bindgen" } diff --git a/crates/cpp/tests/symmetric_stream_string/test/build.rs b/crates/cpp/tests/symmetric_stream_string/test/build.rs new file mode 100644 index 000000000..6946faa2a --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/test/build.rs @@ -0,0 +1,9 @@ +use std::env; + +fn main() { + let manifest = env::var_os("CARGO_MANIFEST_DIR").unwrap(); + println!( + r"cargo:rustc-env=BINDINGS={}/src/test.rs", + manifest.into_string().unwrap() + ); +} diff --git a/crates/cpp/tests/symmetric_stream_string/test/src/lib.rs b/crates/cpp/tests/symmetric_stream_string/test/src/lib.rs new file mode 120000 index 000000000..ff0ca2a84 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/test/src/lib.rs @@ -0,0 +1 @@ +../../../../../../tests/runtime-async/async/stream-string/test.rs \ No newline at end of file diff --git a/crates/cpp/tests/symmetric_stream_string/test/src/test.rs b/crates/cpp/tests/symmetric_stream_string/test/src/test.rs new file mode 100644 index 000000000..af185dab8 --- /dev/null +++ b/crates/cpp/tests/symmetric_stream_string/test/src/test.rs @@ -0,0 +1,158 @@ +// Generated by `wit-bindgen` 0.41.0. DO NOT EDIT! +// Options used: +#[allow(dead_code, clippy::all)] +pub mod exports { + pub mod a { + pub mod b { + + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod the_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, unused_unsafe)] + pub unsafe fn _export_f_cabi() -> *mut u8 { unsafe {#[cfg(target_arch="wasm32")] + _rt::run_ctors_once();let result0 = { + T::f() + }; + (result0).take_handle() as *mut u8 + } } + pub trait Guest { + #[allow(async_fn_in_trait)] + fn f() -> wit_bindgen_symmetric_rt::async_support::StreamReader<_rt::String>; + } + #[doc(hidden)] + + macro_rules! __export_a_b_the_test_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 aX3AbX2Fthe_testX00f() -> *mut u8 { + unsafe { $($path_to_types)*::_export_f_cabi::<$ty>() } + } + };); + } + #[doc(hidden)] + pub(crate) use __export_a_b_the_test_cabi; + + } + + } +} +} +mod _rt { + #![allow(dead_code, clippy::all)] + pub use alloc_crate::string::String; + pub use alloc_crate::vec::Vec; + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + unsafe { + let layout = alloc::Layout::from_size_align_unchecked(size, align); + alloc::dealloc(ptr, layout); + } + } + + #[cfg(target_arch = "wasm32")] + pub fn run_ctors_once() { + wit_bindgen::rt::run_ctors_once(); + } + extern crate alloc as alloc_crate; + pub use alloc_crate::alloc; +} +pub mod wit_stream { + #![allow(dead_code, unused_variables, clippy::all)] + + pub trait StreamPayload: Unpin + Sized + 'static { + const VTABLE: &'static wit_bindgen::rt::async_support::StreamVtable; + } + #[doc(hidden)] + #[allow(unused_unsafe)] + pub mod vtable0 { + + unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + let len2 = l1; + let string2 = String::from(std::str::from_utf8(std::slice::from_raw_parts(l0, len2)).unwrap()); + + string2 } } + unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { unsafe { let vec0 = (value.into_bytes()).into_boxed_slice(); + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + ::core::mem::forget(vec0); + *ptr.add(::core::mem::size_of::<*const u8>()).cast::() = len0; + *ptr.add(0).cast::<*mut u8>() = ptr0.cast_mut(); + } } + unsafe fn dealloc_lists(ptr: *mut u8) { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + super::super::_rt::cabi_dealloc(l0, l1, 1); +} } + +pub static VTABLE: wit_bindgen_symmetric_rt::async_support::StreamVtable = wit_bindgen_symmetric_rt::async_support::StreamVtable:: { + layout: unsafe { + ::std::alloc::Layout::from_size_align_unchecked(8, 4) + }, + lift: Some(lift), + lower: Some(lower), +}; + +impl super::StreamPayload for super::super::_rt::String { + const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::StreamVtable = &VTABLE; +} +} +/// Creates a new Component Model `stream` with the specified payload type. +pub fn new() -> (wit_bindgen_symmetric_rt::async_support::StreamWriter, wit_bindgen_symmetric_rt::async_support::StreamReader) { + wit_bindgen_symmetric_rt::async_support::stream_support::new_stream(T::VTABLE) +} +} + +/// 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_test_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::a::b::the_test::__export_a_b_the_test_cabi!($ty with_types_in $($path_to_types_root)*::exports::a::b::the_test); + ) +} +#[doc(inline)] +pub(crate) use __export_test_impl as export; + +#[cfg(target_arch = "wasm32")] +#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:a:b:test:encoded world")] +#[doc(hidden)] +#[allow(clippy::octal_escapes)] +pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 176] = *b"\ +\0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x076\x01A\x02\x01A\x02\x01\ +B\x03\x01f\x01s\x01@\0\0\0\x04\0\x01f\x01\x01\x04\0\x0ca:b/the-test\x05\0\x04\0\x08\ +a:b/test\x04\0\x0b\x0a\x01\0\x04test\x03\0\0\0G\x09producers\x01\x0cprocessed-by\ +\x02\x0dwit-component\x070.229.0\x10wit-bindgen-rust\x060.41.0"; + +#[inline(never)] +#[doc(hidden)] +pub fn __link_custom_section_describing_imports() { + wit_bindgen::rt::maybe_link_cabi_realloc(); +} + diff --git a/tests/runtime-async/async/future-string/runner.rs b/tests/runtime-async/async/future-string/runner.rs index fa7f23b12..79cf33604 100644 --- a/tests/runtime-async/async/future-string/runner.rs +++ b/tests/runtime-async/async/future-string/runner.rs @@ -9,6 +9,6 @@ use crate::a::b::the_test::f; fn main() { async_support::block_on(async { let result = f().await; - assert_eq!(result, String::from("Hello")); + assert_eq!(result, Some(String::from("Hello"))); }); } diff --git a/tests/runtime-async/async/future-string/test.rs b/tests/runtime-async/async/future-string/test.rs index bcdb8cd02..ba91bf505 100644 --- a/tests/runtime-async/async/future-string/test.rs +++ b/tests/runtime-async/async/future-string/test.rs @@ -6,12 +6,12 @@ export!(Component); use crate::exports::a::b::the_test::Guest; -use wit_bindgen::rt::async_support::FutureReader; +use wit_bindgen::rt::async_support::{self, FutureReader}; impl Guest for Component { - fn f() -> wit::FutureReader { - let (wr,rd) = wit_future::new(); - async_support::spawn(move || async { + fn f() -> FutureReader { + let (wr, rd) = wit_future::new(); + async_support::spawn(async move { wr.write(String::from("Hello")).await; }); rd diff --git a/tests/runtime-async/async/stream-string/runner.rs b/tests/runtime-async/async/stream-string/runner.rs new file mode 100644 index 000000000..f4e1fbf1d --- /dev/null +++ b/tests/runtime-async/async/stream-string/runner.rs @@ -0,0 +1,25 @@ +//@ args = '--async=-none' + +include!(env!("BINDINGS")); + +use wit_bindgen::rt::async_support; + +use crate::a::b::the_test::f; + +fn main() { + async_support::block_on(async { + let stream = f(); + let result = stream.next().await; + assert_eq!(result, Some(String::from("Hello"))); + let result = stream.next().await; + assert_eq!(result, Some(String::from("World!"))); + let result = stream.next().await; + assert_eq!(result, Some(String::from("From"))); + let result = stream.next().await; + assert_eq!(result, Some(String::from("a"))); + let result = stream.next().await; + assert_eq!(result, Some(String::from("stream."))); + let result = stream.next().await; + assert_eq!(result, None); + }); +} diff --git a/tests/runtime-async/async/stream-string/test.rs b/tests/runtime-async/async/stream-string/test.rs new file mode 100644 index 000000000..a01dcd95d --- /dev/null +++ b/tests/runtime-async/async/stream-string/test.rs @@ -0,0 +1,23 @@ +include!(env!("BINDINGS")); + +struct Component; + +export!(Component); + +use crate::exports::a::b::the_test::Guest; + +use wit_bindgen::rt::async_support::{self, StreamReader}; + +impl Guest for Component { + fn f() -> StreamReader { + let (wr, rd) = wit_future::new(); + async_support::spawn(async move { + wr.write(String::from("Hello")).await; + wr.write(String::from("World!")).await; + wr.write(String::from("From")).await; + wr.write(String::from("a")).await; + wr.write(String::from("stream.")).await; + }); + rd + } +} diff --git a/tests/runtime-async/async/stream-string/test.wit b/tests/runtime-async/async/stream-string/test.wit new file mode 100644 index 000000000..bf3ab480b --- /dev/null +++ b/tests/runtime-async/async/stream-string/test.wit @@ -0,0 +1,12 @@ +package a:b; + +interface the-test { + f: func() -> stream; +} + +world test { + export the-test; +} +world runner { + import the-test; +} From 9defe398841c5f6bf7c4c7dc6cd1412c3a479e97 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 18:43:30 +0200 Subject: [PATCH 562/672] fmt --- .../symmetric_stream_string/src/runner.rs | 272 ++++++++++-------- .../symmetric_stream_string/test/src/test.rs | 189 ++++++------ 2 files changed, 251 insertions(+), 210 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream_string/src/runner.rs b/crates/cpp/tests/symmetric_stream_string/src/runner.rs index 7466e2f39..2607d10ab 100644 --- a/crates/cpp/tests/symmetric_stream_string/src/runner.rs +++ b/crates/cpp/tests/symmetric_stream_string/src/runner.rs @@ -2,143 +2,170 @@ // Options used: #[allow(dead_code, clippy::all)] pub mod a { - pub mod b { + pub mod b { - #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] - pub mod the_test { - #[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)] - #[allow(async_fn_in_trait)] - pub fn f() -> wit_bindgen_symmetric_rt::async_support::StreamReader<_rt::String>{ - unsafe { - - #[link(wasm_import_module = "a:b/the-test")] - unsafe extern "C" { - #[cfg_attr(target_arch = "wasm32", link_name = "f")] - fn aX3AbX2Fthe_testX00f() -> *mut u8; - } - let ret = aX3AbX2Fthe_testX00f(); - wit_bindgen_symmetric_rt::async_support::StreamReader::from_handle(ret) - } - } + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod the_test { + #[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)] + #[allow(async_fn_in_trait)] + pub fn f() -> wit_bindgen_symmetric_rt::async_support::StreamReader<_rt::String> { + unsafe { + #[link(wasm_import_module = "a:b/the-test")] + unsafe extern "C" { + #[cfg_attr(target_arch = "wasm32", link_name = "f")] + fn aX3AbX2Fthe_testX00f() -> *mut u8; + } + let ret = aX3AbX2Fthe_testX00f(); + wit_bindgen_symmetric_rt::async_support::StreamReader::from_handle(ret) + } + } + } } - - } } mod _rt { - #![allow(dead_code, clippy::all)] - pub use alloc_crate::string::String; - pub use alloc_crate::vec::Vec; - pub unsafe fn string_lift(bytes: Vec) -> String { - if cfg!(debug_assertions) { - String::from_utf8(bytes).unwrap() - } else { - unsafe { String::from_utf8_unchecked(bytes) } - } - } - pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { - if size == 0 { - return; + #![allow(dead_code, clippy::all)] + pub use alloc_crate::string::String; + pub use alloc_crate::vec::Vec; + pub unsafe fn string_lift(bytes: Vec) -> String { + if cfg!(debug_assertions) { + String::from_utf8(bytes).unwrap() + } else { + unsafe { String::from_utf8_unchecked(bytes) } + } } - unsafe { - let layout = alloc::Layout::from_size_align_unchecked(size, align); - alloc::dealloc(ptr, layout); + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + unsafe { + 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; + extern crate alloc as alloc_crate; + pub use alloc_crate::alloc; } pub mod wit_stream { - #![allow(dead_code, unused_variables, clippy::all)] + #![allow(dead_code, unused_variables, clippy::all)] - pub trait StreamPayload: Unpin + Sized + 'static {} - #[doc(hidden)] - #[allow(unused_unsafe)] - pub mod vtable0 { + pub trait StreamPayload: Unpin + Sized + 'static {} + #[doc(hidden)] + #[allow(unused_unsafe)] + pub mod vtable0 { - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn cancel_write(_: u32) -> u32 { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn cancel_read(_: u32) -> u32 { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn close_writable(_: u32) { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn close_readable(_: u32) { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn new() -> u64 { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn start_read(_: u32, _: *mut u8, _: usize) -> u32 { unreachable!() } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn start_write(_: u32, _: *const u8, _: usize) -> u32 { unreachable!() } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn cancel_write(_: u32) -> u32 { + unreachable!() + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn cancel_read(_: u32) -> u32 { + unreachable!() + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn close_writable(_: u32) { + unreachable!() + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn close_readable(_: u32) { + unreachable!() + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn new() -> u64 { + unreachable!() + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn start_read(_: u32, _: *mut u8, _: usize) -> u32 { + unreachable!() + } + #[cfg(not(target_arch = "wasm32"))] + unsafe extern "C" fn start_write(_: u32, _: *const u8, _: usize) -> u32 { + unreachable!() + } - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "a:b/the-test")] - unsafe extern "C" { - #[link_name = "[stream-new-0]f"] - fn new() -> u64; - #[link_name = "[stream-cancel-write-0]f"] - fn cancel_write(_: u32) -> u32; - #[link_name = "[stream-cancel-read-0]f"] - fn cancel_read(_: u32) -> u32; - #[link_name = "[stream-close-writable-0]f"] - fn close_writable(_: u32); - #[link_name = "[stream-close-readable-0]f"] - fn close_readable(_: u32); - #[link_name = "[async-lower][stream-read-0]f"] - fn start_read(_: u32, _: *mut u8, _: usize) -> u32; - #[link_name = "[async-lower][stream-write-0]f"] - fn start_write(_: u32, _: *const u8, _: usize) -> u32; - } + #[cfg(target_arch = "wasm32")] + #[link(wasm_import_module = "a:b/the-test")] + unsafe extern "C" { + #[link_name = "[stream-new-0]f"] + fn new() -> u64; + #[link_name = "[stream-cancel-write-0]f"] + fn cancel_write(_: u32) -> u32; + #[link_name = "[stream-cancel-read-0]f"] + fn cancel_read(_: u32) -> u32; + #[link_name = "[stream-close-writable-0]f"] + fn close_writable(_: u32); + #[link_name = "[stream-close-readable-0]f"] + fn close_readable(_: u32); + #[link_name = "[async-lower][stream-read-0]f"] + fn start_read(_: u32, _: *mut u8, _: usize) -> u32; + #[link_name = "[async-lower][stream-write-0]f"] + fn start_write(_: u32, _: *const u8, _: usize) -> u32; + } - unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); - let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); - let len2 = l1; - let bytes2 = if len2>0 { - super::super::_rt::Vec::from_raw_parts(l0.cast(), len2, len2) - } else { Default::default() }; + unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { + unsafe { + let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + let len2 = l1; + let bytes2 = if len2 > 0 { + super::super::_rt::Vec::from_raw_parts(l0.cast(), len2, len2) + } else { + Default::default() + }; - super::super::_rt::string_lift(bytes2) } } - unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { unsafe { let vec0 = value; - let ptr0 = vec0.as_ptr().cast::(); - let len0 = vec0.len(); - *ptr.add(::core::mem::size_of::<*const u8>()).cast::() = len0; - *ptr.add(0).cast::<*mut u8>() = ptr0.cast_mut(); - } } - unsafe fn dealloc_lists(ptr: *mut u8) { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); - let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); - super::super::_rt::cabi_dealloc(l0, l1, 1); -} } + super::super::_rt::string_lift(bytes2) + } + } + unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { + unsafe { + let vec0 = value; + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + *ptr.add(::core::mem::size_of::<*const u8>()).cast::() = len0; + *ptr.add(0).cast::<*mut u8>() = ptr0.cast_mut(); + } + } + unsafe fn dealloc_lists(ptr: *mut u8) { + unsafe { + let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + super::super::_rt::cabi_dealloc(l0, l1, 1); + } + } -pub static VTABLE: wit_bindgen_symmetric_rt::async_support::StreamVtable = wit_bindgen_symmetric_rt::async_support::StreamVtable:: { - cancel_write, - cancel_read, - close_writable, - close_readable, - dealloc_lists: Some(dealloc_lists), - layout: unsafe { - ::std::alloc::Layout::from_size_align_unchecked(8, 4) - }, - lift: Some(lift), - lower: Some(lower), - new, - start_read, - start_write, -}; + pub static VTABLE: wit_bindgen_symmetric_rt::async_support::StreamVtable< + super::super::_rt::String, + > = wit_bindgen_symmetric_rt::async_support::StreamVtable:: { + cancel_write, + cancel_read, + close_writable, + close_readable, + dealloc_lists: Some(dealloc_lists), + layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(8, 4) }, + lift: Some(lift), + lower: Some(lower), + new, + start_read, + start_write, + }; -impl super::StreamPayload for super::super::_rt::String { - const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::StreamVtable = &VTABLE; -} -} -/// Creates a new Component Model `stream` with the specified payload type. -pub fn new() -> (wit_bindgen_symmetric_rt::async_support::StreamWriter, wit_bindgen_symmetric_rt::async_support::StreamReader) { - wit_bindgen_symmetric_rt::async_support::stream_support::new_stream() -} + impl super::StreamPayload for super::super::_rt::String { + const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::StreamVtable = + &VTABLE; + } + } + /// Creates a new Component Model `stream` with the specified payload type. + pub fn new() -> ( + wit_bindgen_symmetric_rt::async_support::StreamWriter, + wit_bindgen_symmetric_rt::async_support::StreamReader, + ) { + wit_bindgen_symmetric_rt::async_support::stream_support::new_stream() + } } #[cfg(target_arch = "wasm32")] @@ -154,6 +181,5 @@ d-by\x02\x0dwit-component\x070.229.0\x10wit-bindgen-rust\x060.41.0"; #[inline(never)] #[doc(hidden)] pub fn __link_custom_section_describing_imports() { - wit_bindgen::rt::maybe_link_cabi_realloc(); + wit_bindgen::rt::maybe_link_cabi_realloc(); } - diff --git a/crates/cpp/tests/symmetric_stream_string/test/src/test.rs b/crates/cpp/tests/symmetric_stream_string/test/src/test.rs index af185dab8..f4f9adb4c 100644 --- a/crates/cpp/tests/symmetric_stream_string/test/src/test.rs +++ b/crates/cpp/tests/symmetric_stream_string/test/src/test.rs @@ -2,32 +2,34 @@ // Options used: #[allow(dead_code, clippy::all)] pub mod exports { - pub mod a { - pub mod b { + pub mod a { + pub mod b { - #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] - pub mod the_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, unused_unsafe)] - pub unsafe fn _export_f_cabi() -> *mut u8 { unsafe {#[cfg(target_arch="wasm32")] - _rt::run_ctors_once();let result0 = { - T::f() - }; - (result0).take_handle() as *mut u8 - } } - pub trait Guest { - #[allow(async_fn_in_trait)] - fn f() -> wit_bindgen_symmetric_rt::async_support::StreamReader<_rt::String>; - } - #[doc(hidden)] + #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] + pub mod the_test { + #[used] + #[doc(hidden)] + static __FORCE_SECTION_REF: fn() = + super::super::super::super::__link_custom_section_describing_imports; - macro_rules! __export_a_b_the_test_cabi{ + use super::super::super::super::_rt; + #[doc(hidden)] + #[allow(non_snake_case, unused_unsafe)] + pub unsafe fn _export_f_cabi() -> *mut u8 { + unsafe { + #[cfg(target_arch = "wasm32")] + _rt::run_ctors_once(); + let result0 = { T::f() }; + (result0).take_handle() as *mut u8 + } + } + pub trait Guest { + #[allow(async_fn_in_trait)] + fn f() -> wit_bindgen_symmetric_rt::async_support::StreamReader<_rt::String>; + } + #[doc(hidden)] + + macro_rules! __export_a_b_the_test_cabi{ ($ty:ident with_types_in $($path_to_types:tt)*) => (const _: () = { #[cfg_attr(target_arch = "wasm32", export_name = "f")] @@ -37,79 +39,93 @@ pub mod exports { } };); } - #[doc(hidden)] - pub(crate) use __export_a_b_the_test_cabi; - + #[doc(hidden)] + pub(crate) use __export_a_b_the_test_cabi; + } + } } - - } -} } mod _rt { - #![allow(dead_code, clippy::all)] - pub use alloc_crate::string::String; - pub use alloc_crate::vec::Vec; - pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { - if size == 0 { - return; + #![allow(dead_code, clippy::all)] + pub use alloc_crate::string::String; + pub use alloc_crate::vec::Vec; + pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { + if size == 0 { + return; + } + unsafe { + let layout = alloc::Layout::from_size_align_unchecked(size, align); + alloc::dealloc(ptr, layout); + } } - unsafe { - let layout = alloc::Layout::from_size_align_unchecked(size, align); - alloc::dealloc(ptr, layout); + + #[cfg(target_arch = "wasm32")] + pub fn run_ctors_once() { + wit_bindgen::rt::run_ctors_once(); } - } - - #[cfg(target_arch = "wasm32")] - pub fn run_ctors_once() { - wit_bindgen::rt::run_ctors_once(); - } - extern crate alloc as alloc_crate; - pub use alloc_crate::alloc; + extern crate alloc as alloc_crate; + pub use alloc_crate::alloc; } pub mod wit_stream { - #![allow(dead_code, unused_variables, clippy::all)] + #![allow(dead_code, unused_variables, clippy::all)] - pub trait StreamPayload: Unpin + Sized + 'static { - const VTABLE: &'static wit_bindgen::rt::async_support::StreamVtable; - } - #[doc(hidden)] - #[allow(unused_unsafe)] - pub mod vtable0 { + pub trait StreamPayload: Unpin + Sized + 'static { + const VTABLE: &'static wit_bindgen::rt::async_support::StreamVtable; + } + #[doc(hidden)] + #[allow(unused_unsafe)] + pub mod vtable0 { - unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); - let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); - let len2 = l1; - let string2 = String::from(std::str::from_utf8(std::slice::from_raw_parts(l0, len2)).unwrap()); + unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { + unsafe { + let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + let len2 = l1; + let string2 = String::from( + std::str::from_utf8(std::slice::from_raw_parts(l0, len2)).unwrap(), + ); - string2 } } - unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { unsafe { let vec0 = (value.into_bytes()).into_boxed_slice(); - let ptr0 = vec0.as_ptr().cast::(); - let len0 = vec0.len(); - ::core::mem::forget(vec0); - *ptr.add(::core::mem::size_of::<*const u8>()).cast::() = len0; - *ptr.add(0).cast::<*mut u8>() = ptr0.cast_mut(); - } } - unsafe fn dealloc_lists(ptr: *mut u8) { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); - let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); - super::super::_rt::cabi_dealloc(l0, l1, 1); -} } + string2 + } + } + unsafe fn lower(value: super::super::_rt::String, ptr: *mut u8) { + unsafe { + let vec0 = (value.into_bytes()).into_boxed_slice(); + let ptr0 = vec0.as_ptr().cast::(); + let len0 = vec0.len(); + ::core::mem::forget(vec0); + *ptr.add(::core::mem::size_of::<*const u8>()).cast::() = len0; + *ptr.add(0).cast::<*mut u8>() = ptr0.cast_mut(); + } + } + unsafe fn dealloc_lists(ptr: *mut u8) { + unsafe { + let l0 = *ptr.add(0).cast::<*mut u8>(); + let l1 = *ptr.add(::core::mem::size_of::<*const u8>()).cast::(); + super::super::_rt::cabi_dealloc(l0, l1, 1); + } + } -pub static VTABLE: wit_bindgen_symmetric_rt::async_support::StreamVtable = wit_bindgen_symmetric_rt::async_support::StreamVtable:: { - layout: unsafe { - ::std::alloc::Layout::from_size_align_unchecked(8, 4) - }, - lift: Some(lift), - lower: Some(lower), -}; + pub static VTABLE: wit_bindgen_symmetric_rt::async_support::StreamVtable< + super::super::_rt::String, + > = wit_bindgen_symmetric_rt::async_support::StreamVtable:: { + layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(8, 4) }, + lift: Some(lift), + lower: Some(lower), + }; -impl super::StreamPayload for super::super::_rt::String { - const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::StreamVtable = &VTABLE; -} -} -/// Creates a new Component Model `stream` with the specified payload type. -pub fn new() -> (wit_bindgen_symmetric_rt::async_support::StreamWriter, wit_bindgen_symmetric_rt::async_support::StreamReader) { - wit_bindgen_symmetric_rt::async_support::stream_support::new_stream(T::VTABLE) -} + impl super::StreamPayload for super::super::_rt::String { + const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::StreamVtable = + &VTABLE; + } + } + /// Creates a new Component Model `stream` with the specified payload type. + pub fn new() -> ( + wit_bindgen_symmetric_rt::async_support::StreamWriter, + wit_bindgen_symmetric_rt::async_support::StreamReader, + ) { + wit_bindgen_symmetric_rt::async_support::stream_support::new_stream(T::VTABLE) + } } /// Generates `#[unsafe(no_mangle)]` functions to export the specified type as @@ -153,6 +169,5 @@ a:b/test\x04\0\x0b\x0a\x01\0\x04test\x03\0\0\0G\x09producers\x01\x0cprocessed-by #[inline(never)] #[doc(hidden)] pub fn __link_custom_section_describing_imports() { - wit_bindgen::rt::maybe_link_cabi_realloc(); + wit_bindgen::rt::maybe_link_cabi_realloc(); } - From 681d333cb39312ed49d9653b3a983c699832b9b2 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 20:29:01 +0200 Subject: [PATCH 563/672] a fully linking string stream example --- .../tests/symmetric_stream_string/Cargo.lock | 29 ++++++ .../tests/symmetric_stream_string/Cargo.toml | 1 + .../symmetric_stream_string/src/runner.rs | 64 +------------ .../src/async_support/stream_support.rs | 93 ++++++++++++++++--- .../async/stream-string/runner.rs | 18 ++-- .../runtime-async/async/stream-string/test.rs | 16 ++-- 6 files changed, 138 insertions(+), 83 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream_string/Cargo.lock b/crates/cpp/tests/symmetric_stream_string/Cargo.lock index bd45ae91d..2e90e7d9d 100644 --- a/crates/cpp/tests/symmetric_stream_string/Cargo.lock +++ b/crates/cpp/tests/symmetric_stream_string/Cargo.lock @@ -101,6 +101,12 @@ dependencies = [ "slab", ] +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + [[package]] name = "memchr" version = "2.7.4" @@ -154,11 +160,34 @@ 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 = "symmetric_stream_string" version = "0.1.0" dependencies = [ + "futures-util", + "mini-bindgen", + "symmetric_executor", + "symmetric_stream", "test", + "wit-bindgen-symmetric-rt", ] [[package]] diff --git a/crates/cpp/tests/symmetric_stream_string/Cargo.toml b/crates/cpp/tests/symmetric_stream_string/Cargo.toml index feb2bdb46..1c8c293ce 100644 --- a/crates/cpp/tests/symmetric_stream_string/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream_string/Cargo.toml @@ -12,3 +12,4 @@ symmetric_executor = { path = "../../../symmetric_executor", features = ["trace" symmetric_stream = { path = "../../../symmetric_executor/symmetric_stream" } wit-bindgen-symmetric-rt = { path = "../../../symmetric_executor/rust-client" } wit-bindgen = { path = "../../../symmetric_executor/dummy-bindgen", package = "mini-bindgen" } +futures-util = "0.3.31" diff --git a/crates/cpp/tests/symmetric_stream_string/src/runner.rs b/crates/cpp/tests/symmetric_stream_string/src/runner.rs index 2607d10ab..6b8dc9e50 100644 --- a/crates/cpp/tests/symmetric_stream_string/src/runner.rs +++ b/crates/cpp/tests/symmetric_stream_string/src/runner.rs @@ -22,7 +22,7 @@ pub mod a { fn aX3AbX2Fthe_testX00f() -> *mut u8; } let ret = aX3AbX2Fthe_testX00f(); - wit_bindgen_symmetric_rt::async_support::StreamReader::from_handle(ret) + wit_bindgen_symmetric_rt::async_support::StreamReader::from_handle(ret, <_rt::String as super::super::super::wit_stream::StreamPayload>::VTABLE) } } } @@ -54,59 +54,13 @@ mod _rt { pub mod wit_stream { #![allow(dead_code, unused_variables, clippy::all)] - pub trait StreamPayload: Unpin + Sized + 'static {} + pub trait StreamPayload: Unpin + Sized + 'static { + const VTABLE: &'static wit_bindgen::rt::async_support::StreamVtable; + } #[doc(hidden)] #[allow(unused_unsafe)] pub mod vtable0 { - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn cancel_write(_: u32) -> u32 { - unreachable!() - } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn cancel_read(_: u32) -> u32 { - unreachable!() - } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn close_writable(_: u32) { - unreachable!() - } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn close_readable(_: u32) { - unreachable!() - } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn new() -> u64 { - unreachable!() - } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn start_read(_: u32, _: *mut u8, _: usize) -> u32 { - unreachable!() - } - #[cfg(not(target_arch = "wasm32"))] - unsafe extern "C" fn start_write(_: u32, _: *const u8, _: usize) -> u32 { - unreachable!() - } - - #[cfg(target_arch = "wasm32")] - #[link(wasm_import_module = "a:b/the-test")] - unsafe extern "C" { - #[link_name = "[stream-new-0]f"] - fn new() -> u64; - #[link_name = "[stream-cancel-write-0]f"] - fn cancel_write(_: u32) -> u32; - #[link_name = "[stream-cancel-read-0]f"] - fn cancel_read(_: u32) -> u32; - #[link_name = "[stream-close-writable-0]f"] - fn close_writable(_: u32); - #[link_name = "[stream-close-readable-0]f"] - fn close_readable(_: u32); - #[link_name = "[async-lower][stream-read-0]f"] - fn start_read(_: u32, _: *mut u8, _: usize) -> u32; - #[link_name = "[async-lower][stream-write-0]f"] - fn start_write(_: u32, _: *const u8, _: usize) -> u32; - } - unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); @@ -141,17 +95,9 @@ pub mod wit_stream { pub static VTABLE: wit_bindgen_symmetric_rt::async_support::StreamVtable< super::super::_rt::String, > = wit_bindgen_symmetric_rt::async_support::StreamVtable:: { - cancel_write, - cancel_read, - close_writable, - close_readable, - dealloc_lists: Some(dealloc_lists), layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(8, 4) }, lift: Some(lift), lower: Some(lower), - new, - start_read, - start_write, }; impl super::StreamPayload for super::super::_rt::String { @@ -164,7 +110,7 @@ pub mod wit_stream { wit_bindgen_symmetric_rt::async_support::StreamWriter, wit_bindgen_symmetric_rt::async_support::StreamReader, ) { - wit_bindgen_symmetric_rt::async_support::stream_support::new_stream() + wit_bindgen_symmetric_rt::async_support::stream_support::new_stream(T::VTABLE) } } 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 index 82e1ee782..4cf8652ac 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -6,6 +6,7 @@ use crate::{ use { futures::sink::Sink, std::{ + alloc::Layout, convert::Infallible, fmt, future::Future, @@ -17,7 +18,12 @@ use { }, }; -pub use super::future_support::FutureVtable as StreamVtable; +#[doc(hidden)] +pub struct StreamVtable { + pub layout: Layout, + pub lower: Option, + pub lift: Option T>, +} fn ceiling(x: usize, y: usize) -> usize { (x / y) + if x % y == 0 { 0 } else { 1 } @@ -29,19 +35,84 @@ pub mod results { pub const CANCELED: isize = 0; } +pub struct AbiBuffer(PhantomData); + +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum StreamResult { + Complete(usize), + // Closed, + // Cancelled, +} + +pub struct StreamWrite<'a, T: 'static> { + _phantom: PhantomData<&'a T>, + writer: &'a mut StreamWriter, + future: Option + 'static + Send>>>, + values: Vec, +} + +impl Future for StreamWrite<'_, T> { + type Output = (StreamResult, AbiBuffer); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let me = self.get_mut(); + match Pin::new(&mut me.writer).poll_ready(cx) { + Poll::Ready(_) => { + let values = me.values.drain(..).collect(); + Pin::new(&mut me.writer).start_send(values).unwrap(); + match Pin::new(&mut me.writer).poll_ready(cx) { + Poll::Ready(_) => Poll::Ready((StreamResult::Complete(1), AbiBuffer(PhantomData))), + Poll::Pending => Poll::Pending, + } + } + Poll::Pending => Poll::Pending, + } + +// if me.future.is_none() { +// let handle = me.writer.handle.clone(); +// let values = &mut me.values; +// let lower = me.writer.vtable.lower; +// 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 as *mut u8; +// if let Some(lower) = lower { + +// } +// // unsafe { (lower)(data, addr) }; +// buffer.set_size(1); +// handle.finish_writing(Some(buffer)); +// }) as Pin + Send>>); +// } +// me.future.as_mut().unwrap().poll_unpin(cx) + } +} + pub struct StreamWriter { handle: Stream, future: Option + 'static + Send>>>, - _phantom: PhantomData, + vtable: &'static StreamVtable, } impl StreamWriter { #[doc(hidden)] - pub fn new(handle: Stream) -> Self { + pub fn new(handle: Stream,vtable: &'static StreamVtable,) -> Self { Self { handle, future: None, + vtable, + } + } + + pub fn write(&mut self, values: Vec) -> StreamWrite<'_, T> { + StreamWrite{ + writer: self, + future: None, _phantom: PhantomData, + values, } } @@ -129,7 +200,7 @@ impl Drop for StreamWriter { pub struct StreamReader { handle: Stream, future: Option>> + 'static + Send>>>, - _phantom: PhantomData, + vtable: &'static StreamVtable, } impl fmt::Debug for StreamReader { @@ -142,16 +213,16 @@ impl fmt::Debug for StreamReader { impl StreamReader { #[doc(hidden)] - pub fn new(handle: Stream) -> Self { + pub fn new(handle: Stream, vtable: &'static StreamVtable,) -> Self { Self { handle, future: None, - _phantom: PhantomData, + vtable, } } - pub unsafe fn from_handle(handle: *mut u8) -> Self { - Self::new(unsafe { Stream::from_handle(handle as usize) }) + pub unsafe fn from_handle(handle: *mut u8,vtable: &'static StreamVtable,) -> Self { + Self::new(unsafe { Stream::from_handle(handle as usize) }, vtable) } /// Cancel the current pending read operation. @@ -221,10 +292,8 @@ impl Drop for StreamReader { } } -// pub type StreamHandle2 = Stream; - -pub fn new_stream() -> (StreamWriter, StreamReader) { +pub fn new_stream(vtable: &'static StreamVtable,) -> (StreamWriter, StreamReader) { let handle = Stream::new(); let handle2 = handle.clone(); - (StreamWriter::new(handle), StreamReader::new(handle2)) + (StreamWriter::new(handle, vtable), StreamReader::new(handle2, vtable)) } diff --git a/tests/runtime-async/async/stream-string/runner.rs b/tests/runtime-async/async/stream-string/runner.rs index f4e1fbf1d..f7b770bc5 100644 --- a/tests/runtime-async/async/stream-string/runner.rs +++ b/tests/runtime-async/async/stream-string/runner.rs @@ -5,20 +5,26 @@ include!(env!("BINDINGS")); use wit_bindgen::rt::async_support; use crate::a::b::the_test::f; +use futures_util::StreamExt; fn main() { + // I didn't find a different way to tell rust that aX3AbX2Fthe_testX00f + // is defined by the test crate + #[cfg(not(target_arch = "wasm32"))] + test::force_link(); + async_support::block_on(async { - let stream = f(); + let mut stream = f(); let result = stream.next().await; - assert_eq!(result, Some(String::from("Hello"))); + assert_eq!(result, Some(vec![String::from("Hello")])); let result = stream.next().await; - assert_eq!(result, Some(String::from("World!"))); + assert_eq!(result, Some(vec![String::from("World!")])); let result = stream.next().await; - assert_eq!(result, Some(String::from("From"))); + assert_eq!(result, Some(vec![String::from("From")])); let result = stream.next().await; - assert_eq!(result, Some(String::from("a"))); + assert_eq!(result, Some(vec![String::from("a")])); let result = stream.next().await; - assert_eq!(result, Some(String::from("stream."))); + assert_eq!(result, Some(vec![String::from("stream.")])); let result = stream.next().await; assert_eq!(result, None); }); diff --git a/tests/runtime-async/async/stream-string/test.rs b/tests/runtime-async/async/stream-string/test.rs index a01dcd95d..aa1281849 100644 --- a/tests/runtime-async/async/stream-string/test.rs +++ b/tests/runtime-async/async/stream-string/test.rs @@ -10,14 +10,18 @@ use wit_bindgen::rt::async_support::{self, StreamReader}; impl Guest for Component { fn f() -> StreamReader { - let (wr, rd) = wit_future::new(); + let (mut wr, rd) = wit_stream::new(); async_support::spawn(async move { - wr.write(String::from("Hello")).await; - wr.write(String::from("World!")).await; - wr.write(String::from("From")).await; - wr.write(String::from("a")).await; - wr.write(String::from("stream.")).await; + wr.write(vec![String::from("Hello")]).await; + wr.write(vec![String::from("World!")]).await; + wr.write(vec![String::from("From")]).await; + wr.write(vec![String::from("a")]).await; + wr.write(vec![String::from("stream.")]).await; }); rd } } + +#[inline(never)] +#[cfg(not(target_arch = "wasm32"))] +pub fn force_link() {} From 72cdf1e85a65e57a53b23c275bf9ac818bcadaf0 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 21:33:16 +0200 Subject: [PATCH 564/672] fully operational string stream example --- .../tests/symmetric_stream_string/Cargo.toml | 2 +- .../src/async_support/stream_support.rs | 61 ++++++++----------- 2 files changed, 27 insertions(+), 36 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream_string/Cargo.toml b/crates/cpp/tests/symmetric_stream_string/Cargo.toml index 1c8c293ce..7f105f3f2 100644 --- a/crates/cpp/tests/symmetric_stream_string/Cargo.toml +++ b/crates/cpp/tests/symmetric_stream_string/Cargo.toml @@ -9,7 +9,7 @@ edition = "2024" [dependencies] test = { path = "test" } symmetric_executor = { path = "../../../symmetric_executor", features = ["trace"]} -symmetric_stream = { path = "../../../symmetric_executor/symmetric_stream" } +symmetric_stream = { path = "../../../symmetric_executor/symmetric_stream", features = ["trace"] } wit-bindgen-symmetric-rt = { path = "../../../symmetric_executor/rust-client" } wit-bindgen = { path = "../../../symmetric_executor/dummy-bindgen", package = "mini-bindgen" } futures-util = "0.3.31" 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 index 4cf8652ac..51d914784 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -51,43 +51,29 @@ pub struct StreamWrite<'a, T: 'static> { values: Vec, } -impl Future for StreamWrite<'_, T> { +impl Future for StreamWrite<'_, T> { type Output = (StreamResult, AbiBuffer); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let me = self.get_mut(); - match Pin::new(&mut me.writer).poll_ready(cx) { + let me = self.get_mut(); + match Pin::new(&mut me.writer).poll_ready(cx) { Poll::Ready(_) => { - let values = me.values.drain(..).collect(); - Pin::new(&mut me.writer).start_send(values).unwrap(); - match Pin::new(&mut me.writer).poll_ready(cx) { - Poll::Ready(_) => Poll::Ready((StreamResult::Complete(1), AbiBuffer(PhantomData))), - Poll::Pending => Poll::Pending, + let values: Vec<_> = me.values.drain(..).collect(); + if values.is_empty() { + // delayed flush + Poll::Ready((StreamResult::Complete(1), AbiBuffer(PhantomData))) + } else { + Pin::new(&mut me.writer).start_send(values).unwrap(); + match Pin::new(&mut me.writer).poll_ready(cx) { + Poll::Ready(_) => { + Poll::Ready((StreamResult::Complete(1), AbiBuffer(PhantomData))) + } + Poll::Pending => Poll::Pending, + } } } Poll::Pending => Poll::Pending, } - -// if me.future.is_none() { -// let handle = me.writer.handle.clone(); -// let values = &mut me.values; -// let lower = me.writer.vtable.lower; -// 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 as *mut u8; -// if let Some(lower) = lower { - -// } -// // unsafe { (lower)(data, addr) }; -// buffer.set_size(1); -// handle.finish_writing(Some(buffer)); -// }) as Pin + Send>>); -// } -// me.future.as_mut().unwrap().poll_unpin(cx) } } @@ -99,7 +85,7 @@ pub struct StreamWriter { impl StreamWriter { #[doc(hidden)] - pub fn new(handle: Stream,vtable: &'static StreamVtable,) -> Self { + pub fn new(handle: Stream, vtable: &'static StreamVtable) -> Self { Self { handle, future: None, @@ -108,7 +94,7 @@ impl StreamWriter { } pub fn write(&mut self, values: Vec) -> StreamWrite<'_, T> { - StreamWrite{ + StreamWrite { writer: self, future: None, _phantom: PhantomData, @@ -213,7 +199,7 @@ impl fmt::Debug for StreamReader { impl StreamReader { #[doc(hidden)] - pub fn new(handle: Stream, vtable: &'static StreamVtable,) -> Self { + pub fn new(handle: Stream, vtable: &'static StreamVtable) -> Self { Self { handle, future: None, @@ -221,7 +207,7 @@ impl StreamReader { } } - pub unsafe fn from_handle(handle: *mut u8,vtable: &'static StreamVtable,) -> Self { + pub unsafe fn from_handle(handle: *mut u8, vtable: &'static StreamVtable) -> Self { Self::new(unsafe { Stream::from_handle(handle as usize) }, vtable) } @@ -292,8 +278,13 @@ impl Drop for StreamReader { } } -pub fn new_stream(vtable: &'static StreamVtable,) -> (StreamWriter, StreamReader) { +pub fn new_stream( + vtable: &'static StreamVtable, +) -> (StreamWriter, StreamReader) { let handle = Stream::new(); let handle2 = handle.clone(); - (StreamWriter::new(handle, vtable), StreamReader::new(handle2, vtable)) + ( + StreamWriter::new(handle, vtable), + StreamReader::new(handle2, vtable), + ) } From ee27d1a61c8ff0a0fd6bfc248318000efe3bedd8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 21:41:07 +0200 Subject: [PATCH 565/672] cargo fmt --- crates/core/src/abi.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index e65601a4a..49286ab45 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -864,7 +864,11 @@ pub fn deallocate_lists_and_own_in_types( ptr: B::Operand, bindgen: &mut B, ) { - Generator::new(resolve, bindgen, false).deallocate_in_types(types, ptr, Deallocate::ListsAndOwn); + Generator::new(resolve, bindgen, false).deallocate_in_types( + types, + ptr, + Deallocate::ListsAndOwn, + ); } #[derive(Copy, Clone)] From a3cf2649b0f6f2d53f17736aad0dd0460a4d60ee Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 21:55:43 +0200 Subject: [PATCH 566/672] fix rust stream example --- .../tests/symmetric_stream/stream/src/lib.rs | 2 +- .../stream/src/stream_world.rs | 36 ++++++++++++++++--- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs index 88a10e84b..9be415124 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/lib.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/lib.rs @@ -10,7 +10,7 @@ struct MyStruct; impl stream_world::exports::test::test::stream_test::Guest for MyStruct { fn create() -> async_support::StreamReader { - let (mut writer, reader) = async_support::stream_support::new_stream(); + let (mut writer, reader) = stream_world::wit_stream::new(); let mut input = create(); async_support::spawn(async move { diff --git a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs index 705ebcecd..a0ef648ab 100644 --- a/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs +++ b/crates/cpp/tests/symmetric_stream/stream/src/stream_world.rs @@ -22,7 +22,10 @@ pub mod test { fn testX3AtestX2Fstream_sourceX00create() -> *mut u8; } let ret = testX3AtestX2Fstream_sourceX00create(); - wit_bindgen_symmetric_rt::async_support::StreamReader::from_handle(ret) + wit_bindgen_symmetric_rt::async_support::StreamReader::from_handle( + ret, + ::VTABLE, + ) } } } @@ -84,14 +87,39 @@ mod _rt { pub mod wit_stream { #![allow(dead_code, unused_variables, clippy::all)] - pub trait StreamPayload: Unpin + Sized + 'static {} - impl StreamPayload for u32 {} + pub trait StreamPayload: Unpin + Sized + 'static { + const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::StreamVtable; + } + #[doc(hidden)] + #[allow(unused_unsafe)] + pub mod vtable0 { + + unsafe fn lift(ptr: *mut u8) -> u32 { + unsafe { *ptr.cast::() } + } + unsafe fn lower(value: u32, ptr: *mut u8) { + unsafe { + *ptr.cast::() = value; + } + } + + pub static VTABLE: wit_bindgen_symmetric_rt::async_support::StreamVtable = + wit_bindgen_symmetric_rt::async_support::StreamVtable:: { + layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(4, 4) }, + lift: Some(lift), + lower: Some(lower), + }; + impl super::StreamPayload for u32 { + const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::StreamVtable = + &VTABLE; + } + } /// Creates a new Component Model `stream` with the specified payload type. pub fn new() -> ( wit_bindgen_symmetric_rt::async_support::StreamWriter, wit_bindgen_symmetric_rt::async_support::StreamReader, ) { - wit_bindgen_symmetric_rt::async_support::stream_support::new_stream() + wit_bindgen_symmetric_rt::async_support::stream_support::new_stream(T::VTABLE) } } From c03dcbd112c87fbf0eb1257b84ab954173205f59 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 3 May 2025 23:29:59 +0200 Subject: [PATCH 567/672] stream lifting and lowering, more elegant than Rust --- .../future_cpp/future_world.cpp | 6 ++- .../stream_cpp/stream_world.cpp | 21 ++++++++ .../cpp-client/async_support.h | 18 +++---- .../cpp-client/stream_support.h | 50 ++++++++++++------- 4 files changed, 67 insertions(+), 28 deletions(-) diff --git a/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp b/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp index da1f26953..bce9a0088 100644 --- a/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp +++ b/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp @@ -30,6 +30,10 @@ struct IntLifting { static T lift(uint8_t const*ptr) { return *(T const*)ptr; } + static void lower(T&& obj, uint8_t *ptr) { +// new ((T*)ptr) T(std::move(obj)); + *(T*)ptr = obj; + } }; extern "C" uint8_t* testX3AtestX2Ffuture_sourceX00create(); @@ -42,7 +46,7 @@ extern "C" uint8_t* testX3AtestX2Ffuture_testX00create() { auto result0 = exports::test::test::future_test::Create(); - return lower_future(std::move(result0)); + return lower_future>(std::move(result0)); } // Component Adapters diff --git a/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world.cpp b/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world.cpp index fb8b9b3ee..5a4a5f8c6 100644 --- a/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world.cpp +++ b/crates/cpp/tests/symmetric_stream/stream_cpp/stream_world.cpp @@ -22,6 +22,27 @@ void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { return ret; } +template <> +const uint32_t wit::StreamProperties::lowered_size = 4; +template <> +uint32_t wit::StreamProperties::lift(uint8_t const*ptr) { + return *(uint32_t const*)ptr; +} +template <> +void wit::StreamProperties::lower(uint32_t && value, uint8_t *ptr) { + *(uint32_t*)ptr = value; +} + +// struct IntLifting { +// static constexpr size_t SIZE = sizeof(T); +// static T lift(uint8_t const*ptr) { +// return *(T const*)ptr; +// } +// static void lower(T&& obj, uint8_t *ptr) { +// // new ((T*)ptr) T(std::move(obj)); +// *(T*)ptr = obj; +// } +// }; extern "C" uint8_t* testX3AtestX2Fstream_sourceX00create(); wit::stream test::test::stream_source::Create() diff --git a/crates/symmetric_executor/cpp-client/async_support.h b/crates/symmetric_executor/cpp-client/async_support.h index b4ad6881f..97c2ab4a6 100644 --- a/crates/symmetric_executor/cpp-client/async_support.h +++ b/crates/symmetric_executor/cpp-client/async_support.h @@ -134,11 +134,11 @@ template struct stream_writer { } auto buffer = handle.StartWriting(); auto capacity = buffer.Capacity(); - T* dest = (T*)buffer.GetAddress().into_handle(); + uint8_t* dest = (uint8_t*)buffer.GetAddress().into_handle(); auto elements = data.size(); if (elements::lower(std::move(data[i]), dest+(i*wit::StreamProperties::lowered_size)); } buffer.SetSize(elements); handle.FinishWriting(std::optional(std::move(buffer))); @@ -172,16 +172,16 @@ struct write_to_future_data { std::future fut; }; -template +template static symmetric::runtime::symmetric_executor::CallbackState write_to_future(void* data) { std::unique_ptr> ptr((write_to_future_data*)data); // is future ready? if (ptr->fut.wait_for(std::chrono::seconds::zero()) == std::future_status::ready) { auto buffer = ptr->wr.handle.StartWriting(); assert(buffer.GetSize()==1); //sizeof(T)); - T* dataptr = (T*)(buffer.GetAddress().into_handle()); + uint8_t* dataptr = (uint8_t*)(buffer.GetAddress().into_handle()); auto result = ptr->fut.get(); - new (dataptr) T(std::move(result)); + LOWER::lower(std::move(result), dataptr); buffer.SetSize(1); ptr->wr.handle.FinishWriting(std::optional(std::move(buffer))); } else { @@ -193,9 +193,9 @@ static symmetric::runtime::symmetric_executor::CallbackState write_to_future(voi symmetric::runtime::symmetric_executor::EventGenerator &&gen){ auto buffer = ptr->wr.handle.StartWriting(); // assert(buffer.GetSize()==1); //sizeof(T)); - T* dataptr = (T*)(buffer.GetAddress().into_handle()); + uint8_t* dataptr = (uint8_t*)(buffer.GetAddress().into_handle()); auto result = ptr->fut.get(); - new (dataptr) T(std::move(result)); + LOWER::lower(std::move(result), dataptr); buffer.SetSize(1); ptr->wr.handle.FinishWriting(std::optional(std::move(buffer))); gen.Activate(); @@ -208,13 +208,13 @@ static symmetric::runtime::symmetric_executor::CallbackState write_to_future(voi return symmetric::runtime::symmetric_executor::CallbackState::kReady; } -template +template uint8_t* lower_future(std::future &&f) { auto handles = create_wasi_future(); auto wait_on = handles.first.handle.WriteReadySubscribe(); auto fut = std::make_unique>(write_to_future_data{std::move(handles.first), std::move(f)}); symmetric::runtime::symmetric_executor::Register(std::move(wait_on), - symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase((uint8_t*)&write_to_future)), + symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase((uint8_t*)&write_to_future)), symmetric::runtime::symmetric_executor::CallbackData(wit::ResourceImportBase((uint8_t*)fut.release()))); return handles.second.handle.into_handle(); } diff --git a/crates/symmetric_executor/cpp-client/stream_support.h b/crates/symmetric_executor/cpp-client/stream_support.h index 1d56badcc..530ee2c49 100644 --- a/crates/symmetric_executor/cpp-client/stream_support.h +++ b/crates/symmetric_executor/cpp-client/stream_support.h @@ -6,37 +6,46 @@ #include namespace wit { + // template + // union MaybeUninit + // { + // T value; + // char dummy; + // MaybeUninit() + // : dummy() + // { } + // MaybeUninit(MaybeUninit const& b) + // : dummy() + // { } + // ~MaybeUninit() + // { } + // }; + template - union MaybeUninit - { - T value; - char dummy; - MaybeUninit() - : dummy() - { } - MaybeUninit(MaybeUninit const& b) - : dummy() - { } - ~MaybeUninit() - { } + struct StreamProperties { + static const uint32_t lowered_size; + static T lift(uint8_t const*); + static void lower(T&&, uint8_t*); }; template struct stream { symmetric::runtime::symmetric_stream::StreamObj handle; + // StreamProperties const* lifting; uint32_t buffer_size = 1; static stream new_empty() { - return stream{symmetric::runtime::symmetric_stream::StreamObj(wit::ResourceImportBase()), 1}; + return stream{symmetric::runtime::symmetric_stream::StreamObj(wit::ResourceImportBase()), nullptr, 1}; } struct background_object { symmetric::runtime::symmetric_stream::StreamObj handle; std::function)> reader; - std::vector> buffer; + std::vector buffer; + // StreamProperties const* lifting; background_object(symmetric::runtime::symmetric_stream::StreamObj && h, - std::function)>&& r, std::vector> b) + std::function)>&& r, std::vector &&b) : handle(std::move(h)), reader(std::move(r)), buffer(std::move(b)) {} }; @@ -49,18 +58,23 @@ namespace wit { if (buffer.has_value()) { assert(buffer->GetAddress().into_handle() == (wit::ResourceImportBase::handle_t)data->buffer.data()); uint32_t size = buffer->GetSize(); + std::vector lifted; + lifted.reserve(size); + for (uint32_t i = 0; i::lift(data->buffer.data()+i*StreamProperties::lowered_size)); + } if (size>0) - data->reader(wit::span(&data->buffer[0].value, size)); + data->reader(wit::span(lifted.data(), size)); data->handle.StartReading(std::move(*buffer)); return symmetric::runtime::symmetric_executor::CallbackState::kPending; } else { - data->reader(wit::span(&data->buffer[0].value, 0)); + data->reader(wit::span(nullptr, 0)); auto release = std::unique_ptr(data); return symmetric::runtime::symmetric_executor::CallbackState::kReady; } } void set_reader(std::function)> &&fun) && { - std::vector> buffer(buffer_size, MaybeUninit()); + std::vector buffer(buffer_size*StreamProperties::lowered_size, uint8_t(0)); background_object* object = std::make_unique(background_object{std::move(handle), std::move(fun), std::move(buffer)}).release(); From ef5401451443e2d4d3c91a399db6a2accfa37812 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 5 May 2025 23:40:26 +0200 Subject: [PATCH 568/672] fix compilation of executor --- crates/symmetric_executor/cpp-client/stream_support.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/symmetric_executor/cpp-client/stream_support.h b/crates/symmetric_executor/cpp-client/stream_support.h index 530ee2c49..ba2732dfb 100644 --- a/crates/symmetric_executor/cpp-client/stream_support.h +++ b/crates/symmetric_executor/cpp-client/stream_support.h @@ -35,7 +35,7 @@ namespace wit { uint32_t buffer_size = 1; static stream new_empty() { - return stream{symmetric::runtime::symmetric_stream::StreamObj(wit::ResourceImportBase()), nullptr, 1}; + return stream{symmetric::runtime::symmetric_stream::StreamObj(wit::ResourceImportBase()), 1}; } struct background_object { From 425a75a4cefe8b7f51f9b993010afea85d2d79d1 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 7 May 2025 22:43:40 +0200 Subject: [PATCH 569/672] update bindings --- crates/core/src/abi.rs | 104 +++++++++++++++++++++++++++-------- crates/core/src/symmetric.rs | 3 + crates/cpp/src/lib.rs | 3 + crates/cpp/src/wamr.rs | 2 + 4 files changed, 90 insertions(+), 22 deletions(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 8175d17cb..2826b5a2a 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -2271,22 +2271,31 @@ fn cast(from: WasmType, to: WasmType) -> Bitcast { } } -fn push_flat_symmetric(resolve: &Resolve, ty: &Type, vec: &mut Vec) { +fn push_flat_symmetric(resolve: &Resolve, ty: &Type, vec: &mut FlatTypes<'_>) -> bool { if let Type::Id(id) = ty { if matches!( &resolve.types[*id].kind, TypeDefKind::Handle(_) | TypeDefKind::Stream(_) | TypeDefKind::Future(_) ) { - vec.push(WasmType::Pointer); + vec.push(WasmType::Pointer) } else { - resolve.push_flat(ty, vec); + resolve.push_flat(ty, vec) } } else { - resolve.push_flat(ty, vec); + resolve.push_flat(ty, vec) } } // another hack +fn push_flat_list_symmetric<'a>( + resolve: &Resolve, + mut list: impl Iterator, + result: &mut FlatTypes<'_>, +// _symmetric: bool, +) -> bool { + list.all(|ty| push_flat_symmetric(resolve, ty, result)) +} + pub fn wasm_signature_symmetric( resolve: &Resolve, variant: AbiVariant, @@ -2299,39 +2308,90 @@ pub fn wasm_signature_symmetric( 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); - } + let mut storage = [WasmType::I32; MAX_FLAT_PARAMS + 1]; + let mut params = FlatTypes::new(&mut storage); + let ok = push_flat_list_symmetric(resolve, func.params.iter().map(|(_, param)| param), &mut params); + // assert_eq!(ok, !params.overflow); - if params.len() > MAX_FLAT_PARAMS { - params.truncate(0); + let indirect_params = !ok || params.to_vec().len() > MAX_FLAT_PARAMS; + if indirect_params { + params = FlatTypes::new(&mut storage); params.push(WasmType::Pointer); - indirect_params = true; + } else { + if matches!( + (&func.kind, variant), + ( + crate::FunctionKind::Method(_) | crate::FunctionKind::AsyncMethod(_), + AbiVariant::GuestExport + | AbiVariant::GuestExportAsync + | AbiVariant::GuestExportAsyncStackful + ) + ) { + // Guest exported methods always receive resource rep as first argument + // + // TODO: Ideally you would distinguish between imported and exported + // resource Handles and then use either I32 or Pointer in abi::push_flat(). + // But this contextual information isn't available, yet. + // See https://github.com/bytecodealliance/wasm-tools/pull/1438 for more details. + let mut old = params.to_vec(); + assert!(matches!(old[0], WasmType::I32)); + old[0] = WasmType::Pointer; + params = FlatTypes::new(&mut storage); + old.iter().for_each(|e| { params.push(*e); }); +// params.push(WasmType::Pointer); + } } - let mut results = Vec::new(); - for ty in func.result.iter() { - push_flat_symmetric(resolve, ty, &mut results) + match variant { + AbiVariant::GuestExportAsync => { + return WasmSignature { + params: params.to_vec(), + indirect_params, + results: vec![WasmType::Pointer], + retptr: false, + }; + } + AbiVariant::GuestExportAsyncStackful => { + return WasmSignature { + params: params.to_vec(), + indirect_params, + results: Vec::new(), + retptr: false, + }; + } + _ => {} + } + + let mut storage = [WasmType::I32; MAX_FLAT_RESULTS+1]; + let mut results = FlatTypes::new(&mut storage); + if let Some(ty) = &func.result { + push_flat_symmetric(resolve, ty, &mut results); } - let mut retptr = false; + let retptr = results.to_vec().len()>MAX_FLAT_RESULTS; // 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); + if retptr { + results = FlatTypes::new(&mut storage); +// results.cur = 0; + match variant { + AbiVariant::GuestImport => { + assert!(params.push(WasmType::Pointer)); + } + AbiVariant::GuestExport => { + assert!(results.push(WasmType::Pointer)); + } + _ => unreachable!(), + } } WasmSignature { - params, + params: params.to_vec(), indirect_params, - results, + results: results.to_vec(), retptr, } } diff --git a/crates/core/src/symmetric.rs b/crates/core/src/symmetric.rs index 6d73fbca2..2eedd7a16 100644 --- a/crates/core/src/symmetric.rs +++ b/crates/core/src/symmetric.rs @@ -39,6 +39,7 @@ fn needs_dealloc2(resolve: &Resolve, tp: &Type) -> bool { TypeDefKind::Stream(_) => todo!(), TypeDefKind::Type(tp) => needs_dealloc2(resolve, tp), TypeDefKind::Unknown => false, + TypeDefKind::FixedSizeList(_, _) => todo!(), }, Type::ErrorContext => todo!(), } @@ -104,6 +105,7 @@ fn has_non_canonical_list2(resolve: &Resolve, ty: &Type, maybe: bool) -> bool { TypeDefKind::Future(_) | TypeDefKind::Stream(_) => false, TypeDefKind::Type(ty) => has_non_canonical_list2(resolve, ty, maybe), TypeDefKind::Unknown => false, + TypeDefKind::FixedSizeList(_, _) => todo!(), }, Type::ErrorContext => todo!(), } @@ -168,6 +170,7 @@ fn has_non_canonical_list_rust2(resolve: &Resolve, ty: &Type) -> bool { TypeDefKind::Future(_) | TypeDefKind::Stream(_) => false, TypeDefKind::Type(ty) => has_non_canonical_list_rust2(resolve, ty), TypeDefKind::Unknown => false, + TypeDefKind::FixedSizeList(_, _) => todo!(), }, Type::ErrorContext => todo!(), } diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index df41fa847..82628efd8 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -962,6 +962,7 @@ impl CppInterfaceGenerator<'_> { TypeDefKind::Stream(_) => todo!("generate for stream"), TypeDefKind::Handle(_) => todo!("generate for handle"), TypeDefKind::Unknown => unreachable!(), + TypeDefKind::FixedSizeList(_, _) => todo!(), } } @@ -2055,6 +2056,7 @@ impl CppInterfaceGenerator<'_> { } TypeDefKind::Type(ty) => self.type_name(ty, from_namespace, flavor), TypeDefKind::Unknown => todo!(), + TypeDefKind::FixedSizeList(_, _) => todo!(), }, Type::ErrorContext => todo!(), } @@ -2702,6 +2704,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { _ => false, }, TypeDefKind::Unknown => todo!(), + TypeDefKind::FixedSizeList(_, _) => todo!(), } } } diff --git a/crates/cpp/src/wamr.rs b/crates/cpp/src/wamr.rs index 6121cbbb3..fab0c6f71 100644 --- a/crates/cpp/src/wamr.rs +++ b/crates/cpp/src/wamr.rs @@ -61,6 +61,7 @@ fn push_wamr(ty: &Type, resolve: &Resolve, params_str: &mut String) { TypeDefKind::Handle(_h) => { params_str.push('i'); } + TypeDefKind::FixedSizeList(_, _) => todo!(), }, Type::ErrorContext => todo!(), } @@ -124,6 +125,7 @@ fn wamr_add_result(sig: &mut WamrSig, resolve: &Resolve, ty: &Type) { TypeDefKind::Handle(_h) => { sig.wamr_result = "i".into(); } + TypeDefKind::FixedSizeList(_, _) => todo!(), }, Type::ErrorContext => todo!(), } From 402e474dc595511a6b7f62f955eb444e1c32db27 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 7 May 2025 22:50:07 +0200 Subject: [PATCH 570/672] fix warning --- crates/core/src/abi.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index 2826b5a2a..0e42129a2 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -937,7 +937,7 @@ impl<'a, B: Bindgen> Generator<'a, B> { lift_lower: LiftLower, async_: bool, ) { - const MAX_FLAT_PARAMS: usize = 16; + // const MAX_FLAT_PARAMS: usize = 16; // Lowering parameters calling a wasm import _or_ returning a result // from an async-lifted wasm export means we don't need to pass From 4138036f6ba058ce43a8fa81a7f2fafef46eac3e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 20 Apr 2025 23:37:44 +0200 Subject: [PATCH 571/672] initial c++ skeleton --- Cargo.lock | 16 + Cargo.toml | 4 + README.md | 8 + crates/cpp/Cargo.toml | 27 + crates/cpp/DESIGN.md | 98 + crates/cpp/helper-types/wit-common.h | 69 + crates/cpp/helper-types/wit-guest.h | 213 ++ crates/cpp/src/lib.rs | 3994 +++++++++++++++++++++ crates/cpp/test_headers/expected | 10 + crates/cpp/test_headers/expected.hpp | 2444 +++++++++++++ crates/cpp/test_headers/wit-common.h | 1 + crates/cpp/test_headers/wit-guest.h | 1 + src/bin/wit-bindgen.rs | 10 + tests/runtime/lists/test.new.cpp | 185 + tests/runtime/many_arguments/wasm.new.cpp | 46 + tests/runtime/numbers/test.cpp | 112 + tests/runtime/options/test.new.cpp | 58 + tests/runtime/records/test.new.cpp | 75 + tests/runtime/results/test.new.cpp | 56 + tests/runtime/strings/test.cpp | 28 + tests/runtime/strings/test.new.cpp | 29 + 21 files changed, 7484 insertions(+) create mode 100644 crates/cpp/Cargo.toml create mode 100644 crates/cpp/DESIGN.md create mode 100644 crates/cpp/helper-types/wit-common.h create mode 100644 crates/cpp/helper-types/wit-guest.h create mode 100644 crates/cpp/src/lib.rs create mode 100644 crates/cpp/test_headers/expected create mode 100644 crates/cpp/test_headers/expected.hpp create mode 120000 crates/cpp/test_headers/wit-common.h create mode 120000 crates/cpp/test_headers/wit-guest.h create mode 100644 tests/runtime/lists/test.new.cpp create mode 100644 tests/runtime/many_arguments/wasm.new.cpp create mode 100644 tests/runtime/numbers/test.cpp create mode 100644 tests/runtime/options/test.new.cpp create mode 100644 tests/runtime/records/test.new.cpp create mode 100644 tests/runtime/results/test.new.cpp create mode 100644 tests/runtime/strings/test.cpp create mode 100644 tests/runtime/strings/test.new.cpp diff --git a/Cargo.lock b/Cargo.lock index 389a616de..e1a09ad20 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1222,6 +1222,7 @@ dependencies = [ "wasm-encoder 0.230.0", "wit-bindgen-c", "wit-bindgen-core", + "wit-bindgen-cpp", "wit-bindgen-csharp", "wit-bindgen-markdown", "wit-bindgen-moonbit", @@ -1241,6 +1242,21 @@ dependencies = [ "wit-parser", ] +[[package]] +name = "wit-bindgen-cpp" +version = "0.41.0" +dependencies = [ + "anyhow", + "clap", + "heck", + "test-helpers", + "wasm-encoder 0.229.0", + "wasm-metadata 0.229.0", + "wit-bindgen-c", + "wit-bindgen-core", + "wit-component", +] + [[package]] name = "wit-bindgen-csharp" version = "0.42.1" diff --git a/Cargo.toml b/Cargo.toml index bc64aef2a..cc863f4dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ wasm-compose = "0.230.0" wit-bindgen-core = { path = 'crates/core', version = '0.42.1' } wit-bindgen-c = { path = 'crates/c', version = '0.42.1' } +wit-bindgen-cpp = { path = 'crates/cpp', version = '0.42.1' } wit-bindgen-rust = { path = "crates/rust", version = "0.42.1" } wit-bindgen-csharp = { path = 'crates/csharp', version = '0.42.1' } wit-bindgen-markdown = { path = 'crates/markdown', version = '0.42.1' } @@ -57,6 +58,7 @@ clap = { workspace = true, features = ['wrap_help'] } wit-bindgen-core = { workspace = true } wit-bindgen-rust = { workspace = true, features = ['clap'], optional = true } wit-bindgen-c = { workspace = true, features = ['clap'], optional = true } +wit-bindgen-cpp = { workspace = true, features = ['clap'], optional = true } wit-bindgen-markdown = { workspace = true, features = ['clap'], optional = true } wit-bindgen-moonbit = { workspace = true, features = ['clap'], optional = true } wit-bindgen-csharp = { workspace = true, features = ['clap'], optional = true } @@ -72,10 +74,12 @@ default = [ 'markdown', 'go', 'csharp', + 'cpp', 'moonbit', 'async', ] c = ['dep:wit-bindgen-c'] +cpp = ['dep:wit-bindgen-cpp'] rust = ['dep:wit-bindgen-rust'] markdown = ['dep:wit-bindgen-markdown'] go = [] diff --git a/README.md b/README.md index 08f0aff38..904172cf9 100644 --- a/README.md +++ b/README.md @@ -385,6 +385,14 @@ Then, you can generate the bindings for your project: wit-bindgen-go generate ``` +### Guest: C++-17+ + +The cpp crate 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. + ### Guest: MoonBit MoonBit can be compiled to WebAssembly using [its toolchain](https://moonbitlang.com/download): diff --git a/crates/cpp/Cargo.toml b/crates/cpp/Cargo.toml new file mode 100644 index 000000000..346c9e21a --- /dev/null +++ b/crates/cpp/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "wit-bindgen-cpp" +authors = ["Christof Petig "] +version = "0.41.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 } + +[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 | (&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 | Result<&str, &[]> | std::expected | d,a,l | +| | | option\ | Option\<&str> | optional const& | d,a,l| +| | | list\ | &[\&Resrc]? | vector const& | a,l| +| GIR | t | string | String | wit::string[^2] | &(addr, len) [^8] | | +| | | list | Vec | wit::vector | &(a,l) | +| | | result | Result | std::expected | &(d,a,l) | +| GEA | t | string | String | 🕸 wit::string&& | addr, len | +| | | | | 💎 string_view | | +| | | result | Result | 🕸 std::expected&& | d,a,l | +| | | | | 💎 std::expected | | +| GER | p | string | String | wit::string (or std?) | 📘 -> &(a,l) cabi_post_N:P/I#F [^7] | +| | | | | | 🪞 &(a,l) | +| | | result | Result | std::expected | 📘 -> &(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 | 📘 -> &(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 +#include +#include +#include // size_t +#include +#if __cplusplus > 202001L +#include +#else +#include +#endif + +namespace wit { +#if __cplusplus > 202001L +using std::span; +#else +/// Minimal span (vector view) implementation for older C++ environments +template 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 + span(std::vector 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 ResourceTable { + static std::map 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(id, std::move(value))); + return id; + } + static std::optional remove_resource(int32_t id) { + auto iter = resources.find(id); + std::optional 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 +#include // unique_ptr +#include +#include +#include +#include // memcpy +#include // free +#include + +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 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(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(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 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(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 allocate(size_t len) { + if (!len) return vector(empty_ptr(), 0); + return vector((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 get_view() const { return wit::span(data_, length); } + wit::span get_const_view() const { return wit::span(data_, length); } + template static vector from_view(wit::span const& a) { + auto result = vector::allocate(a.size()); + for (uint32_t i=0;i 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 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/src/lib.rs b/crates/cpp/src/lib.rs new file mode 100644 index 000000000..afafbb820 --- /dev/null +++ b/crates/cpp/src/lib.rs @@ -0,0 +1,3994 @@ +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, Results, 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, + 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, +} + +#[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, + // interface_header: SourceWithState, + extern_c_decls: Source, + dependencies: Includes, + includes: Vec, + host_functions: HashMap>, + world: String, + world_id: Option, + imported_interfaces: HashSet, + user_class_files: HashMap, + defined_types: HashSet<(Vec, String)>, + + // needed for symmetric disambiguation + interface_prefixes: HashMap<(Direction, WorldKey), String>, + import_prefix: Option, +} + +#[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 { + 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, + + /// 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, + + /// 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 { + 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, 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, + ) -> 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(""); + self.include(""); // for std::move + if self.dependencies.needs_string { + self.include(""); + } + if self.dependencies.needs_string_view { + self.include(""); + } + if self.dependencies.needs_vector { + self.include(""); + } + if self.dependencies.needs_expected { + self.include(""); + } + if self.dependencies.needs_optional { + self.include(""); + } + if self.dependencies.needs_cstring { + self.include(""); + } + if self.dependencies.needs_imported_resources { + self.include(""); + } + if self.dependencies.needs_exported_resources { + self.include(""); + } + if self.dependencies.needs_variant { + self.include(""); + } + if self.dependencies.needs_tuple { + self.include(""); + } + if self.dependencies.needs_wit { + if self.opts.host_side() { + self.include(""); + } else { + self.include(""); + } + } + if self.dependencies.needs_memory { + self.include(""); + } + } + + fn start_new_file(&mut self, condition: Option) -> 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, + 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 // 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-micro-runtime header\n\ + #include \n\ + #include " + ); + + 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 std::map wit::{RESOURCE_TABLE_NAME}::resources;" + ); + } + if self.dependencies.needs_assert { + uwriteln!(c_str.src, "#include "); + } + + 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 { + 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) { + 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) { + 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, + _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, +} + +// I wish this was possible +// impl Equivalent<(Vec, String)> for (&Vec, &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!(), + TypeDefKind::ErrorContext => todo!(), + } + } + + /// This describes the C++ side name + fn func_namespace_name( + &self, + func: &Function, + guest_export: bool, + cpp_file: bool, + ) -> (Vec, 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), + } + .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 { + 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, + ) -> 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()) + { + match &func.results { + wit_bindgen_core::wit_parser::Results::Named(n) => { + if n.is_empty() { + res.result = "void".into(); + } else { + res.result = "std::tuple<".into(); + for (i, (_name, ty)) in n.iter().enumerate() { + if i > 0 { + res.result.push_str(", "); + } + res.result.push_str(&self.type_name( + ty, + &res.namespace, + Flavor::Result(abi_variant), + )); + } + res.result.push('>'); + } + } + wit_bindgen_core::wit_parser::Results::Anon(ty) => { + if matches!(is_drop, SpecialMethod::Allocate) { + res.result = OWNED_CLASS_NAME.into(); + } else { + res.result = + self.type_name(ty, from_namespace, Flavor::Result(abi_variant)); + if matches!( + is_drop, + SpecialMethod::Allocate | SpecialMethod::ResourceRep + ) { + res.result.push('*'); + } + } + } + } + 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 { + 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::>() + .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 { + 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 _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!(), + }] + .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 { + 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, + 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, + 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, 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" + } + 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!(), + TypeDefKind::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))], + results: Results::Named(vec![]), + 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, + } { + 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(), + results: Results::Anon(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))], + results: Results::Anon(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)], + results: Results::Anon(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)], + results: Results::Named(vec![]), + 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, _docs: &Docs) { + todo!() + } + + fn type_stream(&mut self, _id: TypeId, _name: &str, _ty: &Option, _docs: &Docs) { + todo!() + } + + fn type_error_context(&mut self, _id: TypeId, _name: &str, _docs: &Docs) { + todo!() + } +} + +struct CabiPostInformation { + module: String, + name: String, + ret_type: String, +} + +struct FunctionBindgen<'a, 'b> { + gen: &'b mut CppInterfaceGenerator<'a>, + params: Vec, + tmp: usize, + // import_return_pointer_area_size: usize, + // import_return_pointer_area_align: usize, + namespace: Vec, + src: Source, + block_storage: Vec, + /// intermediate calculations for contained objects + blocks: Vec<(String, Vec)>, + payloads: Vec, + // caching for wasm + wamr_signature: Option, + variant: AbiVariant, + cabi_post: Option, + needs_dealloc: bool, + leak_on_insertion: Option, +} + +impl<'a, 'b> FunctionBindgen<'a, 'b> { + fn new(gen: &'b mut CppInterfaceGenerator<'a>, params: Vec) -> 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) { + 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, + ) { + 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, + ) { + 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, // correct? + Type::Id(id) => self.has_resources(id), + } + } + 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!(), + TypeDefKind::ErrorContext => 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, + results: &mut Vec, + ) { + 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::>() + .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::>(); + let payloads = self + .payloads + .drain(self.payloads.len() - variant.cases.len()..) + .collect::>(); + + 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::>(); + 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 = 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(func.results.len(), 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 { + 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::>(); + + 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::AsyncMalloc { .. } => 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) { + 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/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 +#undef unexpected +#include + +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 +// . +/// + +#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 +#include +#include +#include + +#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 +#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 +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign + +// 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 + +// 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 +struct is_trivially_copy_constructible + : std::is_trivially_copy_constructible {}; +#ifdef _GLIBCXX_VECTOR +template +struct is_trivially_copy_constructible> : std::false_type {}; +#endif +} // namespace detail +} // namespace tl +#endif + +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#else +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible +#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 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 unexpected { +public: + static_assert(!std::is_same::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 ::value>::type * = nullptr> + constexpr explicit unexpected(Args &&...args) + : m_val(std::forward(args)...) {} + template < + class U, class... Args, + typename std::enable_if &, Args &&...>::value>::type * = nullptr> + constexpr explicit unexpected(std::initializer_list l, Args &&...args) + : m_val(l, std::forward(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 unexpected(E) -> unexpected; +#endif + +template +constexpr bool operator==(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() == rhs.value(); +} +template +constexpr bool operator!=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() != rhs.value(); +} +template +constexpr bool operator<(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() < rhs.value(); +} +template +constexpr bool operator<=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() <= rhs.value(); +} +template +constexpr bool operator>(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() > rhs.value(); +} +template +constexpr bool operator>=(const unexpected &lhs, const unexpected &rhs) { + return lhs.value() >= rhs.value(); +} + +template +unexpected::type> make_unexpected(E &&e) { + return unexpected::type>(std::forward(e)); +} + +struct unexpect_t { + unexpect_t() = default; +}; +static constexpr unexpect_t unexpect{}; + +namespace detail { +template +[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward(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 using remove_const_t = typename std::remove_const::type; +template +using remove_reference_t = typename std::remove_reference::type; +template using decay_t = typename std::decay::type; +template +using enable_if_t = typename std::enable_if::type; +template +using conditional_t = typename std::conditional::type; + +// std::conjunction from C++17 +template struct conjunction : std::true_type {}; +template struct conjunction : B {}; +template +struct conjunction + : std::conditional, 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 +struct is_pointer_to_non_const_member_func : std::false_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; +template +struct is_pointer_to_non_const_member_func + : std::true_type {}; + +template struct is_const_or_const_ref : std::false_type {}; +template struct is_const_or_const_ref : std::true_type {}; +template struct is_const_or_const_ref : 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::value && + is_const_or_const_ref::value)>, +#endif + typename = enable_if_t>::value>, int = 0> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::mem_fn(f)(std::forward(args)...))) + -> decltype(std::mem_fn(f)(std::forward(args)...)) { + return std::mem_fn(f)(std::forward(args)...); +} + +template >::value>> +constexpr auto invoke(Fn &&f, Args &&...args) noexcept( + noexcept(std::forward(f)(std::forward(args)...))) + -> decltype(std::forward(f)(std::forward(args)...)) { + return std::forward(f)(std::forward(args)...); +} + +// std::invoke_result from C++17 +template struct invoke_result_impl; + +template +struct invoke_result_impl< + F, + decltype(detail::invoke(std::declval(), std::declval()...), void()), + Us...> { + using type = + decltype(detail::invoke(std::declval(), std::declval()...)); +}; + +template +using invoke_result = invoke_result_impl; + +template +using invoke_result_t = typename invoke_result::type; + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template struct is_swappable : std::true_type {}; + +template 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 tag swap(T &, T &); +template 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 std::false_type can_swap(...) noexcept(false); +template (), std::declval()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), + std::declval()))); + +template std::false_type uses_std(...); +template +std::is_same(), std::declval())), tag> +uses_std(int); + +template +struct is_std_swap_noexcept + : std::integral_constant::value && + std::is_nothrow_move_assignable::value> {}; + +template +struct is_std_swap_noexcept : is_std_swap_noexcept {}; + +template +struct is_adl_swap_noexcept + : std::integral_constant(0))> {}; +} // namespace swap_adl_tests + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std(0))::value || + (std::is_move_assignable::value && + std::is_move_constructible::value))> {}; + +template +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap(0))::value && + (!decltype(detail::swap_adl_tests::uses_std( + 0))::value || + is_swappable::value)> {}; + +template +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable::value && + ((decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_std_swap_noexcept::value) || + (!decltype(detail::swap_adl_tests::uses_std(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept::value))> {}; +#endif +#endif + +// Trait for checking if a type is a tl::expected +template struct is_expected_impl : std::false_type {}; +template +struct is_expected_impl> : std::true_type {}; +template using is_expected = is_expected_impl>; + +template +using expected_enable_forward_value = detail::enable_if_t< + std::is_constructible::value && + !std::is_same, in_place_t>::value && + !std::is_same, detail::decay_t>::value && + !std::is_same, detail::decay_t>::value>; + +template +using expected_enable_from_other = detail::enable_if_t< + std::is_constructible::value && + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value>; + +template +using is_void_or = conditional_t::value, std::true_type, U>; + +template +using is_copy_constructible_or_void = + is_void_or>; + +template +using is_move_constructible_or_void = + is_void_or>; + +template +using is_copy_assignable_or_void = is_void_or>; + +template +using is_move_assignable_or_void = is_void_or>; + +} // 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 ::value, + bool = std::is_trivially_destructible::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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } else { + m_unexpect.~unexpected(); + } + } + union { + T m_val; + unexpected 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 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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// T is trivial, E is not. +template struct expected_storage_base { + 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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// E is trivial, T is not. +template 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 ::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&...args) + : m_val(std::forward(args)...), m_has_val(true) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list il, + Args &&...args) + : m_val(il, std::forward(args)...), m_has_val(true) {} + template ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } + } + union { + T m_val; + unexpected m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is trivially-destructible +template struct expected_storage_base { + #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 ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + struct dummy {}; + union { + unexpected m_unexpect; + dummy m_val; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is not trivially-destructible +template struct expected_storage_base { + 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 ::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&...args) + : m_unexpect(std::forward(args)...), m_has_val(false) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list il, + Args &&...args) + : m_unexpect(il, std::forward(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected(); + } + } + + union { + unexpected 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 +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template void construct(Args &&...args) noexcept { + new (std::addressof(this->m_val)) T(std::forward(args)...); + this->m_has_val = true; + } + + template void construct_with(Rhs &&rhs) noexcept { + new (std::addressof(this->m_val)) T(std::forward(rhs).get()); + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(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 ::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + 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 ::value && + std::is_nothrow_move_constructible::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(); + 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 ::value && + !std::is_nothrow_move_constructible::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(); + +#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 ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + + template ::value> + * = nullptr> + void assign(expected_operations_base &&rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected(); +#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(); + 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(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + +#endif + + // The common part of move/copy assigning + template void assign_common(Rhs &&rhs) { + if (this->m_has_val) { + if (rhs.m_has_val) { + get() = std::forward(rhs).get(); + } else { + destroy_val(); + construct_error(std::forward(rhs).geterr()); + } + } else { + if (!rhs.m_has_val) { + geterr() = std::forward(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 &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&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 +struct expected_operations_base : expected_storage_base { + using expected_storage_base::expected_storage_base; + + template 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 void construct_with(Rhs &&) noexcept { + this->m_has_val = true; + } + + template void construct_error(Args &&...args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected(std::forward(args)...); + this->m_has_val = false; + } + + template void assign(Rhs &&rhs) noexcept { + if (!this->m_has_val) { + if (rhs.m_has_val) { + geterr().~unexpected(); + construct(); + } else { + geterr() = std::forward(rhs).geterr(); + } + } else { + if (!rhs.m_has_val) { + construct_error(std::forward(rhs).geterr()); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR unexpected &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected &&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 :: + value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; +}; + +// This specialization is for when T or E are not trivially copy constructible +template +struct expected_copy_base : expected_operations_base { + using expected_operations_base::expected_operations_base; + + expected_copy_base() = default; + expected_copy_base(const expected_copy_base &rhs) + : expected_operations_base(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 >::value + &&std::is_trivially_move_constructible::value> +struct expected_move_base : expected_copy_base { + using expected_copy_base::expected_copy_base; +}; +#else +template struct expected_move_base; +#endif +template +struct expected_move_base : expected_copy_base { + using expected_copy_base::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::value) + : expected_copy_base(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 >::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 { + using expected_move_base::expected_move_base; +}; + +template +struct expected_copy_assign_base : expected_move_base { + using expected_move_base::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 , + std::is_trivially_move_constructible, + std::is_trivially_move_assignable>>:: + value &&std::is_trivially_destructible::value + &&std::is_trivially_move_constructible::value + &&std::is_trivially_move_assignable::value> +struct expected_move_assign_base : expected_copy_assign_base { + using expected_copy_assign_base::expected_copy_assign_base; +}; +#else +template struct expected_move_assign_base; +#endif + +template +struct expected_move_assign_base + : expected_copy_assign_base { + using expected_copy_assign_base::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::value + &&std::is_nothrow_move_assignable::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 ::value && + std::is_copy_constructible::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::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 +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 = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template +struct expected_delete_ctor_base { + 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 +struct expected_delete_ctor_base { + 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 ::value && + std::is_copy_constructible::value && + is_copy_assignable_or_void::value && + std::is_copy_assignable::value), + bool EnableMove = (is_move_constructible_or_void::value && + std::is_move_constructible::value && + is_move_assignable_or_void::value && + std::is_move_assignable::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 +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 = delete; +}; + +template +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 &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template +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 &) = 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 ::value || std::is_void::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 struct expected_default_ctor_base { + 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 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` 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 expected : private detail::expected_move_assign_base, + private detail::expected_delete_ctor_base, + private detail::expected_delete_assign_base, + private detail::expected_default_ctor_base { + static_assert(!std::is_reference::value, "T must not be a reference"); + static_assert(!std::is_same::type>::value, + "T must not be in_place_t"); + static_assert(!std::is_same::type>::value, + "T must not be unexpect_t"); + static_assert( + !std::is_same>::type>::value, + "T must not be unexpected"); + static_assert(!std::is_reference::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 *errptr() { return std::addressof(this->m_unexpect); } + const unexpected *errptr() const { + return std::addressof(this->m_unexpect); + } + + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &val() { + return this->m_val; + } + TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } + + template ::value> * = nullptr> + constexpr const U &val() const { + return this->m_val; + } + constexpr const unexpected &err() const { return this->m_unexpect; } + + using impl_base = detail::expected_move_assign_base; + using ctor_base = detail::expected_default_ctor_base; + +public: + typedef T value_type; + typedef E error_type; + typedef unexpected unexpected_type; + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { + return and_then_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + return and_then_impl(std::move(*this), std::forward(f)); + } + template constexpr auto and_then(F &&f) const & { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template constexpr auto and_then(F &&f) const && { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif + +#else + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) & -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) && -> decltype(and_then_impl(std::declval(), + std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } + template + constexpr auto and_then(F &&f) const & -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval(), std::forward(f))) { + return and_then_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + map(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + template constexpr auto transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval(), std::declval())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const & { + return expected_map_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(expected_map_impl(std::declval(), + std::declval())) + transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template constexpr auto transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + template constexpr auto transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#else + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) & { + return map_error_impl(*this, std::forward(f)); + } + template + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward(f)); + } + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const & { + return map_error_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template + constexpr decltype(map_error_impl(std::declval(), + std::declval())) + transform_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward(f)); + } +#endif +#endif + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + return or_else_impl(*this, std::forward(f)); + } + + template expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + return or_else_impl(std::move(*this), std::forward(f)); + } + + template expected constexpr or_else(F &&f) const & { + return or_else_impl(*this, std::forward(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template expected constexpr or_else(F &&f) const && { + return or_else_impl(std::move(*this), std::forward(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 ::value> * = + nullptr> + constexpr expected(in_place_t, Args &&...args) + : impl_base(in_place, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr expected(in_place_t, std::initializer_list il, Args &&...args) + : impl_base(in_place, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr, + detail::enable_if_t::value> * = + nullptr> + explicit constexpr expected(const unexpected &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected const &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + explicit constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t::value> * = nullptr> + constexpr expected(unexpected &&e) noexcept( + std::is_nothrow_constructible::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value> * = + nullptr> + constexpr explicit expected(unexpect_t, Args &&...args) + : impl_base(unexpect, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template &, Args &&...>::value> * = nullptr> + constexpr explicit expected(unexpect_t, std::initializer_list il, + Args &&...args) + : impl_base(unexpect, il, std::forward(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(const expected &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template ::value && + std::is_convertible::value)> * = + nullptr, + detail::expected_enable_from_other + * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(const expected &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::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(expected &&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::value && + std::is_convertible::value)> * = nullptr, + detail::expected_enable_from_other * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(expected &&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::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, + detail::enable_if_t::value> * = nullptr, + detail::expected_enable_forward_value * = nullptr> + TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward(v)) {} + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + err().~unexpected(); + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } + + return *this; + } + + template < + class U = T, class G = T, + detail::enable_if_t::value> * = + nullptr, + detail::enable_if_t::value> * = nullptr, + detail::enable_if_t< + (!std::is_same, detail::decay_t>::value && + !detail::conjunction, + std::is_same>>::value && + std::is_constructible::value && + std::is_assignable::value && + std::is_nothrow_move_constructible::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward(v); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(v)); + this->m_has_val = true; +#endif + } + + return *this; + } + + template ::value && + std::is_assignable::value> * = nullptr> + expected &operator=(const unexpected &rhs) { + if (!has_value()) { + err() = rhs; + } else { + this->destroy_val(); + ::new (errptr()) unexpected(rhs); + this->m_has_val = false; + } + + return *this; + } + + template ::value && + std::is_move_assignable::value> * = nullptr> + expected &operator=(unexpected &&rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } else { + this->destroy_val(); + ::new (errptr()) unexpected(std::move(rhs)); + this->m_has_val = false; + } + + return *this; + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + } else { + err().~unexpected(); + this->m_has_val = true; + } + ::new (valptr()) T(std::forward(args)...); + } + + template ::value> * = nullptr> + void emplace(Args &&...args) { + if (has_value()) { + val().~T(); + ::new (valptr()) T(std::forward(args)...); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(std::forward(args)...); + this->m_has_val = true; +#endif + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + err().~unexpected(); + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } + } + + template &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list il, Args &&...args) { + if (has_value()) { + T t(il, std::forward(args)...); + val() = std::move(t); + } else { + auto tmp = std::move(err()); + err().~unexpected(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(il, std::forward(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } +#else + ::new (valptr()) T(il, std::forward(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::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::type{}, + typename std::is_nothrow_move_constructible::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 + detail::enable_if_t::value && + detail::is_swappable::value && + (std::is_nothrow_move_constructible::value || + std::is_nothrow_move_constructible::value)> + swap(expected &rhs) noexcept( + std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value + &&std::is_nothrow_move_constructible::value + &&detail::is_nothrow_swappable::value) { + if (has_value() && rhs.has_value()) { + swap_where_both_have_value(rhs, typename std::is_void::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::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 ::value> * = nullptr> + constexpr const U &operator*() const & { + TL_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { + TL_ASSERT(has_value()); + return val(); + } + template ::value> * = nullptr> + constexpr const U &&operator*() const && { + TL_ASSERT(has_value()); + return std::move(val()); + } + template ::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 ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access(err().value())); + return val(); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access(std::move(err()).value())); + return std::move(val()); + } + template ::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access(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 constexpr T value_or(U &&v) const & { + static_assert(std::is_copy_constructible::value && + std::is_convertible::value, + "T must be copy-constructible and convertible to from U&&"); + return bool(*this) ? **this : static_cast(std::forward(v)); + } + template TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + static_assert(std::is_move_constructible::value && + std::is_convertible::value, + "T must be move-constructible and convertible to from U&&"); + return bool(*this) ? std::move(**this) : static_cast(std::forward(v)); + } +}; + +namespace detail { +template using exp_t = typename detail::decay_t::value_type; +template using err_t = typename detail::decay_t::error_type; +template using ret_t = expected>; + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#else +template struct TC; +template (), + *std::declval())), + detail::enable_if_t>::value> * = nullptr> +auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward(f), *std::forward(exp)) + : Ret(unexpect, std::forward(exp).error()); +} + +template ())), + detail::enable_if_t>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward(f)) + : Ret(unexpect, std::forward(exp).error()); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t>; + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected>; + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return result(); + } + + return result(unexpect, std::forward(exp).error()); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f), + *std::forward(exp))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + *std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f), *std::forward(exp)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t> { + using result = ret_t>; + + return exp.has_value() ? result(detail::invoke(std::forward(f))) + : result(unexpect, std::forward(exp).error()); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval())), + detail::enable_if_t::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected> { + if (exp.has_value()) { + detail::invoke(std::forward(f)); + return {}; + } + + return unexpected>(std::forward(exp).error()); +} +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, detail::decay_t>; + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#else +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result(*std::forward(exp)) + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(*std::forward(exp)); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected, detail::decay_t> { + using result = expected, detail::decay_t>; + + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward(f), + std::forward(exp).error())); +} + +template >::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval(), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected, monostate> { + using result = expected, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward(f), std::forward(exp).error()); + return result(unexpect, monostate{}); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +constexpr auto or_else_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#else +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +auto or_else_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected::value, "F must return an expected"); + return exp.has_value() ? std::forward(exp) + : detail::invoke(std::forward(f), + std::forward(exp).error()); +} + +template (), + std::declval().error())), + detail::enable_if_t::value> * = nullptr> +detail::decay_t or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() ? std::forward(exp) + : (detail::invoke(std::forward(f), + std::forward(exp).error()), + std::forward(exp)); +} +#endif +} // namespace detail + +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); +} +template +constexpr bool operator==(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : true); +} +template +constexpr bool operator!=(const expected &lhs, + const expected &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() == rhs.error() : false); +} + +template +constexpr bool operator==(const expected &x, const U &v) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator==(const U &v, const expected &x) { + return x.has_value() ? *x == v : false; +} +template +constexpr bool operator!=(const expected &x, const U &v) { + return x.has_value() ? *x != v : true; +} +template +constexpr bool operator!=(const U &v, const expected &x) { + return x.has_value() ? *x != v : true; +} + +template +constexpr bool operator==(const expected &x, const unexpected &e) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator==(const unexpected &e, const expected &x) { + return x.has_value() ? false : x.error() == e.value(); +} +template +constexpr bool operator!=(const expected &x, const unexpected &e) { + return x.has_value() ? true : x.error() != e.value(); +} +template +constexpr bool operator!=(const unexpected &e, const expected &x) { + return x.has_value() ? true : x.error() != e.value(); +} + +template ::value || + std::is_move_constructible::value) && + detail::is_swappable::value && + std::is_move_constructible::value && + detail::is_swappable::value> * = nullptr> +void swap(expected &lhs, + expected &rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} +} // namespace tl + +#endif 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/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs index 2d1499a29..e232c15f6 100644 --- a/src/bin/wit-bindgen.rs +++ b/src/bin/wit-bindgen.rs @@ -46,6 +46,14 @@ enum Opt { #[clap(flatten)] args: Common, }, + /// Generates bindings for C++ 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")] @@ -128,6 +136,8 @@ fn main() -> Result<()> { Opt::Moonbit { opts, args } => (opts.build(), args), #[cfg(feature = "c")] Opt::C { 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")] diff --git a/tests/runtime/lists/test.new.cpp b/tests/runtime/lists/test.new.cpp new file mode 100644 index 000000000..0e4481a6d --- /dev/null +++ b/tests/runtime/lists/test.new.cpp @@ -0,0 +1,185 @@ +#include +#include +#include +#include + +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 +static bool equal(T const&a, S const& b) { + return a == b; +} +template +static bool equal(wit::span const&a, wit::span const& b) { + if (a.size() != b.size()) { return false; } + for (uint32_t i = 0; i +static bool equal(wit::vector const&a, wit::span const& b) { + return equal(a.get_view(), b); +} +template +static bool equal(wit::span const&a, wit::vector const& b) { + return equal(b, a); +} +template +static bool equal(wit::span const&a, std::vector const& b) { + return equal(a, wit::span(b)); +} +template +static bool equal(wit::vector const&a, std::vector const& b) { + return equal(a.get_view(), wit::span(b)); +} +static bool equal(wit::vector const&a, std::vector const& b) { + return equal(a.get_view(), wit::span(b)); +} +template +static bool equal(std::tuple const&a, std::tuple 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(std::vector())); + test::lists::test::EmptyStringParam(""); + assert(test::lists::test::EmptyListResult().empty()); + assert(test::lists::test::EmptyStringResult().empty()); + + test::lists::test::ListParam(std::vector{1, 2, 3, 4}); + test::lists::test::ListParam2("foo"); + test::lists::test::ListParam3(std::vector{"foo", "bar", "baz"}); + test::lists::test::ListParam4(std::vector>{ + std::vector{"foo", "bar"}, + std::vector{"baz"}, + }); + assert(equal(test::lists::test::ListResult(), std::vector{1, 2, 3, 4, 5})); + assert(equal(test::lists::test::ListResult2(), "hello!")); + assert(equal(test::lists::test::ListResult3(), std::vector{"hello,", "world!"})); + + assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector())), std::vector())); + assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'x'})), std::vector{'x'})); + assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'h', 'e', 'l', 'l', 'o'})), std::vector{'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{0, UINT8_MAX}, std::vector{INT8_MIN, INT8_MAX}), + std::make_tuple(std::vector{0, UINT8_MAX}, std::vector{INT8_MIN, INT8_MAX}) + )); + assert(equal( + test::lists::test::ListMinmax16(std::vector{0, UINT16_MAX}, std::vector{INT16_MIN, INT16_MAX}), + std::make_tuple(std::vector{0, UINT16_MAX}, std::vector{INT16_MIN, INT16_MAX}) + )); + assert(equal( + test::lists::test::ListMinmax32(std::vector{0, UINT32_MAX}, std::vector{INT32_MIN, INT32_MAX}), + std::make_tuple(std::vector{0, UINT32_MAX}, std::vector{INT32_MIN, INT32_MAX}) + )); + assert(equal( + test::lists::test::ListMinmax64(std::vector{0, UINT64_MAX}, std::vector{INT64_MIN, INT64_MAX}), + std::make_tuple(std::vector{0, UINT64_MAX}, std::vector{INT64_MIN, INT64_MAX}) + )); + assert(equal( + test::lists::test::ListMinmaxFloat( + std::vector{FLT_MIN, FLT_MAX, -HUGE_VALF, HUGE_VALF}, + std::vector{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL} + ), + std::make_tuple( + std::vector{FLT_MIN, FLT_MAX, -HUGE_VALF, HUGE_VALF}, + std::vector{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL} + ) + )); +} + + +void exports::test::lists::test::EmptyListParam(wit::span a) { + assert(a.empty()); +} + +void exports::test::lists::test::EmptyStringParam(std::string_view a) { + assert(a.empty()); +} + +wit::vector exports::test::lists::test::EmptyListResult() { + return wit::vector(); +} + +wit::string exports::test::lists::test::EmptyStringResult() { + return wit::string::from_view(std::string_view()); +} + +void exports::test::lists::test::ListParam(wit::span list) { + assert(equal(list, std::vector{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 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> 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 exports::test::lists::test::ListResult() { + return wit::vector::from_view(wit::span(std::vector{1, 2, 3, 4, 5})); +} + +wit::string exports::test::lists::test::ListResult2() { + return wit::string::from_view("hello!"); +} + +wit::vector exports::test::lists::test::ListResult3() { + return wit::vector::from_view(wit::span(std::vector{wit::string::from_view("hello,"), wit::string::from_view("world!")})); +} + +wit::vector exports::test::lists::test::ListRoundtrip(wit::span x) { + return wit::vector::from_view(x); +} + +wit::string exports::test::lists::test::StringRoundtrip(std::string_view x) { + return wit::string::from_view(x); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmax8(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmax16(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmax32(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmax64(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} + +std::tuple, wit::vector> exports::test::lists::test::ListMinmaxFloat(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} 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 +#include + +template +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/test.cpp b/tests/runtime/numbers/test.cpp new file mode 100644 index 000000000..e1cc759d6 --- /dev/null +++ b/tests/runtime/numbers/test.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +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/test.new.cpp b/tests/runtime/options/test.new.cpp new file mode 100644 index 000000000..8925a3f74 --- /dev/null +++ b/tests/runtime/options/test.new.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +template +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 const& a, std::optional 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()); + OptionSomeParam(std::optional("foo")); + assert(!OptionNoneResult()); + assert(equal(OptionSomeResult(), std::optional("foo"))); + assert(equal(OptionRoundtrip(std::optional("foo")), std::optional("foo"))); + assert(equal(DoubleOptionRoundtrip(std::optional>(std::optional(42))), std::optional>(std::optional(42)))); + assert(equal(DoubleOptionRoundtrip(std::optional>(std::optional())), std::optional>(std::optional()))); + assert(equal(DoubleOptionRoundtrip(std::optional>()), std::optional>())); +} + +void exports::test::options::test::OptionNoneParam(std::optional a) +{ + assert(!a.has_value()); +} + +std::optional exports::test::options::test::OptionNoneResult() { + return std::optional(); +} + +void exports::test::options::test::OptionSomeParam(std::optional a) { + assert(equal(a, std::optional("foo"))); +} + +std::optional exports::test::options::test::OptionSomeResult() { + return std::optional(wit::string::from_view("foo")); +} + +std::optional exports::test::options::test::OptionRoundtrip(std::optional a) { + if (!a.has_value()) return std::optional(); + return std::optional(wit::string::from_view(*a)); +} + +std::optional> exports::test::options::test::DoubleOptionRoundtrip(std::optional> a) { + return a; +} diff --git a/tests/runtime/records/test.new.cpp b/tests/runtime/records/test.new.cpp new file mode 100644 index 000000000..d5b276f46 --- /dev/null +++ b/tests/runtime/records/test.new.cpp @@ -0,0 +1,75 @@ +#include +#include + +template +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(4, 5))); + + assert(equal(SwapTuple(std::tuple(1, 2)), std::tuple(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(1)), std::tuple(1))); +} + +std::tuple exports::test::records::test::MultipleResults() { + return std::tuple(100, 200); +} + +std::tuple exports::test::records::test::SwapTuple(std::tuple a) { + return std::tuple(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 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 exports::test::records::test::Tuple1(std::tuple a) { + return std::tuple(std::get<0>(a)); +} diff --git a/tests/runtime/results/test.new.cpp b/tests/runtime/results/test.new.cpp new file mode 100644 index 000000000..68ea1e46a --- /dev/null +++ b/tests/runtime/results/test.new.cpp @@ -0,0 +1,56 @@ +#include +#include + +template +bool equal(T const&a, T const&b) { + return a==b; +} + +std::expected exports::test::results::test::StringError(float a) { + return ::test::results::test::StringError(a); +} + +std::expected 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 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 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 exports::test::results::test::EmptyError(uint32_t a) { + return ::test::results::test::EmptyError(a); +} + +std::expected, wit::string> exports::test::results::test::DoubleError(uint32_t a) { + return ::test::results::test::DoubleError(a); +} diff --git a/tests/runtime/strings/test.cpp b/tests/runtime/strings/test.cpp new file mode 100644 index 000000000..ecf43f653 --- /dev/null +++ b/tests/runtime/strings/test.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include + +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/test.new.cpp b/tests/runtime/strings/test.new.cpp new file mode 100644 index 000000000..966189074 --- /dev/null +++ b/tests/runtime/strings/test.new.cpp @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +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); +} From e55da993fe172eae9b538acf3e4b1b4960150d38 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 21 Apr 2025 00:11:27 +0200 Subject: [PATCH 572/672] reduce code (no host, no symmetric) --- crates/c/src/lib.rs | 2 +- crates/cpp/src/lib.rs | 644 ++++++++---------------------------------- 2 files changed, 124 insertions(+), 522 deletions(-) diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index dc331a794..3203a3c1b 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::*; diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index afafbb820..9aeb4adaf 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -9,16 +9,15 @@ use std::{ 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, + uwrite, uwriteln, wit_parser::{ Alignment, ArchitectureSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, - Resolve, Results, SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, - WorldKey, + Resolve, SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, }, Files, InterfaceGenerator, Source, WorldGenerator, }; -mod wamr; +// mod wamr; pub const RESOURCE_IMPORT_BASE_CLASS_NAME: &str = "ResourceImportBase"; pub const RESOURCE_EXPORT_BASE_CLASS_NAME: &str = "ResourceExportBase"; @@ -181,18 +180,9 @@ impl core::fmt::Display for Ownership { #[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 @@ -227,11 +217,6 @@ pub struct Opts { #[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()))] @@ -245,30 +230,12 @@ impl Opts { 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) + false == 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), - } + "int32_t" } } @@ -298,11 +265,7 @@ impl Cpp { in_guest_import: bool, wasm_import_module: Option, ) -> CppInterfaceGenerator<'a> { - let mut sizes = if self.opts.symmetric { - SizeAlign::new_symmetric() - } else { - SizeAlign::default() - }; + let mut sizes = SizeAlign::default(); sizes.fill(resolve); CppInterfaceGenerator { @@ -408,11 +371,7 @@ impl Cpp { self.include(""); } if self.dependencies.needs_wit { - if self.opts.host_side() { - self.include(""); - } else { - self.include(""); - } + self.include(""); } if self.dependencies.needs_memory { self.include(""); @@ -438,9 +397,6 @@ impl Cpp { 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}"); } @@ -473,10 +429,9 @@ impl WorldGenerator for Cpp { 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" + uwriteln!( + self.c_src_head, + r#"#include "{}_cpp.h" #include // realloc extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size); @@ -491,9 +446,8 @@ impl WorldGenerator for Cpp { }} "#, - self.world.to_snake_case(), - ); - } + self.world.to_snake_case(), + ); } fn import_interface( @@ -642,36 +596,14 @@ impl WorldGenerator for Cpp { "// 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(), - ); - } + uwrite!( + h_str.src, + "#ifndef __CPP_GUEST_BINDINGS_{0}_H + #define __CPP_GUEST_BINDINGS_{0}_H\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}"); } @@ -680,48 +612,20 @@ impl WorldGenerator for Cpp { 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__ + 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-micro-runtime header\n\ - #include \n\ - #include " - ); - - 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 std::map wit::{RESOURCE_TABLE_NAME}::resources;" - ); - } + ); if self.dependencies.needs_assert { uwriteln!(c_str.src, "#include "); } @@ -737,40 +641,6 @@ impl WorldGenerator for Cpp { 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, " @@ -782,16 +652,8 @@ impl WorldGenerator for Cpp { 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()); - } + files.push(&format!("{snake}.cpp"), c_str.src.as_bytes()); + files.push(&format!("{snake}_cpp.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)) { @@ -814,30 +676,6 @@ impl WorldGenerator for Cpp { ); 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) @@ -956,7 +794,6 @@ impl CppInterfaceGenerator<'_> { TypeDefKind::Stream(_) => todo!("generate for stream"), TypeDefKind::Handle(_) => todo!("generate for handle"), TypeDefKind::Unknown => unreachable!(), - TypeDefKind::ErrorContext => todo!(), } } @@ -972,6 +809,9 @@ impl CppInterfaceGenerator<'_> { 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]; @@ -996,21 +836,13 @@ impl CppInterfaceGenerator<'_> { } else { match is_drop { SpecialMethod::ResourceDrop => { - if self.gen.opts.host_side() && !guest_export { - "Dtor".to_string() - } else if guest_export { + 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::Dtor => "Dtor".to_string(), SpecialMethod::ResourceNew => "ResourceNew".to_string(), SpecialMethod::ResourceRep => "ResourceRep".to_string(), SpecialMethod::Allocate => "New".to_string(), @@ -1024,34 +856,10 @@ impl CppInterfaceGenerator<'_> { (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 { let is_drop = is_special_method(func); - let id_type = if self.gen.opts.symmetric { - WasmType::Pointer - } else { - WasmType::I32 - }; + let id_type = WasmType::I32; let signature = match is_drop { SpecialMethod::ResourceDrop => WasmSignature { params: vec![id_type], @@ -1079,7 +887,7 @@ impl CppInterfaceGenerator<'_> { }, SpecialMethod::None => { // TODO perhaps remember better names for the arguments - self.patched_wasm_signature(variant, func) + self.wasm_signature(variant, func) } SpecialMethod::Allocate => WasmSignature { params: vec![], @@ -1090,10 +898,6 @@ impl CppInterfaceGenerator<'_> { }; 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, @@ -1103,36 +907,18 @@ impl CppInterfaceGenerator<'_> { ) { 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(); + let func_name = func.name.clone(); + 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, + r#"extern "C" __attribute__((__export_name__("{module_prefix}{func_name}")))"# + ); + let return_via_pointer = false; self.gen .c_src .src @@ -1152,10 +938,6 @@ impl CppInterfaceGenerator<'_> { 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}"); @@ -1181,19 +963,6 @@ impl CppInterfaceGenerator<'_> { 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 } @@ -1218,17 +987,10 @@ impl CppInterfaceGenerator<'_> { 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()) + && matches!(abi_variant, AbiVariant::GuestImport)) { match &func.results { wit_bindgen_core::wit_parser::Results::Named(n) => { @@ -1272,55 +1034,25 @@ impl CppInterfaceGenerator<'_> { } 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()) + && matches!(abi_variant, AbiVariant::GuestImport)) { 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())) + if i == 0 && name == "self" && matches!(&func.kind, FunctionKind::Method(_)) + || (matches!(&is_drop, SpecialMethod::ResourceDrop) + && matches!(abi_variant, AbiVariant::GuestImport)) { 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)), - )); - } + 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(); + let import = matches!(abi_variant, AbiVariant::GuestImport); if matches!(func.kind, FunctionKind::Method(_)) && import { res.const_member = true; } @@ -2523,10 +2255,6 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> fn type_stream(&mut self, _id: TypeId, _name: &str, _ty: &Option, _docs: &Docs) { todo!() } - - fn type_error_context(&mut self, _id: TypeId, _name: &str, _docs: &Docs) { - todo!() - } } struct CabiPostInformation { @@ -2548,7 +2276,6 @@ struct FunctionBindgen<'a, 'b> { blocks: Vec<(String, Vec)>, payloads: Vec, // caching for wasm - wamr_signature: Option, variant: AbiVariant, cabi_post: Option, needs_dealloc: bool, @@ -2568,7 +2295,6 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { block_storage: Default::default(), blocks: Default::default(), payloads: Default::default(), - wamr_signature: None, variant: AbiVariant::GuestImport, cabi_post: None, needs_dealloc: false, @@ -2618,16 +2344,12 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { operands: &[String], results: &mut Vec, ) { - 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) - )); - } + results.push(format!( + "*(({}*) ({} + {}))", + ty, + operands[0], + offset.format(POINTER_SIZE_EXPRESSION) + )); } fn load_ext( @@ -2643,25 +2365,14 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } 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] - ); - } + uwriteln!( + self.src, + "*(({}*)({} + {})) = {};", + ty, + operands[1], + offset.format(POINTER_SIZE_EXPRESSION), + operands[0] + ); } fn has_resources2(&self, ty: &Type) -> bool { @@ -2678,8 +2389,9 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { | Type::F32 | Type::F64 | Type::Char => false, - Type::String => false, // correct? + Type::String => false, Type::Id(id) => self.has_resources(id), + Type::ErrorContext => todo!(), } } fn has_resources(&self, id: &TypeId) -> bool { @@ -2701,7 +2413,6 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { _ => false, }, TypeDefKind::Unknown => todo!(), - TypeDefKind::ErrorContext => todo!(), } } } @@ -2723,7 +2434,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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 { + if self.gen.in_guest_import { results.push("(*this)".to_string()); } else { results.push("(*lookup_resource(self))".to_string()); @@ -2815,25 +2526,17 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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)); - } + 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)) - { + if matches!(self.variant, AbiVariant::GuestImport) { uwriteln!(self.src, "{}.leak();\n", operands[0]); } results.push(ptr); @@ -2847,25 +2550,17 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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)); - } + 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)) - { + if matches!(self.variant, AbiVariant::GuestImport) { uwriteln!(self.src, "{}.leak();\n", operands[0]); } results.push(ptr); @@ -2881,25 +2576,17 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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)); - } + 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)) - { + if matches!(self.variant, AbiVariant::GuestImport) { uwriteln!(self.src, "{}.leak();\n", operands[0]); } results.push(ptr); @@ -2913,23 +2600,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { .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 + let result = 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] - ) - } + format!( + "wit::vector<{inner} const>(({inner}*)({}), {len}).get_view()", + operands[0] + ) } else { format!("wit::vector<{inner}>(({inner}*)({}), {len})", operands[0]) }; @@ -2939,34 +2616,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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]) - }; + let result = format!("wit::string((char const*)({}), {len})", operands[0]); results.push(result); } abi::Instruction::ListLift { element, .. } => { @@ -2997,10 +2647,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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 - { + if self.gen.gen.opts.new_api && matches!(self.variant, AbiVariant::GuestExport) { assert!(self.needs_dealloc); self.push_str(&format!("if ({len}>0) _deallocate.push_back({base});\n")); } @@ -3020,18 +2667,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // 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)) + if 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" @@ -3069,21 +2707,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { .. } => { 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()")); - } + if matches!(self.variant, AbiVariant::GuestImport) { + results.push(format!("{op}.into_handle()")); } else { - if matches!(self.variant, AbiVariant::GuestImport) { - results.push(format!("{op}.into_handle()")); - } else { - results.push(format!("{op}.release()->handle")); - } + results.push(format!("{op}.release()->handle")); } } abi::Instruction::HandleLower { @@ -3091,13 +2718,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { .. } => { 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)" { + if op == "(*this)" { // TODO is there a better way to decide? results.push(format!("{op}.get_handle()")); } else { @@ -3106,7 +2727,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } abi::Instruction::HandleLift { handle, .. } => { let op = &operands[0]; - match (handle, self.gen.gen.opts.host_side()) { + match (handle, false) { (Handle::Own(ty), true) => match self.variant { AbiVariant::GuestExport => { results.push(format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}{{{op}}}")) @@ -3146,9 +2767,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src, "auto {var} = {tname}::Owned({tname}::ResourceRep({op}));" ); - if !self.gen.gen.opts.symmetric { - uwriteln!(self.src, "{var}->into_handle();"); - } + + uwriteln!(self.src, "{var}->into_handle();"); + results.push(format!("std::move({var})")) } AbiVariant::GuestImportAsync => todo!(), @@ -3749,7 +3370,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } else { self.src.push_str("std::tuple<"); - if let Results::Named(params) = &func.results { + if let Some(params) = &func.result { for (num, (_name, ty)) in params.iter().enumerate() { if num > 0 { self.src.push_str(", "); @@ -3769,35 +3390,20 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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)); - } + 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(".into_handle()"); } self.src.push_str(";\n"); } @@ -3872,10 +3478,6 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::StreamLift { .. } => todo!(), abi::Instruction::ErrorContextLower { .. } => todo!(), abi::Instruction::ErrorContextLift { .. } => todo!(), - abi::Instruction::AsyncMalloc { .. } => 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(); From 1547c534a1ad9b9e54608113a6374ebf5b23447d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 21 Apr 2025 17:59:46 +0200 Subject: [PATCH 573/672] c++ generator compiles --- crates/c/src/lib.rs | 2 +- crates/cpp/src/lib.rs | 744 +++++++++------------------------- crates/cpp/src/symbol_name.rs | 50 +++ 3 files changed, 244 insertions(+), 552 deletions(-) create mode 100644 crates/cpp/src/symbol_name.rs diff --git a/crates/c/src/lib.rs b/crates/c/src/lib.rs index 3203a3c1b..08c2e83a7 100644 --- a/crates/c/src/lib.rs +++ b/crates/c/src/lib.rs @@ -3821,7 +3821,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", diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 9aeb4adaf..2427d52b3 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -6,6 +6,7 @@ use std::{ process::{Command, Stdio}, str::FromStr, }; +use symbol_name::{make_external_component, make_external_symbol}; use wit_bindgen_c::to_c_ident; use wit_bindgen_core::{ abi::{self, AbiVariant, Bindgen, Bitcast, LiftLower, WasmSignature, WasmType}, @@ -18,6 +19,7 @@ use wit_bindgen_core::{ }; // mod wamr; +mod symbol_name; pub const RESOURCE_IMPORT_BASE_CLASS_NAME: &str = "ResourceImportBase"; pub const RESOURCE_EXPORT_BASE_CLASS_NAME: &str = "ResourceExportBase"; @@ -71,7 +73,7 @@ struct Includes { needs_string_view: bool, needs_optional: bool, needs_cstring: bool, - needs_guest_alloc: bool, + // needs_guest_alloc: bool, needs_imported_resources: bool, needs_exported_resources: bool, needs_variant: bool, @@ -82,13 +84,6 @@ struct Includes { needs_memory: bool, } -#[derive(Clone)] -struct HostFunction { - wasm_name: String, - wamr_signature: String, - host_name: String, -} - #[derive(Default)] struct SourceWithState { src: Source, @@ -112,7 +107,7 @@ struct Cpp { extern_c_decls: Source, dependencies: Includes, includes: Vec, - host_functions: HashMap>, + // host_functions: HashMap>, world: String, world_id: Option, imported_interfaces: HashSet, @@ -810,8 +805,8 @@ impl CppInterfaceGenerator<'_> { FunctionKind::Static(i) => Some(i), FunctionKind::Constructor(i) => Some(i), FunctionKind::AsyncFreestanding => todo!(), - FunctionKind::AsyncMethod(id) => todo!(), - FunctionKind::AsyncStatic(id) => todo!(), + FunctionKind::AsyncMethod(_id) => todo!(), + FunctionKind::AsyncStatic(_id) => todo!(), } .map(|i| { let ty = &self.resolve.types[*i]; @@ -887,7 +882,7 @@ impl CppInterfaceGenerator<'_> { }, SpecialMethod::None => { // TODO perhaps remember better names for the arguments - self.wasm_signature(variant, func) + self.resolve.wasm_signature(variant, func) } SpecialMethod::Allocate => WasmSignature { params: vec![], @@ -897,7 +892,7 @@ impl CppInterfaceGenerator<'_> { }, }; let mut module_name = self.wasm_import_module.as_ref().map(|e| e.clone()); - let mut symbol_variant = variant; + let symbol_variant = variant; if matches!(variant, AbiVariant::GuestExport) && matches!( is_drop, @@ -925,7 +920,7 @@ impl CppInterfaceGenerator<'_> { .push_str(if signature.results.is_empty() || return_via_pointer { "void" } else { - self.gen.opts.wasm_type(signature.results[0]) + wit_bindgen_c::wasm_type(signature.results[0]) }); self.gen.c_src.src.push_str(" "); let export_name = match module_name { @@ -946,7 +941,7 @@ impl CppInterfaceGenerator<'_> { } else { first_arg = false; } - self.gen.c_src.src.push_str(self.gen.opts.wasm_type(*ty)); + self.gen.c_src.src.push_str(wit_bindgen_c::wasm_type(*ty)); self.gen.c_src.src.push_str(" "); self.gen.c_src.src.push_str(&name); params.push(name); @@ -971,7 +966,7 @@ impl CppInterfaceGenerator<'_> { func: &Function, abi_variant: AbiVariant, // import: bool, - from_namespace: &Vec, + _from_namespace: &Vec, ) -> HighlevelSignature { let mut res = HighlevelSignature::default(); // let abi_variant = if import ^ self.gen.opts.host_side() { @@ -992,39 +987,14 @@ impl CppInterfaceGenerator<'_> { && !(matches!(is_drop, SpecialMethod::ResourceDrop) && matches!(abi_variant, AbiVariant::GuestImport)) { - match &func.results { - wit_bindgen_core::wit_parser::Results::Named(n) => { - if n.is_empty() { - res.result = "void".into(); - } else { - res.result = "std::tuple<".into(); - for (i, (_name, ty)) in n.iter().enumerate() { - if i > 0 { - res.result.push_str(", "); - } - res.result.push_str(&self.type_name( - ty, - &res.namespace, - Flavor::Result(abi_variant), - )); - } - res.result.push('>'); - } - } - wit_bindgen_core::wit_parser::Results::Anon(ty) => { - if matches!(is_drop, SpecialMethod::Allocate) { - res.result = OWNED_CLASS_NAME.into(); - } else { - res.result = - self.type_name(ty, from_namespace, Flavor::Result(abi_variant)); - if matches!( - is_drop, - SpecialMethod::Allocate | SpecialMethod::ResourceRep - ) { - res.result.push('*'); - } - } - } + if let Some(ty) = &func.result { + res.result.push_str(&self.type_name( + ty, + &res.namespace, + Flavor::Result(abi_variant), + )); + } else { + res.result = "void".into(); } if matches!(abi_variant, AbiVariant::GuestExport) && abi::guest_export_needs_post_return(self.resolve, func) @@ -1066,105 +1036,71 @@ impl CppInterfaceGenerator<'_> { import: bool, ) -> Vec { 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, - "{{\ + 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 "); + } + self.gen.h_src.src.push_str(&cpp_sig.result); + 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("("); + 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, false, &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::>() - .join(", ") - ); - // body is inside the header - return Vec::default(); - } - (SpecialMethod::Dtor, _, AbiVariant::GuestImport) - | (SpecialMethod::ResourceDrop, true, _) => { - uwrite!( - self.gen.h_src.src, - "{{\ + cpp_sig.namespace.last().unwrap(), //join("::"), + cpp_sig + .arguments + .iter() + .map(|(arg, _)| arg.clone()) + .collect::>() + .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"), + 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 - )) + if !import + && !matches!( + &is_special, + SpecialMethod::ResourceDrop + | SpecialMethod::ResourceNew + | SpecialMethod::ResourceRep + ) { self.print_export_signature(func, variant) } else { @@ -1172,25 +1108,13 @@ impl CppInterfaceGenerator<'_> { 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()); } @@ -1239,8 +1163,8 @@ impl CppInterfaceGenerator<'_> { } let export = match variant { - AbiVariant::GuestImport => self.gen.opts.host_side(), - AbiVariant::GuestExport => !self.gen.opts.host_side(), + AbiVariant::GuestImport => false, + AbiVariant::GuestExport => true, AbiVariant::GuestImportAsync => todo!(), AbiVariant::GuestExportAsync => todo!(), AbiVariant::GuestExportAsyncStackful => todo!(), @@ -1249,25 +1173,18 @@ impl CppInterfaceGenerator<'_> { 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 _deallocate;\n"); - self.gen.dependencies.needs_vector = true; - true - } else { - false - }; - let lift_lower = if self.gen.opts.symmetric { - LiftLower::Symmetric - } else if export { + let needs_dealloc = + if self.gen.opts.new_api && matches!(variant, AbiVariant::GuestExport) { + self.gen + .c_src + .src + .push_str("std::vector _deallocate;\n"); + self.gen.dependencies.needs_vector = true; + true + } else { + false + }; + let lift_lower = if export { LiftLower::LiftArgsLowerResults } else { LiftLower::LowerArgsLiftResults @@ -1275,177 +1192,66 @@ impl CppInterfaceGenerator<'_> { 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 - ); - } + 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) {{ + let name = + self.declare_import(&module_name, &func.name, &[WasmType::I32], &[]); + uwriteln!( + self.gen.c_src.src, + " if (handle>=0) {{ {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); - } - } + let classname = class_namespace(self, func, variant).join("::"); + 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));"); - } + 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 + ); } 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);",); - } + 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 + ); } SpecialMethod::Allocate => unreachable!(), SpecialMethod::None => { @@ -1463,6 +1269,9 @@ impl CppInterfaceGenerator<'_> { 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( @@ -1477,29 +1286,11 @@ impl CppInterfaceGenerator<'_> { 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.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 - }; + f.cabi_post = 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); @@ -1507,12 +1298,10 @@ impl CppInterfaceGenerator<'_> { } 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) + if matches!(variant, AbiVariant::GuestExport) && abi::guest_export_needs_post_return(self.resolve, func) { - let sig = self.patched_wasm_signature(variant, func); + let sig = self.resolve.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) => { @@ -1528,15 +1317,9 @@ impl CppInterfaceGenerator<'_> { }; //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 - }, - ), + Some(ref module_name) => { + make_external_symbol(module_name, &func.name, AbiVariant::GuestExport) + } None => make_external_component(&func.name), }; // make_external_symbol(&module_name, &func.name, AbiVariant::GuestExport); @@ -1557,24 +1340,15 @@ impl CppInterfaceGenerator<'_> { uwrite!( self.gen.c_src.src, "{} {name}", - self.gen.opts.wasm_type(*result) + wit_bindgen_c::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); + abi::post_return(f.gen.resolve, func, &mut f); let FunctionBindgen { src, .. } = f; self.gen.c_src.src.push_str(&src); self.gen.c_src.src.push_str("}\n"); @@ -1682,14 +1456,10 @@ impl CppInterfaceGenerator<'_> { self.gen.dependencies.needs_string_view = true; "std::string_view".into() } - Flavor::Argument(AbiVariant::GuestExport) if !self.gen.opts.host_side() => { + Flavor::Argument(AbiVariant::GuestExport) => { 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() @@ -1704,7 +1474,7 @@ impl CppInterfaceGenerator<'_> { } TypeDefKind::Handle(Handle::Own(id)) => { let mut typename = self.type_name(&Type::Id(*id), from_namespace, flavor); - match (self.gen.opts.host_side(), flavor) { + match (false, flavor) { (false, Flavor::Argument(AbiVariant::GuestImport)) | (true, Flavor::Argument(AbiVariant::GuestExport)) => { typename.push_str("&&") @@ -1773,14 +1543,10 @@ impl CppInterfaceGenerator<'_> { self.gen.dependencies.needs_wit = true; format!("wit::span<{inner} const>") } - Flavor::Argument(AbiVariant::GuestExport) if !self.gen.opts.host => { + Flavor::Argument(AbiVariant::GuestExport) => { 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}>") @@ -1791,8 +1557,8 @@ impl CppInterfaceGenerator<'_> { TypeDefKind::Stream(_) => todo!(), TypeDefKind::Type(ty) => self.type_name(ty, from_namespace, flavor), TypeDefKind::Unknown => todo!(), - TypeDefKind::ErrorContext => todo!(), }, + Type::ErrorContext => todo!(), } } @@ -1805,11 +1571,8 @@ impl CppInterfaceGenerator<'_> { 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") - }; + let import = format!("extern \"C\" __attribute__((import_module(\"{module_name}\")))\n __attribute__((import_name(\"{name}\")))\n {result} {extern_name}({args});\n") + ; (extern_name, import) } @@ -1822,7 +1585,7 @@ impl CppInterfaceGenerator<'_> { ) -> String { let mut args = String::default(); for (n, param) in params.iter().enumerate() { - args.push_str(self.gen.opts.wasm_type(*param)); + args.push_str(wit_bindgen_c::wasm_type(*param)); if n + 1 != params.len() { args.push_str(", "); } @@ -1830,13 +1593,9 @@ impl CppInterfaceGenerator<'_> { 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 + wit_bindgen_c::wasm_type(results[0]) }; + let variant = AbiVariant::GuestImport; let (name, code) = self.declare_import2(module_name, name, &args, result, variant); self.gen.extern_c_decls.push_str(&code); name @@ -1896,7 +1655,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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 definition = !(guest_import); let store = self.gen.start_new_file(Some(definition)); let mut world_name = self.gen.world.to_snake_case(); world_name.push_str("::"); @@ -1929,7 +1688,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> // for unique_ptr // self.gen.dependencies.needs_memory = true; - let base_type = match (definition, self.gen.opts.host_side()) { + let base_type = match (definition, false) { (true, false) => format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}<{pascal}>"), (false, false) => { String::from_str("wit::").unwrap() + RESOURCE_IMPORT_BASE_CLASS_NAME @@ -1968,7 +1727,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> name: name, kind: FunctionKind::Static(id), params: vec![("self".into(), Type::Id(id))], - results: Results::Named(vec![]), + result: None, docs: Docs::default(), stability: Stability::Unknown, }; @@ -1985,10 +1744,13 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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() + && matches!(variant, AbiVariant::GuestExport) != false { // functional safety requires the option to use a different allocator, so move new into the implementation let func2 = Function { @@ -1996,7 +1758,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> kind: FunctionKind::Static(id), // same params as constructor params: func.params.clone(), - results: Results::Anon(Type::Id(id)), + result: Some(Type::Id(id)), docs: Docs::default(), stability: Stability::Unknown, }; @@ -2020,16 +1782,12 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> ); } if matches!(variant, AbiVariant::GuestExport) { - let id_type = if self.gen.opts.symmetric { - Type::Id(id) - } else { - Type::S32 - }; + let id_type = Type::S32; let func = Function { name: "[resource-new]".to_string() + &name, kind: FunctionKind::Static(id), params: vec![("self".into(), Type::Id(id))], - results: Results::Anon(id_type), + result: Some(id_type), docs: Docs::default(), stability: Stability::Unknown, }; @@ -2039,7 +1797,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> name: "[resource-rep]".to_string() + &name, kind: FunctionKind::Static(id), params: vec![("id".into(), id_type)], - results: Results::Anon(Type::Id(id)), + result: Some(Type::Id(id)), docs: Docs::default(), stability: Stability::Unknown, }; @@ -2049,7 +1807,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> name: "[resource-drop]".to_string() + &name, kind: FunctionKind::Static(id), params: vec![("id".into(), id_type)], - results: Results::Named(vec![]), + result: None, docs: Docs::default(), stability: Stability::Unknown, }; @@ -2884,7 +2642,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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(wit_bindgen_c::wasm_type(*ty)); self.src.push_str(" "); self.src.push_str(&name); self.src.push_str(";\n"); @@ -2999,7 +2757,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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(wit_bindgen_c::wasm_type(*ty)); self.src.push_str(" "); self.src.push_str(&name); self.src.push_str(";\n"); @@ -3074,7 +2832,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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(wit_bindgen_c::wasm_type(*ty)); self.src.push_str(" "); self.src.push_str(&name); self.src.push_str(";\n"); @@ -3161,11 +2919,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { ); results.push(resultname); } - abi::Instruction::CallWasm { - name, - sig, - module_prefix, - } => { + abi::Instruction::CallWasm { name, sig } => { let module_name = self .gen .wasm_import_module @@ -3177,121 +2931,34 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { .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"); + 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(func.results.len(), results); - let (mut namespace, func_name_h) = - self.gen - .func_namespace_name(func, !self.gen.gen.opts.host_side(), true); + self.let_results(if func.result.is_some() { 1 } else { 0 }, results); + let (namespace, func_name_h) = self.gen.func_namespace_name(func, true, 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})->",); - } + //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); @@ -3299,20 +2966,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // 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(_)) @@ -3369,20 +3023,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src.push_str(&operands[0]); } } else { - self.src.push_str("std::tuple<"); - if let Some(params) = &func.result { - 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(")"); + todo!(); } if let Some(CabiPostInformation { module: func_module, @@ -3486,6 +3127,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { results.push(result); } } + abi::Instruction::AsyncTaskReturn { .. } => todo!(), } } diff --git a/crates/cpp/src/symbol_name.rs b/crates/cpp/src/symbol_name.rs new file mode 100644 index 000000000..4b71d1005 --- /dev/null +++ b/crates/cpp/src/symbol_name.rs @@ -0,0 +1,50 @@ +use wit_bindgen_core::abi; + +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 + } +} From e6a3e441cd3cabac19e3381b2e97d799dfcf29c0 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 28 Apr 2025 00:17:49 +0200 Subject: [PATCH 574/672] skeletal c++ testing support --- crates/cpp/src/lib.rs | 8 +- crates/test/src/c.rs | 2 +- crates/test/src/cpp.rs | 153 ++++++++++++++++++ crates/test/src/lib.rs | 6 +- tests/runtime/strings/runner.cpp | 19 +++ .../strings/{test.new.cpp => test_new.cpp} | 2 + .../strings/{test.cpp => test_old.cpp} | 2 + 7 files changed, 189 insertions(+), 3 deletions(-) create mode 100644 crates/test/src/cpp.rs create mode 100644 tests/runtime/strings/runner.cpp rename tests/runtime/strings/{test.new.cpp => test_new.cpp} (97%) rename tests/runtime/strings/{test.cpp => test_old.cpp} (97%) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 2427d52b3..cedc154e9 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -214,8 +214,14 @@ pub struct Opts { /// 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()))] + #[cfg_attr(feature = "clap", arg(long, default_value_t = true, overrides_with = "_old_api"))] pub new_api: bool, + + /// Asymmetric API: Imported functions borrow arguments (const&), + /// while exported functions received owned arguments (&&). + /// Reduces the allocation overhead for canonical ABI. + #[cfg_attr(feature = "clap", arg(long))] + pub _old_api: bool, } impl Opts { diff --git a/crates/test/src/c.rs b/crates/test/src/c.rs index 4f0e7b972..8ffb09cf5 100644 --- a/crates/test/src/c.rs +++ b/crates/test/src/c.rs @@ -12,7 +12,7 @@ use std::process::Command; pub struct COpts { /// Path to the installation of wasi-sdk #[clap(long, env = "WASI_SDK_PATH", value_name = "PATH")] - wasi_sdk_path: Option, + pub(crate) wasi_sdk_path: Option, /// Name of the C target to compile for. #[clap(long, default_value = "wasm32-wasip2", value_name = "TARGET")] diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs new file mode 100644 index 000000000..54b98b7c5 --- /dev/null +++ b/crates/test/src/cpp.rs @@ -0,0 +1,153 @@ +use crate::config::StringList; +use crate::{Kind, LanguageMethods, Runner}; +// use anyhow::bail; +// use anyhow::Result; +// use clap::Parser; +use heck::ToSnakeCase; +use serde::Deserialize; +// use std::env; +use std::path::PathBuf; +use std::process::Command; + +// option wasi_sdk_path is inherited from C + +pub struct Cpp17; + +/// C/C++-specific configuration of component files +#[derive(Default, Deserialize)] +#[serde(deny_unknown_fields)] +struct LangConfig { + /// Space-separated list or array of compiler flags to pass. + #[serde(default)] + cflags: StringList, +} + +fn clangpp(runner: &Runner<'_>) -> PathBuf { + match &runner.opts.c.wasi_sdk_path { + Some(path) => path.join("bin/wasm32-wasip2-clang++"), + None => "wasm32-wasip2-clang++".into(), + } +} + +impl LanguageMethods for Cpp17 { + fn display(&self) -> &str { + "cpp17" + } + + fn comment_prefix_for_test_config(&self) -> Option<&str> { + Some("//@") + } + + fn bindgen_name(&self) -> Option<&str> { + Some("cpp") + } + + fn should_fail_verify( + &self, + _name: &str, + _config: &crate::config::WitConfig, + _args: &[String], + ) -> bool { + false + } + + // fn default_bindgen_args(&self) -> &[&str] { + // &[] + // } + + fn prepare(&self, runner: &mut crate::Runner<'_>) -> anyhow::Result<()> { + let compiler = clangpp(runner); + let cwd = std::env::current_dir()?; + let dir = cwd.join(&runner.opts.artifacts).join("cpp"); + + super::write_if_different(&dir.join("test.cpp"), "int main() { return 0; }")?; + + println!("Testing if `{}` works...", compiler.display()); + runner + .run_command(Command::new(&compiler).current_dir(&dir).arg("test.c")) + .inspect_err(|_| { + eprintln!( + "Error: failed to find `{}`. Hint: pass `--wasi-sdk-path` or set `WASI_SDK_PATH`", + compiler.display() + ); + })?; + + Ok(()) + } + + fn compile(&self, runner: &crate::Runner<'_>, compile: &crate::Compile) -> anyhow::Result<()> { + let compiler = clangpp(runner); + let config = compile.component.deserialize_lang_config::()?; + + // Compile the C-based bindings to an object file. + let bindings_object = compile.output.with_extension("bindings.o"); + let mut cmd = Command::new(clangpp(runner)); + cmd.arg( + compile + .bindings_dir + .join(format!("{}.cpp", compile.component.bindgen.world)), + ) + .arg("-I") + .arg(&compile.bindings_dir) + .arg("-Wall") + .arg("-Wextra") + .arg("-Werror") + .arg("-Wno-unused-parameter") + .arg("-c") + .arg("-o") + .arg(&bindings_object); + runner.run_command(&mut cmd)?; + + // Now compile the runner's source code to with the above object and the + // component-type object into a final component. + let mut cmd = Command::new(compiler); + cmd.arg(&compile.component.path) + .arg(&bindings_object) + .arg(compile.bindings_dir.join(format!( + "{}_component_type.o", + compile.component.bindgen.world + ))) + .arg("-I") + .arg(&compile.bindings_dir) + .arg("-Wall") + .arg("-Wextra") + .arg("-Werror") + .arg("-Wc++-compat") + .arg("-Wno-unused-parameter") + .arg("-g") + .arg("-o") + .arg(&compile.output); + for flag in Vec::from(config.cflags) { + cmd.arg(flag); + } + match compile.component.kind { + Kind::Runner => {} + Kind::Test => { + cmd.arg("-mexec-model=reactor"); + } + } + runner.run_command(&mut cmd)?; + Ok(()) + } + + fn verify(&self, runner: &crate::Runner<'_>, verify: &crate::Verify) -> anyhow::Result<()> { + let compiler = clangpp(runner); + let mut cmd = Command::new(compiler); + cmd.arg( + verify + .bindings_dir + .join(format!("{}.cpp", verify.world.to_snake_case())), + ) + .arg("-I") + .arg(&verify.bindings_dir) + .arg("-Wall") + .arg("-Wextra") + .arg("-Werror") + .arg("-Wc++-compat") + .arg("-Wno-unused-parameter") + .arg("-c") + .arg("-o") + .arg(verify.artifacts_dir.join("tmp.o")); + runner.run_command(&mut cmd) + } +} diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index cbcbd462d..9edfb025d 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -14,6 +14,7 @@ use wit_component::{ComponentEncoder, StringEncoding}; mod c; mod config; +mod cpp; mod csharp; mod custom; mod moonbit; @@ -191,6 +192,7 @@ enum Language { Rust, C, Cpp, + Cpp17, Wat, Csharp, MoonBit, @@ -411,7 +413,7 @@ impl Runner<'_> { let language = match extension { "rs" => Language::Rust, "c" => Language::C, - "cpp" => Language::Cpp, + "cpp" => Language::Cpp17, "wat" => Language::Wat, "cs" => Language::Csharp, "mbt" => Language::MoonBit, @@ -1201,6 +1203,7 @@ impl Language { Language::Rust, Language::C, Language::Cpp, + Language::Cpp17, Language::Wat, Language::Csharp, Language::MoonBit, @@ -1211,6 +1214,7 @@ impl Language { Language::Rust => &rust::Rust, Language::C => &c::C, Language::Cpp => &c::Cpp, + Language::Cpp17 => &cpp::Cpp17, Language::Wat => &wat::Wat, Language::Csharp => &csharp::Csharp, Language::MoonBit => &moonbit::MoonBit, diff --git a/tests/runtime/strings/runner.cpp b/tests/runtime/strings/runner.cpp new file mode 100644 index 000000000..a4b759bbd --- /dev/null +++ b/tests/runtime/strings/runner.cpp @@ -0,0 +1,19 @@ +//@ args = '--new-api' + +#include +#include +#include +#include +#include + +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); +} + +int main() { + test::strings::to_test::take_basic("latin utf16") + + return 0; +} diff --git a/tests/runtime/strings/test.new.cpp b/tests/runtime/strings/test_new.cpp similarity index 97% rename from tests/runtime/strings/test.new.cpp rename to tests/runtime/strings/test_new.cpp index 966189074..bc8239f80 100644 --- a/tests/runtime/strings/test.new.cpp +++ b/tests/runtime/strings/test_new.cpp @@ -1,3 +1,5 @@ +//@ args = '--new-api' + #include #include #include diff --git a/tests/runtime/strings/test.cpp b/tests/runtime/strings/test_old.cpp similarity index 97% rename from tests/runtime/strings/test.cpp rename to tests/runtime/strings/test_old.cpp index ecf43f653..09fb0d07d 100644 --- a/tests/runtime/strings/test.cpp +++ b/tests/runtime/strings/test_old.cpp @@ -1,3 +1,5 @@ +//@ args = '--old-api' + #include #include #include From a807a30af4d3129d0492445eb121eee7c475ea6a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 28 Apr 2025 00:39:18 +0200 Subject: [PATCH 575/672] proper include --- crates/test/src/cpp.rs | 7 +++++++ tests/runtime/strings/runner.cpp | 11 ++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index 54b98b7c5..a709217e5 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -78,6 +78,11 @@ impl LanguageMethods for Cpp17 { fn compile(&self, runner: &crate::Runner<'_>, compile: &crate::Compile) -> anyhow::Result<()> { let compiler = clangpp(runner); let config = compile.component.deserialize_lang_config::()?; + let cwd = std::env::current_dir()?; + let mut helper_dir = cwd; + helper_dir.push("crates"); + helper_dir.push("cpp"); + helper_dir.push("helper-types"); // Compile the C-based bindings to an object file. let bindings_object = compile.output.with_extension("bindings.o"); @@ -89,6 +94,8 @@ impl LanguageMethods for Cpp17 { ) .arg("-I") .arg(&compile.bindings_dir) + .arg("-I") + .arg(helper_dir.to_str().unwrap().to_string()) .arg("-Wall") .arg("-Wextra") .arg("-Werror") diff --git a/tests/runtime/strings/runner.cpp b/tests/runtime/strings/runner.cpp index a4b759bbd..7d5476753 100644 --- a/tests/runtime/strings/runner.cpp +++ b/tests/runtime/strings/runner.cpp @@ -13,7 +13,16 @@ void assert_str(std::string_view str, const char* expected) { } int main() { - test::strings::to_test::take_basic("latin utf16") + test::strings::to_test::take_basic("latin utf16"); + let str2 = test::strings::to_test::return_unicode(); + assert_str(str2, "🚀🚀🚀 𠈄𓀀"); + + let str3 = test::strings::to_test::return_empty(); + assert_str(str3, ""); + + let str5 = test::strings::to_test::roundtrip("🚀🚀🚀 𠈄𓀀"); + assert_str(str5, "🚀🚀🚀 𠈄𓀀"); + return 0; } From 013633cb18044a445d27b1c6058810ffddbbe9a4 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 7 May 2025 23:16:59 +0200 Subject: [PATCH 576/672] post merge fixes --- Cargo.lock | 8 ++++---- crates/cpp/Cargo.toml | 2 +- crates/cpp/src/lib.rs | 9 ++++++++- crates/test/src/cpp.rs | 8 ++++---- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1a09ad20..ac675f332 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1244,14 +1244,14 @@ dependencies = [ [[package]] name = "wit-bindgen-cpp" -version = "0.41.0" +version = "0.42.1" dependencies = [ "anyhow", "clap", - "heck", + "heck 0.5.0", "test-helpers", - "wasm-encoder 0.229.0", - "wasm-metadata 0.229.0", + "wasm-encoder 0.230.0", + "wasm-metadata 0.230.0", "wit-bindgen-c", "wit-bindgen-core", "wit-component", diff --git a/crates/cpp/Cargo.toml b/crates/cpp/Cargo.toml index 346c9e21a..e84941e1b 100644 --- a/crates/cpp/Cargo.toml +++ b/crates/cpp/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "wit-bindgen-cpp" authors = ["Christof Petig "] -version = "0.41.0" +version = "0.42.1" edition.workspace = true repository = 'https://github.com/cpetig/wit-bindgen' license = "Apache-2.0 WITH LLVM-exception" diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index cedc154e9..938465a08 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -214,7 +214,10 @@ pub struct Opts { /// 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 = true, overrides_with = "_old_api"))] + #[cfg_attr( + feature = "clap", + arg(long, default_value_t = true, overrides_with = "_old_api") + )] pub new_api: bool, /// Asymmetric API: Imported functions borrow arguments (const&), @@ -794,6 +797,7 @@ impl CppInterfaceGenerator<'_> { TypeDefKind::Future(_) => todo!("generate for future"), TypeDefKind::Stream(_) => todo!("generate for stream"), TypeDefKind::Handle(_) => todo!("generate for handle"), + TypeDefKind::FixedSizeList(_, _) => todo!(), TypeDefKind::Unknown => unreachable!(), } } @@ -1562,6 +1566,7 @@ impl CppInterfaceGenerator<'_> { TypeDefKind::Future(_) => todo!(), TypeDefKind::Stream(_) => todo!(), TypeDefKind::Type(ty) => self.type_name(ty, from_namespace, flavor), + TypeDefKind::FixedSizeList(_, _) => todo!(), TypeDefKind::Unknown => todo!(), }, Type::ErrorContext => todo!(), @@ -2176,6 +2181,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { Type::Id(id) => self.has_resources(id), _ => false, }, + TypeDefKind::FixedSizeList(_, _) => todo!(), TypeDefKind::Unknown => todo!(), } } @@ -3134,6 +3140,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } abi::Instruction::AsyncTaskReturn { .. } => todo!(), + abi::Instruction::DropHandle { .. } => todo!(), } } diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index a709217e5..9b2866014 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -59,9 +59,9 @@ impl LanguageMethods for Cpp17 { let compiler = clangpp(runner); let cwd = std::env::current_dir()?; let dir = cwd.join(&runner.opts.artifacts).join("cpp"); - + super::write_if_different(&dir.join("test.cpp"), "int main() { return 0; }")?; - + println!("Testing if `{}` works...", compiler.display()); runner .run_command(Command::new(&compiler).current_dir(&dir).arg("test.c")) @@ -71,7 +71,7 @@ impl LanguageMethods for Cpp17 { compiler.display() ); })?; - + Ok(()) } @@ -104,7 +104,7 @@ impl LanguageMethods for Cpp17 { .arg("-o") .arg(&bindings_object); runner.run_command(&mut cmd)?; - + // Now compile the runner's source code to with the above object and the // component-type object into a final component. let mut cmd = Command::new(compiler); From 984e9a28299db57544bb7e366b33b161a073ed9d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 8 May 2025 23:11:16 +0200 Subject: [PATCH 577/672] c++ numbers test works --- crates/test/src/cpp.rs | 2 +- tests/runtime/numbers/test.cpp | 82 ++++++---------------------------- 2 files changed, 15 insertions(+), 69 deletions(-) diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index 9b2866014..0539f9629 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -64,7 +64,7 @@ impl LanguageMethods for Cpp17 { println!("Testing if `{}` works...", compiler.display()); runner - .run_command(Command::new(&compiler).current_dir(&dir).arg("test.c")) + .run_command(Command::new(&compiler).current_dir(&dir).arg("test.cpp")) .inspect_err(|_| { eprintln!( "Error: failed to find `{}`. Hint: pass `--wasi-sdk-path` or set `WASI_SDK_PATH`", diff --git a/tests/runtime/numbers/test.cpp b/tests/runtime/numbers/test.cpp index e1cc759d6..1f9d6a430 100644 --- a/tests/runtime/numbers/test.cpp +++ b/tests/runtime/numbers/test.cpp @@ -1,112 +1,58 @@ #include #include #include -#include +#include -uint8_t exports::test::numbers::test::RoundtripU8(uint8_t a) { +uint8_t exports::test::numbers::numbers::RoundtripU8(uint8_t a) { return a; } -int8_t exports::test::numbers::test::RoundtripS8(int8_t a) { +int8_t exports::test::numbers::numbers::RoundtripS8(int8_t a) { return a; } -uint16_t exports::test::numbers::test::RoundtripU16(uint16_t a) { +uint16_t exports::test::numbers::numbers::RoundtripU16(uint16_t a) { return a; } -int16_t exports::test::numbers::test::RoundtripS16(int16_t a) { +int16_t exports::test::numbers::numbers::RoundtripS16(int16_t a) { return a; } -uint32_t exports::test::numbers::test::RoundtripU32(uint32_t a) { +uint32_t exports::test::numbers::numbers::RoundtripU32(uint32_t a) { return a; } -int32_t exports::test::numbers::test::RoundtripS32(int32_t a) { +int32_t exports::test::numbers::numbers::RoundtripS32(int32_t a) { return a; } -uint64_t exports::test::numbers::test::RoundtripU64(uint64_t a) { +uint64_t exports::test::numbers::numbers::RoundtripU64(uint64_t a) { return a; } -int64_t exports::test::numbers::test::RoundtripS64(int64_t a) { +int64_t exports::test::numbers::numbers::RoundtripS64(int64_t a) { return a; } -float exports::test::numbers::test::RoundtripF32(float a) { +float exports::test::numbers::numbers::RoundtripF32(float a) { return a; } -double exports::test::numbers::test::RoundtripF64(double a) { +double exports::test::numbers::numbers::RoundtripF64(double a) { return a; } -uint32_t exports::test::numbers::test::RoundtripChar(uint32_t a) { +uint32_t exports::test::numbers::numbers::RoundtripChar(uint32_t a) { return a; } static uint32_t SCALAR = 0; -void exports::test::numbers::test::SetScalar(uint32_t a) { +void exports::test::numbers::numbers::SetScalar(uint32_t a) { SCALAR = a; } -uint32_t exports::test::numbers::test::GetScalar(void) { +uint32_t exports::test::numbers::numbers::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); -} From c8a7e11d54cef1da8878b5f1eb4c8189edb54d79 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 8 May 2025 23:23:07 +0200 Subject: [PATCH 578/672] many-arguments c++ test works --- tests/runtime/lists/test.new.cpp | 2 +- .../wasm.new.cpp => many-arguments/test-new.cpp} | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) rename tests/runtime/{many_arguments/wasm.new.cpp => many-arguments/test-new.cpp} (82%) diff --git a/tests/runtime/lists/test.new.cpp b/tests/runtime/lists/test.new.cpp index 0e4481a6d..f7a57cc6c 100644 --- a/tests/runtime/lists/test.new.cpp +++ b/tests/runtime/lists/test.new.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include diff --git a/tests/runtime/many_arguments/wasm.new.cpp b/tests/runtime/many-arguments/test-new.cpp similarity index 82% rename from tests/runtime/many_arguments/wasm.new.cpp rename to tests/runtime/many-arguments/test-new.cpp index 80b70603e..7db342b72 100644 --- a/tests/runtime/many_arguments/wasm.new.cpp +++ b/tests/runtime/many-arguments/test-new.cpp @@ -1,12 +1,12 @@ #include -#include +#include template bool equal(T const&a, T const&b) { return a==b; } -void exports::many_arguments::ManyArguments( +void exports::test::many_arguments::to_test::ManyArguments( uint64_t a1, uint64_t a2, uint64_t a3, @@ -40,7 +40,4 @@ void exports::many_arguments::ManyArguments( 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 - ); } From e7419414b60313930c0335d8a8dd6cde682014cc Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 8 May 2025 23:28:28 +0200 Subject: [PATCH 579/672] more test changes --- tests/runtime/options/{test.new.cpp => test-new.cpp} | 2 +- tests/runtime/records/test.new.cpp | 2 +- tests/runtime/results/{test.new.cpp => test-new.cpp} | 0 tests/runtime/strings/{test_new.cpp => test-new.cpp} | 0 tests/runtime/strings/{test_old.cpp => test-old.cpp} | 0 5 files changed, 2 insertions(+), 2 deletions(-) rename tests/runtime/options/{test.new.cpp => test-new.cpp} (98%) rename tests/runtime/results/{test.new.cpp => test-new.cpp} (100%) rename tests/runtime/strings/{test_new.cpp => test-new.cpp} (100%) rename tests/runtime/strings/{test_old.cpp => test-old.cpp} (100%) diff --git a/tests/runtime/options/test.new.cpp b/tests/runtime/options/test-new.cpp similarity index 98% rename from tests/runtime/options/test.new.cpp rename to tests/runtime/options/test-new.cpp index 8925a3f74..6724c3ee9 100644 --- a/tests/runtime/options/test.new.cpp +++ b/tests/runtime/options/test-new.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include template static bool equal(T const& a, T const& b) { diff --git a/tests/runtime/records/test.new.cpp b/tests/runtime/records/test.new.cpp index d5b276f46..4a6a5ac0d 100644 --- a/tests/runtime/records/test.new.cpp +++ b/tests/runtime/records/test.new.cpp @@ -1,5 +1,5 @@ #include -#include +#include template bool equal(T const&a, T const&b) { diff --git a/tests/runtime/results/test.new.cpp b/tests/runtime/results/test-new.cpp similarity index 100% rename from tests/runtime/results/test.new.cpp rename to tests/runtime/results/test-new.cpp diff --git a/tests/runtime/strings/test_new.cpp b/tests/runtime/strings/test-new.cpp similarity index 100% rename from tests/runtime/strings/test_new.cpp rename to tests/runtime/strings/test-new.cpp diff --git a/tests/runtime/strings/test_old.cpp b/tests/runtime/strings/test-old.cpp similarity index 100% rename from tests/runtime/strings/test_old.cpp rename to tests/runtime/strings/test-old.cpp From 2ccd75215dbf38841d61245ee9657582a7983216 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 10 May 2025 11:09:32 +0200 Subject: [PATCH 580/672] records test passes --- crates/cpp/src/lib.rs | 2 +- tests/runtime/records/test.cpp | 30 ++++++++++++ tests/runtime/records/test.new.cpp | 75 ------------------------------ 3 files changed, 31 insertions(+), 76 deletions(-) create mode 100644 tests/runtime/records/test.cpp delete mode 100644 tests/runtime/records/test.new.cpp diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 938465a08..d6ad508e6 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -239,7 +239,7 @@ impl Opts { } fn ptr_type(&self) -> &'static str { - "int32_t" + "uint8_t*" } } diff --git a/tests/runtime/records/test.cpp b/tests/runtime/records/test.cpp new file mode 100644 index 000000000..6d69cc193 --- /dev/null +++ b/tests/runtime/records/test.cpp @@ -0,0 +1,30 @@ +#include +#include + +std::tuple exports::test::records::to_test::MultipleResults() { + return std::tuple(4, 5); +} + +std::tuple exports::test::records::to_test::SwapTuple(std::tuple a) { + return std::tuple(std::get<1>(a), std::get<0>(a)); +} + +test::records::to_test::F1 exports::test::records::to_test::RoundtripFlags1(::test::records::to_test::F1 a) { + return a; +} + +test::records::to_test::F2 exports::test::records::to_test::RoundtripFlags2(::test::records::to_test::F2 a) { + return a; +} + +std::tuple exports::test::records::to_test::RoundtripFlags3(::test::records::to_test::Flag8 a, ::test::records::to_test::Flag16 b, ::test::records::to_test::Flag32 c) { + return std::tuple<::test::records::to_test::Flag8, ::test::records::to_test::Flag16, ::test::records::to_test::Flag32>(a, b, c); +} + +test::records::to_test::R1 exports::test::records::to_test::RoundtripRecord1(::test::records::to_test::R1 a) { + return a; +} + +std::tuple exports::test::records::to_test::Tuple1(std::tuple a) { + return std::tuple(std::get<0>(a)); +} diff --git a/tests/runtime/records/test.new.cpp b/tests/runtime/records/test.new.cpp deleted file mode 100644 index 4a6a5ac0d..000000000 --- a/tests/runtime/records/test.new.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include -#include - -template -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(4, 5))); - - assert(equal(SwapTuple(std::tuple(1, 2)), std::tuple(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(1)), std::tuple(1))); -} - -std::tuple exports::test::records::test::MultipleResults() { - return std::tuple(100, 200); -} - -std::tuple exports::test::records::test::SwapTuple(std::tuple a) { - return std::tuple(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 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 exports::test::records::test::Tuple1(std::tuple a) { - return std::tuple(std::get<0>(a)); -} From a17ab721f58920bc6783c5ee0c3782e97d6ea787 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 10 May 2025 11:57:20 +0200 Subject: [PATCH 581/672] records test passes for c++ --- crates/cpp/src/lib.rs | 4 +-- tests/runtime/records/runner.cpp | 48 ++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 tests/runtime/records/runner.cpp diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index d6ad508e6..29b118606 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -976,7 +976,7 @@ impl CppInterfaceGenerator<'_> { func: &Function, abi_variant: AbiVariant, // import: bool, - _from_namespace: &Vec, + from_namespace: &Vec, ) -> HighlevelSignature { let mut res = HighlevelSignature::default(); // let abi_variant = if import ^ self.gen.opts.host_side() { @@ -1000,7 +1000,7 @@ impl CppInterfaceGenerator<'_> { if let Some(ty) = &func.result { res.result.push_str(&self.type_name( ty, - &res.namespace, + from_namespace, Flavor::Result(abi_variant), )); } else { diff --git a/tests/runtime/records/runner.cpp b/tests/runtime/records/runner.cpp new file mode 100644 index 000000000..2f839e043 --- /dev/null +++ b/tests/runtime/records/runner.cpp @@ -0,0 +1,48 @@ +#include +#include + +template +bool equal(T const&a, T const&b) { + return a==b; +} + +int main() +{ + using namespace ::test::records::to_test; + + assert(equal(MultipleResults(), std::tuple(4, 5))); + + assert(equal(SwapTuple(std::tuple(1, 2)), std::tuple(2, 1))); + assert(equal(RoundtripFlags1(::test::records::to_test::F1::kA), ::test::records::to_test::F1::kA)); + assert(equal(RoundtripFlags1(::test::records::to_test::F1::k_None), ::test::records::to_test::F1::k_None)); + assert(equal(RoundtripFlags1(::test::records::to_test::F1::kB), ::test::records::to_test::F1::kB)); + assert(equal(RoundtripFlags1(::test::records::to_test::F1::kA | ::test::records::to_test::F1::kB), ::test::records::to_test::F1::kA | ::test::records::to_test::F1::kB)); + + assert(equal(RoundtripFlags2(::test::records::to_test::F2::kC), ::test::records::to_test::F2::kC)); + assert(equal(RoundtripFlags2(::test::records::to_test::F2::k_None), ::test::records::to_test::F2::k_None)); + assert(equal(RoundtripFlags2(::test::records::to_test::F2::kD), ::test::records::to_test::F2::kD)); + assert(equal(RoundtripFlags2(::test::records::to_test::F2::kC | ::test::records::to_test::F2::kE), ::test::records::to_test::F2::kC | ::test::records::to_test::F2::kE)); + + assert(equal( + RoundtripFlags3(::test::records::to_test::Flag8::kB0, ::test::records::to_test::Flag16::kB1, ::test::records::to_test::Flag32::kB2), + std::tuple<::test::records::to_test::Flag8, ::test::records::to_test::Flag16, ::test::records::to_test::Flag32>(::test::records::to_test::Flag8::kB0, ::test::records::to_test::Flag16::kB1, ::test::records::to_test::Flag32::kB2) + )); + + { + auto r = RoundtripRecord1(::test::records::to_test::R1 { + 8, + ::test::records::to_test::F1::k_None, + }); + assert(equal(r.a, (uint8_t)8)); + assert(equal(r.b, ::test::records::to_test::F1::k_None)); + } + + auto r = RoundtripRecord1(::test::records::to_test::R1 { + 0, + ::test::records::to_test::F1::kA | ::test::records::to_test::F1::kB, + }); + assert(equal(r.a, (uint8_t)0)); + assert(equal(r.b, ::test::records::to_test::F1::kA | ::test::records::to_test::F1::kB)); + + assert(equal(Tuple1(std::tuple(1)), std::tuple(1))); +} From 0bb5a037a1b900b42c949b112e203e9e840dc3f9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 10 May 2025 12:10:27 +0200 Subject: [PATCH 582/672] numbers ok with c++17 --- crates/cpp/src/lib.rs | 4 +-- tests/runtime/numbers/runner.cpp | 59 ++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 tests/runtime/numbers/runner.cpp diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 29b118606..097c76af9 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -976,7 +976,7 @@ impl CppInterfaceGenerator<'_> { func: &Function, abi_variant: AbiVariant, // import: bool, - from_namespace: &Vec, + outer_namespace: &Vec, ) -> HighlevelSignature { let mut res = HighlevelSignature::default(); // let abi_variant = if import ^ self.gen.opts.host_side() { @@ -1000,7 +1000,7 @@ impl CppInterfaceGenerator<'_> { if let Some(ty) = &func.result { res.result.push_str(&self.type_name( ty, - from_namespace, + outer_namespace, Flavor::Result(abi_variant), )); } else { diff --git a/tests/runtime/numbers/runner.cpp b/tests/runtime/numbers/runner.cpp new file mode 100644 index 000000000..85aaf38a6 --- /dev/null +++ b/tests/runtime/numbers/runner.cpp @@ -0,0 +1,59 @@ +#include +#include +#include + +int main() +{ + using namespace ::test::numbers::numbers; + + assert(RoundtripU8(1) == 1); + assert(RoundtripU8(0) == 0); + assert(RoundtripU8(UCHAR_MAX) == UCHAR_MAX); + + assert(RoundtripS8(1) == 1); + assert(RoundtripS8(SCHAR_MIN) == SCHAR_MIN); + assert(RoundtripS8(SCHAR_MAX) == SCHAR_MAX); + + assert(RoundtripU16(1) == 1); + assert(RoundtripU16(0) == 0); + assert(RoundtripU16(USHRT_MAX) == USHRT_MAX); + + assert(RoundtripS16(1) == 1); + assert(RoundtripS16(SHRT_MIN) == SHRT_MIN); + assert(RoundtripS16(SHRT_MAX) == SHRT_MAX); + + assert(RoundtripU32(1) == 1); + assert(RoundtripU32(0) == 0); + assert(RoundtripU32(UINT_MAX) == UINT_MAX); + + assert(RoundtripS32(1) == 1); + assert(RoundtripS32(INT_MIN) == INT_MIN); + assert(RoundtripS32(INT_MAX) == INT_MAX); + + assert(RoundtripU64(1) == 1); + assert(RoundtripU64(0) == 0); + assert(RoundtripU64(ULONG_MAX) == ULONG_MAX); + + assert(RoundtripS64(1) == 1); + assert(RoundtripS64(LONG_MIN) == LONG_MIN); + assert(RoundtripS64(LONG_MAX) == LONG_MAX); + + assert(RoundtripF32(1.0) == 1.0); + assert(RoundtripF32(INFINITY) == INFINITY); + assert(RoundtripF32(-INFINITY) == -INFINITY); + assert(isnan(RoundtripF32(NAN))); + + assert(RoundtripF64(1.0) == 1.0); + assert(RoundtripF64(INFINITY) == INFINITY); + assert(RoundtripF64(-INFINITY) == -INFINITY); + assert(isnan(RoundtripF64(NAN))); + + assert(RoundtripChar('a') == 'a'); + assert(RoundtripChar(' ') == ' '); + assert(RoundtripChar(U'🚩') == U'🚩'); + + SetScalar(2); + assert(GetScalar() == 2); + SetScalar(4); + assert(GetScalar() == 4); +} From a4cae5d306026700e6483a56306b423d7f1d1347 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 10 May 2025 12:13:59 +0200 Subject: [PATCH 583/672] many arguments cover both sides --- tests/runtime/many-arguments/runner.cpp | 26 +++++++++++++++++++ .../many-arguments/{test-new.cpp => test.cpp} | 0 2 files changed, 26 insertions(+) create mode 100644 tests/runtime/many-arguments/runner.cpp rename tests/runtime/many-arguments/{test-new.cpp => test.cpp} (100%) diff --git a/tests/runtime/many-arguments/runner.cpp b/tests/runtime/many-arguments/runner.cpp new file mode 100644 index 000000000..1d41d8990 --- /dev/null +++ b/tests/runtime/many-arguments/runner.cpp @@ -0,0 +1,26 @@ +#include +#include + +int main() +{ + using namespace ::test::many_arguments::to_test; + + ManyArguments( + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16 + ); +} diff --git a/tests/runtime/many-arguments/test-new.cpp b/tests/runtime/many-arguments/test.cpp similarity index 100% rename from tests/runtime/many-arguments/test-new.cpp rename to tests/runtime/many-arguments/test.cpp From 1b8574eb81badf3d093151f5613e57d0554d2fc6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 10 May 2025 13:59:09 +0200 Subject: [PATCH 584/672] strings test passes --- crates/cpp/src/lib.rs | 14 +++++++- crates/test/src/cpp.rs | 4 +++ tests/runtime/options/runner.cpp | 17 +++++++++ tests/runtime/options/test-new.cpp | 58 ------------------------------ tests/runtime/options/test.cpp | 45 +++++++++++++++++++++++ tests/runtime/strings/runner.cpp | 10 +++--- tests/runtime/strings/test-new.cpp | 31 ---------------- tests/runtime/strings/test.cpp | 34 ++++++++++++++++++ 8 files changed, 118 insertions(+), 95 deletions(-) create mode 100644 tests/runtime/options/runner.cpp delete mode 100644 tests/runtime/options/test-new.cpp create mode 100644 tests/runtime/options/test.cpp delete mode 100644 tests/runtime/strings/test-new.cpp create mode 100644 tests/runtime/strings/test.cpp diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 097c76af9..5f2ab2456 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2386,7 +2386,19 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let tmp = self.tmp(); let len = format!("len{}", tmp); uwriteln!(self.src, "auto {} = {};\n", len, operands[1]); - let result = format!("wit::string((char const*)({}), {len})", operands[0]); + let result = if self.gen.gen.opts.new_api + && matches!(self.variant, AbiVariant::GuestExport) + { + 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, .. } => { diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index 0539f9629..1affd1ecf 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -96,6 +96,7 @@ impl LanguageMethods for Cpp17 { .arg(&compile.bindings_dir) .arg("-I") .arg(helper_dir.to_str().unwrap().to_string()) + .arg("-fno-exceptions") .arg("-Wall") .arg("-Wextra") .arg("-Werror") @@ -116,6 +117,9 @@ impl LanguageMethods for Cpp17 { ))) .arg("-I") .arg(&compile.bindings_dir) + .arg("-I") + .arg(helper_dir.to_str().unwrap().to_string()) + .arg("-fno-exceptions") .arg("-Wall") .arg("-Wextra") .arg("-Werror") diff --git a/tests/runtime/options/runner.cpp b/tests/runtime/options/runner.cpp new file mode 100644 index 000000000..67731afb7 --- /dev/null +++ b/tests/runtime/options/runner.cpp @@ -0,0 +1,17 @@ +#include +#include +#include + +int main() +{ + using namespace ::test::options::to_test; + + OptionNoneParam(std::optional()); + OptionSomeParam(std::optional("foo")); + assert(!OptionNoneResult()); + assert(equal(OptionSomeResult(), std::optional("foo"))); + assert(equal(OptionRoundtrip(std::optional("foo")), std::optional("foo"))); + assert(equal(DoubleOptionRoundtrip(std::optional>(std::optional(42))), std::optional>(std::optional(42)))); + assert(equal(DoubleOptionRoundtrip(std::optional>(std::optional())), std::optional>(std::optional()))); + assert(equal(DoubleOptionRoundtrip(std::optional>()), std::optional>())); +} diff --git a/tests/runtime/options/test-new.cpp b/tests/runtime/options/test-new.cpp deleted file mode 100644 index 6724c3ee9..000000000 --- a/tests/runtime/options/test-new.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include -#include -#include -#include - -template -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 const& a, std::optional 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()); - OptionSomeParam(std::optional("foo")); - assert(!OptionNoneResult()); - assert(equal(OptionSomeResult(), std::optional("foo"))); - assert(equal(OptionRoundtrip(std::optional("foo")), std::optional("foo"))); - assert(equal(DoubleOptionRoundtrip(std::optional>(std::optional(42))), std::optional>(std::optional(42)))); - assert(equal(DoubleOptionRoundtrip(std::optional>(std::optional())), std::optional>(std::optional()))); - assert(equal(DoubleOptionRoundtrip(std::optional>()), std::optional>())); -} - -void exports::test::options::test::OptionNoneParam(std::optional a) -{ - assert(!a.has_value()); -} - -std::optional exports::test::options::test::OptionNoneResult() { - return std::optional(); -} - -void exports::test::options::test::OptionSomeParam(std::optional a) { - assert(equal(a, std::optional("foo"))); -} - -std::optional exports::test::options::test::OptionSomeResult() { - return std::optional(wit::string::from_view("foo")); -} - -std::optional exports::test::options::test::OptionRoundtrip(std::optional a) { - if (!a.has_value()) return std::optional(); - return std::optional(wit::string::from_view(*a)); -} - -std::optional> exports::test::options::test::DoubleOptionRoundtrip(std::optional> a) { - return a; -} diff --git a/tests/runtime/options/test.cpp b/tests/runtime/options/test.cpp new file mode 100644 index 000000000..81a0fd4de --- /dev/null +++ b/tests/runtime/options/test.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include + +template +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 const& a, std::optional 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::test::options::to_test::OptionNoneParam(std::optional a) +{ + assert(!a.has_value()); +} + +std::optional exports::test::options::to_test::OptionNoneResult() { + return std::optional(); +} + +void exports::test::options::to_test::OptionSomeParam(std::optional a) { + assert(equal(a, std::optional("foo"))); +} + +std::optional exports::test::options::to_test::OptionSomeResult() { + return std::optional(wit::string::from_view("foo")); +} + +std::optional exports::test::options::to_test::OptionRoundtrip(std::optional a) { + if (!a.has_value()) return std::optional(); + return std::optional(wit::string::from_view(*a)); +} + +std::optional> exports::test::options::to_test::DoubleOptionRoundtrip(std::optional> a) { + return a; +} diff --git a/tests/runtime/strings/runner.cpp b/tests/runtime/strings/runner.cpp index 7d5476753..83ac9c3a6 100644 --- a/tests/runtime/strings/runner.cpp +++ b/tests/runtime/strings/runner.cpp @@ -6,22 +6,22 @@ #include #include -void assert_str(std::string_view str, const char* expected) { +void assert_str(wit::string const& str, const char* expected) { size_t expected_len = strlen(expected); assert(str.size() == expected_len); assert(memcmp(str.data(), expected, expected_len) == 0); } int main() { - test::strings::to_test::take_basic("latin utf16"); + test::strings::to_test::TakeBasic("latin utf16"); - let str2 = test::strings::to_test::return_unicode(); + auto str2 = test::strings::to_test::ReturnUnicode(); assert_str(str2, "🚀🚀🚀 𠈄𓀀"); - let str3 = test::strings::to_test::return_empty(); + auto str3 = test::strings::to_test::ReturnEmpty(); assert_str(str3, ""); - let str5 = test::strings::to_test::roundtrip("🚀🚀🚀 𠈄𓀀"); + auto str5 = test::strings::to_test::Roundtrip("🚀🚀🚀 𠈄𓀀"); assert_str(str5, "🚀🚀🚀 𠈄𓀀"); return 0; diff --git a/tests/runtime/strings/test-new.cpp b/tests/runtime/strings/test-new.cpp deleted file mode 100644 index bc8239f80..000000000 --- a/tests/runtime/strings/test-new.cpp +++ /dev/null @@ -1,31 +0,0 @@ -//@ args = '--new-api' - -#include -#include -#include -#include -#include - -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/test.cpp b/tests/runtime/strings/test.cpp new file mode 100644 index 000000000..104fd0ed4 --- /dev/null +++ b/tests/runtime/strings/test.cpp @@ -0,0 +1,34 @@ +//@ args = '--new-api' + +#include +#include +#include +#include +#include + +void assert_str(std::string_view const& str, const char* expected) { + size_t expected_len = strlen(expected); + assert(str.size() == expected_len); + assert(memcmp(str.data(), expected, expected_len) == 0); +} + +// new API: Identical for guest import and export +void exports::test::strings::to_test::TakeBasic(std::string_view str) { + assert_str(str, "latin utf16"); +} + +wit::string exports::test::strings::to_test::ReturnUnicode() { + // return a non-zero address (follows cabi_realloc logic) + return wit::string::from_view("🚀🚀🚀 𠈄𓀀"); +} + +wit::string exports::test::strings::to_test::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::test::strings::to_test::Roundtrip(std::string_view str) { + assert(str.size() > 0); + return wit::string::from_view(str); +} From 7ff30cba3fb8aae6735bceb60250e7da8279e1a7 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 10 May 2025 14:01:33 +0200 Subject: [PATCH 585/672] fix options test --- tests/runtime/options/runner.cpp | 15 +++++++++++++++ tests/runtime/options/test.cpp | 10 ---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/runtime/options/runner.cpp b/tests/runtime/options/runner.cpp index 67731afb7..7ab0a236c 100644 --- a/tests/runtime/options/runner.cpp +++ b/tests/runtime/options/runner.cpp @@ -2,6 +2,21 @@ #include #include +template +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 const& a, std::optional const& b) { + if (a.has_value() != b.has_value()) return false; + if (a.has_value()) { + return equal(a.value(), b.value()); + } + return true; +} + int main() { using namespace ::test::options::to_test; diff --git a/tests/runtime/options/test.cpp b/tests/runtime/options/test.cpp index 81a0fd4de..a12458a1c 100644 --- a/tests/runtime/options/test.cpp +++ b/tests/runtime/options/test.cpp @@ -7,16 +7,6 @@ template 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 const& a, std::optional 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::test::options::to_test::OptionNoneParam(std::optional a) { From 66bb0c6623647d1530aba0b8e58b15eceab85de4 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 10 May 2025 14:40:32 +0200 Subject: [PATCH 586/672] fix old string test --- crates/cpp/src/lib.rs | 5 ++++- tests/runtime/strings/test-old.cpp | 18 ++++++++++-------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 5f2ab2456..fbeb093a0 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -228,8 +228,11 @@ pub struct Opts { } impl Opts { - pub fn build(self) -> Box { + pub fn build(mut self) -> Box { let mut r = Cpp::new(); + if self._old_api { + self.new_api = false; + } r.opts = self; Box::new(r) } diff --git a/tests/runtime/strings/test-old.cpp b/tests/runtime/strings/test-old.cpp index 09fb0d07d..09f31379b 100644 --- a/tests/runtime/strings/test-old.cpp +++ b/tests/runtime/strings/test-old.cpp @@ -1,30 +1,32 @@ //@ args = '--old-api' #include -#include +#include #include #include #include -void assert_str(std::string_view str, const char* expected) { +void assert_str(std::string_view const& 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")); +void exports::test::strings::to_test::TakeBasic(wit::string&& str) { + assert_str(str.get_view(), "latin utf16"); +} - wit::string str2 = test::strings::imports::ReturnUnicode(); - assert_str(str2.get_view(), "??? ??"); +wit::string exports::test::strings::to_test::ReturnUnicode() { + // return a non-zero address (follows cabi_realloc logic) + return wit::string::from_view("🚀🚀🚀 𠈄𓀀"); } -wit::string exports::strings::ReturnEmpty() { +wit::string exports::test::strings::to_test::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) { +wit::string exports::test::strings::to_test::Roundtrip(wit::string &&str) { assert(str.size() > 0); return std::move(str); } From ca017dadc576dd428267310ef1671a1d3dd5ec9b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 10 May 2025 14:53:45 +0200 Subject: [PATCH 587/672] list test (in progress) --- tests/runtime/lists/runner.cpp | 118 ++++++++++++++++++++ tests/runtime/lists/test.cpp | 136 +++++++++++++++++++++++ tests/runtime/lists/test.new.cpp | 185 ------------------------------- tests/runtime/results/runner.cpp | 32 ++++++ 4 files changed, 286 insertions(+), 185 deletions(-) create mode 100644 tests/runtime/lists/runner.cpp create mode 100644 tests/runtime/lists/test.cpp delete mode 100644 tests/runtime/lists/test.new.cpp create mode 100644 tests/runtime/results/runner.cpp diff --git a/tests/runtime/lists/runner.cpp b/tests/runtime/lists/runner.cpp new file mode 100644 index 000000000..49ce35c4c --- /dev/null +++ b/tests/runtime/lists/runner.cpp @@ -0,0 +1,118 @@ +#include +#include +#include +#include + +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 +static bool equal(T const&a, S const& b) { + return a == b; +} +template +static bool equal(wit::span const&a, wit::span const& b) { + if (a.size() != b.size()) { return false; } + for (uint32_t i = 0; i +static bool equal(wit::vector const&a, wit::span const& b) { + return equal(a.get_view(), b); +} +template +static bool equal(wit::span const&a, wit::vector const& b) { + return equal(b, a); +} +template +static bool equal(wit::span const&a, std::vector const& b) { + return equal(a, wit::span(b)); +} +template +static bool equal(wit::vector const&a, std::vector const& b) { + return equal(a.get_view(), wit::span(b)); +} +static bool equal(wit::vector const&a, std::vector const& b) { + return equal(a.get_view(), wit::span(b)); +} +template +static bool equal(std::tuple const&a, std::tuple const& b) { + return equal(std::get<0>(a), std::get<0>(b)) && equal(std::get<1>(a), std::get<1>(b)); +} + +template +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 const& a, std::optional const& b) { +// if (a.has_value() != b.has_value()) return false; +// if (a.has_value()) { +// return equal(a.value(), b.value()); +// } +// return true; +// } + +int main() +{ + using namespace ::test::lists::to_test; + + EmptyListParam(wit::span(std::vector())); + EmptyStringParam(""); + assert(EmptyListResult().empty()); + assert(EmptyStringResult().empty()); + + ListParam(std::vector{1, 2, 3, 4}); + ListParam2("foo"); + ListParam3(std::vector{"foo", "bar", "baz"}); + ListParam4(std::vector>{ + std::vector{"foo", "bar"}, + std::vector{"baz"}, + }); + assert(equal(ListResult(), std::vector{1, 2, 3, 4, 5})); + assert(equal(ListResult2(), "hello!")); + assert(equal(ListResult3(), std::vector{"hello,", "world!"})); + + assert(equal(ListRoundtrip(wit::span(std::vector())), std::vector())); + assert(equal(ListRoundtrip(wit::span(std::vector{'x'})), std::vector{'x'})); + assert(equal(ListRoundtrip(wit::span(std::vector{'h', 'e', 'l', 'l', 'o'})), std::vector{'h', 'e', 'l', 'l', 'o'})); + + assert(equal(StringRoundtrip("x"), "x")); + assert(equal(StringRoundtrip(""), "")); + assert(equal(StringRoundtrip("hello"), "hello")); + assert(equal(StringRoundtrip("hello ⚑ world"), "hello ⚑ world")); + + assert(equal( + ListMinmax8(std::vector{0, UINT8_MAX}, std::vector{INT8_MIN, INT8_MAX}), + std::make_tuple(std::vector{0, UINT8_MAX}, std::vector{INT8_MIN, INT8_MAX}) + )); + assert(equal( + ListMinmax16(std::vector{0, UINT16_MAX}, std::vector{INT16_MIN, INT16_MAX}), + std::make_tuple(std::vector{0, UINT16_MAX}, std::vector{INT16_MIN, INT16_MAX}) + )); + assert(equal( + ListMinmax32(std::vector{0, UINT32_MAX}, std::vector{INT32_MIN, INT32_MAX}), + std::make_tuple(std::vector{0, UINT32_MAX}, std::vector{INT32_MIN, INT32_MAX}) + )); + assert(equal( + ListMinmax64(std::vector{0, UINT64_MAX}, std::vector{INT64_MIN, INT64_MAX}), + std::make_tuple(std::vector{0, UINT64_MAX}, std::vector{INT64_MIN, INT64_MAX}) + )); + assert(equal( + ListMinmaxFloat( + std::vector{FLT_MIN, FLT_MAX, -HUGE_VALF, HUGE_VALF}, + std::vector{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL} + ), + std::make_tuple( + std::vector{FLT_MIN, FLT_MAX, -HUGE_VALF, HUGE_VALF}, + std::vector{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL} + ) + )); +} diff --git a/tests/runtime/lists/test.cpp b/tests/runtime/lists/test.cpp new file mode 100644 index 000000000..830ad2cb6 --- /dev/null +++ b/tests/runtime/lists/test.cpp @@ -0,0 +1,136 @@ +#include +#include +#include +#include + +uint32_t exports::test::lists::to_test::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 +static bool equal(T const&a, S const& b) { + return a == b; +} +template +static bool equal(wit::span const&a, wit::span const& b) { + if (a.size() != b.size()) { return false; } + for (uint32_t i = 0; i +static bool equal(wit::vector const&a, wit::span const& b) { + return equal(a.get_view(), b); +} +template +static bool equal(wit::span const&a, wit::vector const& b) { + return equal(b, a); +} +template +static bool equal(wit::span const&a, std::vector const& b) { + return equal(a, wit::span(b)); +} +template +static bool equal(wit::vector const&a, std::vector const& b) { + return equal(a.get_view(), wit::span(b)); +} +// static bool equal(wit::vector const&a, std::vector const& b) { +// return equal(a.get_view(), wit::span(b)); +// } +template +static bool equal(std::tuple const&a, std::tuple const& b) { + return equal(std::get<0>(a), std::get<0>(b)) && equal(std::get<1>(a), std::get<1>(b)); +} + +void exports::test::lists::to_test::EmptyListParam(wit::span a) { + assert(a.empty()); +} + +void exports::test::lists::to_test::EmptyStringParam(std::string_view a) { + assert(a.empty()); +} + +wit::vector exports::test::lists::to_test::EmptyListResult() { + return wit::vector(); +} + +wit::string exports::test::lists::to_test::EmptyStringResult() { + return wit::string::from_view(std::string_view()); +} + +void exports::test::lists::to_test::ListParam(wit::span list) { + assert(equal(list, std::vector{1, 2, 3, 4})); +} + +void exports::test::lists::to_test::ListParam2(std::string_view ptr) { + assert(equal(ptr, std::string_view("foo"))); +} + +void exports::test::lists::to_test::ListParam3(wit::span 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::to_test::ListParam4(wit::span> 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"))); +} + +void exports::test::lists::to_test::ListParam5(wit::span const> a) { + +} + +void exports::test::lists::to_test::ListParamLarge(wit::span a) { + +} + +wit::vector exports::test::lists::to_test::ListResult() { + return wit::vector::from_view(wit::span(std::vector{1, 2, 3, 4, 5})); +} + +wit::string exports::test::lists::to_test::ListResult2() { + return wit::string::from_view("hello!"); +} + +wit::vector exports::test::lists::to_test::ListResult3() { + return wit::vector::from_view(wit::span(std::vector{wit::string::from_view("hello,"), wit::string::from_view("world!")})); +} + +wit::vector exports::test::lists::to_test::ListRoundtrip(wit::span x) { + return wit::vector::from_view(x); +} + +wit::string exports::test::lists::to_test::StringRoundtrip(std::string_view x) { + return wit::string::from_view(x); +} + +std::tuple, wit::vector> exports::test::lists::to_test::ListMinmax8(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} + +std::tuple, wit::vector> exports::test::lists::to_test::ListMinmax16(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} + +std::tuple, wit::vector> exports::test::lists::to_test::ListMinmax32(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} + +std::tuple, wit::vector> exports::test::lists::to_test::ListMinmax64(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} + +std::tuple, wit::vector> exports::test::lists::to_test::ListMinmaxFloat(wit::span a, wit::span b) { + return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); +} diff --git a/tests/runtime/lists/test.new.cpp b/tests/runtime/lists/test.new.cpp deleted file mode 100644 index f7a57cc6c..000000000 --- a/tests/runtime/lists/test.new.cpp +++ /dev/null @@ -1,185 +0,0 @@ -#include -#include -#include -#include - -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 -static bool equal(T const&a, S const& b) { - return a == b; -} -template -static bool equal(wit::span const&a, wit::span const& b) { - if (a.size() != b.size()) { return false; } - for (uint32_t i = 0; i -static bool equal(wit::vector const&a, wit::span const& b) { - return equal(a.get_view(), b); -} -template -static bool equal(wit::span const&a, wit::vector const& b) { - return equal(b, a); -} -template -static bool equal(wit::span const&a, std::vector const& b) { - return equal(a, wit::span(b)); -} -template -static bool equal(wit::vector const&a, std::vector const& b) { - return equal(a.get_view(), wit::span(b)); -} -static bool equal(wit::vector const&a, std::vector const& b) { - return equal(a.get_view(), wit::span(b)); -} -template -static bool equal(std::tuple const&a, std::tuple 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(std::vector())); - test::lists::test::EmptyStringParam(""); - assert(test::lists::test::EmptyListResult().empty()); - assert(test::lists::test::EmptyStringResult().empty()); - - test::lists::test::ListParam(std::vector{1, 2, 3, 4}); - test::lists::test::ListParam2("foo"); - test::lists::test::ListParam3(std::vector{"foo", "bar", "baz"}); - test::lists::test::ListParam4(std::vector>{ - std::vector{"foo", "bar"}, - std::vector{"baz"}, - }); - assert(equal(test::lists::test::ListResult(), std::vector{1, 2, 3, 4, 5})); - assert(equal(test::lists::test::ListResult2(), "hello!")); - assert(equal(test::lists::test::ListResult3(), std::vector{"hello,", "world!"})); - - assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector())), std::vector())); - assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'x'})), std::vector{'x'})); - assert(equal(test::lists::test::ListRoundtrip(wit::span(std::vector{'h', 'e', 'l', 'l', 'o'})), std::vector{'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{0, UINT8_MAX}, std::vector{INT8_MIN, INT8_MAX}), - std::make_tuple(std::vector{0, UINT8_MAX}, std::vector{INT8_MIN, INT8_MAX}) - )); - assert(equal( - test::lists::test::ListMinmax16(std::vector{0, UINT16_MAX}, std::vector{INT16_MIN, INT16_MAX}), - std::make_tuple(std::vector{0, UINT16_MAX}, std::vector{INT16_MIN, INT16_MAX}) - )); - assert(equal( - test::lists::test::ListMinmax32(std::vector{0, UINT32_MAX}, std::vector{INT32_MIN, INT32_MAX}), - std::make_tuple(std::vector{0, UINT32_MAX}, std::vector{INT32_MIN, INT32_MAX}) - )); - assert(equal( - test::lists::test::ListMinmax64(std::vector{0, UINT64_MAX}, std::vector{INT64_MIN, INT64_MAX}), - std::make_tuple(std::vector{0, UINT64_MAX}, std::vector{INT64_MIN, INT64_MAX}) - )); - assert(equal( - test::lists::test::ListMinmaxFloat( - std::vector{FLT_MIN, FLT_MAX, -HUGE_VALF, HUGE_VALF}, - std::vector{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL} - ), - std::make_tuple( - std::vector{FLT_MIN, FLT_MAX, -HUGE_VALF, HUGE_VALF}, - std::vector{DBL_MIN, DBL_MAX, -HUGE_VAL, HUGE_VAL} - ) - )); -} - - -void exports::test::lists::test::EmptyListParam(wit::span a) { - assert(a.empty()); -} - -void exports::test::lists::test::EmptyStringParam(std::string_view a) { - assert(a.empty()); -} - -wit::vector exports::test::lists::test::EmptyListResult() { - return wit::vector(); -} - -wit::string exports::test::lists::test::EmptyStringResult() { - return wit::string::from_view(std::string_view()); -} - -void exports::test::lists::test::ListParam(wit::span list) { - assert(equal(list, std::vector{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 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> 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 exports::test::lists::test::ListResult() { - return wit::vector::from_view(wit::span(std::vector{1, 2, 3, 4, 5})); -} - -wit::string exports::test::lists::test::ListResult2() { - return wit::string::from_view("hello!"); -} - -wit::vector exports::test::lists::test::ListResult3() { - return wit::vector::from_view(wit::span(std::vector{wit::string::from_view("hello,"), wit::string::from_view("world!")})); -} - -wit::vector exports::test::lists::test::ListRoundtrip(wit::span x) { - return wit::vector::from_view(x); -} - -wit::string exports::test::lists::test::StringRoundtrip(std::string_view x) { - return wit::string::from_view(x); -} - -std::tuple, wit::vector> exports::test::lists::test::ListMinmax8(wit::span a, wit::span b) { - return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); -} - -std::tuple, wit::vector> exports::test::lists::test::ListMinmax16(wit::span a, wit::span b) { - return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); -} - -std::tuple, wit::vector> exports::test::lists::test::ListMinmax32(wit::span a, wit::span b) { - return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); -} - -std::tuple, wit::vector> exports::test::lists::test::ListMinmax64(wit::span a, wit::span b) { - return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); -} - -std::tuple, wit::vector> exports::test::lists::test::ListMinmaxFloat(wit::span a, wit::span b) { - return std::make_tuple(wit::vector::from_view(a), wit::vector::from_view(b)); -} diff --git a/tests/runtime/results/runner.cpp b/tests/runtime/results/runner.cpp new file mode 100644 index 000000000..7ab0a236c --- /dev/null +++ b/tests/runtime/results/runner.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +template +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 const& a, std::optional const& b) { + if (a.has_value() != b.has_value()) return false; + if (a.has_value()) { + return equal(a.value(), b.value()); + } + return true; +} + +int main() +{ + using namespace ::test::options::to_test; + + OptionNoneParam(std::optional()); + OptionSomeParam(std::optional("foo")); + assert(!OptionNoneResult()); + assert(equal(OptionSomeResult(), std::optional("foo"))); + assert(equal(OptionRoundtrip(std::optional("foo")), std::optional("foo"))); + assert(equal(DoubleOptionRoundtrip(std::optional>(std::optional(42))), std::optional>(std::optional(42)))); + assert(equal(DoubleOptionRoundtrip(std::optional>(std::optional())), std::optional>(std::optional()))); + assert(equal(DoubleOptionRoundtrip(std::optional>()), std::optional>())); +} From e7ad032917000c2ed1d499e744ef01e1041329ad Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 10 May 2025 20:19:52 +0200 Subject: [PATCH 588/672] fix list test --- crates/cpp/src/lib.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index fbeb093a0..37807bf37 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2309,9 +2309,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if realloc.is_none() { results.push(ptr); } else { - if matches!(self.variant, AbiVariant::GuestImport) { - uwriteln!(self.src, "{}.leak();\n", operands[0]); - } + uwriteln!(self.src, "{}.leak();\n", operands[0]); results.push(ptr); } results.push(len); @@ -2333,9 +2331,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if realloc.is_none() { results.push(ptr); } else { - if matches!(self.variant, AbiVariant::GuestImport) { - uwriteln!(self.src, "{}.leak();\n", operands[0]); - } + uwriteln!(self.src, "{}.leak();\n", operands[0]); results.push(ptr); } results.push(len); @@ -2359,9 +2355,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if realloc.is_none() { results.push(ptr); } else { - if matches!(self.variant, AbiVariant::GuestImport) { - uwriteln!(self.src, "{}.leak();\n", operands[0]); - } + uwriteln!(self.src, "{}.leak();\n", operands[0]); results.push(ptr); } results.push(len); From f077354222de3e34c652fa39a83d0e1cef7016b3 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 10 May 2025 23:14:20 +0200 Subject: [PATCH 589/672] incomplete results test --- .../{test-new.cpp => intermediate.cpp} | 0 tests/runtime/results/leaf.cpp | 56 +++++++++++++++++++ tests/runtime/results/runner.cpp | 42 +++++++++++++- 3 files changed, 97 insertions(+), 1 deletion(-) rename tests/runtime/results/{test-new.cpp => intermediate.cpp} (100%) create mode 100644 tests/runtime/results/leaf.cpp diff --git a/tests/runtime/results/test-new.cpp b/tests/runtime/results/intermediate.cpp similarity index 100% rename from tests/runtime/results/test-new.cpp rename to tests/runtime/results/intermediate.cpp diff --git a/tests/runtime/results/leaf.cpp b/tests/runtime/results/leaf.cpp new file mode 100644 index 000000000..68ea1e46a --- /dev/null +++ b/tests/runtime/results/leaf.cpp @@ -0,0 +1,56 @@ +#include +#include + +template +bool equal(T const&a, T const&b) { + return a==b; +} + +std::expected exports::test::results::test::StringError(float a) { + return ::test::results::test::StringError(a); +} + +std::expected 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 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 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 exports::test::results::test::EmptyError(uint32_t a) { + return ::test::results::test::EmptyError(a); +} + +std::expected, wit::string> exports::test::results::test::DoubleError(uint32_t a) { + return ::test::results::test::DoubleError(a); +} diff --git a/tests/runtime/results/runner.cpp b/tests/runtime/results/runner.cpp index 7ab0a236c..3cda4d923 100644 --- a/tests/runtime/results/runner.cpp +++ b/tests/runtime/results/runner.cpp @@ -19,7 +19,47 @@ static bool equal(std::optional const& a, std::optional(1.0)); + + assert_eq!(enum_error(0.0), Err(E::A)); + assert_eq!(enum_error(1.0), Ok(1.0)); + + 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())); OptionNoneParam(std::optional()); OptionSomeParam(std::optional("foo")); From 3f8a7b6bcbcfd50d7af379bf6995c3725ae80e97 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 14 May 2025 00:21:42 +0200 Subject: [PATCH 590/672] better variant support --- crates/cpp/src/lib.rs | 74 ++++++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 37807bf37..344616dc6 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -752,8 +752,13 @@ impl SourceWithState { self.src.push_str("::"); } } - for i in target.iter().skip(same) { - uwrite!(self.src, "{i}::"); + if same == target.len() && self.namespace.len() != target.len() && same > 0 { + // namespace is parent, qualify at least one namespace (and cross fingers) + uwrite!(self.src, "{}::", target[same - 1]); + } else { + for i in target.iter().skip(same) { + uwrite!(self.src, "{i}::"); + } } } } @@ -1535,11 +1540,14 @@ impl CppInterfaceGenerator<'_> { "std::optional<".to_string() + &self.type_name(o, from_namespace, flavor) + ">" } TypeDefKind::Result(r) => { + let err_type = r.err.as_ref().map_or(String::from("wit::Void"), |ty| { + self.type_name(ty, from_namespace, flavor) + }); 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) + + &err_type + ">" } TypeDefKind::List(ty) => { @@ -1903,28 +1911,32 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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; ") + 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} {{"); + let mut inner_namespace = namespc.clone(); + inner_namespace.push(pascal.clone()); + 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, &inner_namespace, 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; } - 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( @@ -2900,7 +2912,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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(); + let err_result; if result.ok.is_none() { ok.clear(); } else { @@ -2908,6 +2920,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } if result.err.is_none() { err.clear(); + err_result = String::from("wit::Void{}"); } else { err_result = format!("std::move({})", err_results[0]); } @@ -2916,23 +2929,26 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { &self.namespace, Flavor::InStruct, ); - let err_type = self.gen.optional_type_name( - result.err.as_ref(), - &self.namespace, - Flavor::InStruct, - ); + let err_type = result.err.as_ref().map_or(String::from("wit::Void"), |ty| { + self.gen.type_name(ty, &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); + let ok_assign = if result.ok.is_some() { + format!("{resultname}.emplace({ok_result});") + } else { + String::new() + }; uwriteln!( self.src, "{full_type} {resultname}; if ({operand}==0) {{ {ok} - {resultname}.emplace({ok_result}); + {ok_assign} }} else {{ {err} {resultname}={err_type}{{{err_result}}}; From 77afda0df2c2289c885e88a2375bed40dce74ddc Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 14 May 2025 00:22:10 +0200 Subject: [PATCH 591/672] small fix --- tests/runtime/results/intermediate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/runtime/results/intermediate.cpp b/tests/runtime/results/intermediate.cpp index 68ea1e46a..5aca8eaf6 100644 --- a/tests/runtime/results/intermediate.cpp +++ b/tests/runtime/results/intermediate.cpp @@ -1,5 +1,5 @@ #include -#include +#include template bool equal(T const&a, T const&b) { From c3621c51867181aa2bb5e803988012912f68910b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 14 May 2025 00:22:35 +0200 Subject: [PATCH 592/672] use c++17 to compile --- crates/test/src/cpp.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index 1affd1ecf..80467f0d8 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -101,6 +101,7 @@ impl LanguageMethods for Cpp17 { .arg("-Wextra") .arg("-Werror") .arg("-Wno-unused-parameter") + .arg("-std=c++17") .arg("-c") .arg("-o") .arg(&bindings_object); @@ -125,6 +126,7 @@ impl LanguageMethods for Cpp17 { .arg("-Werror") .arg("-Wc++-compat") .arg("-Wno-unused-parameter") + .arg("-std=c++17") .arg("-g") .arg("-o") .arg(&compile.output); From 515c4eaa53e9146bc8afdedcc140a17cf2a4716d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 14 May 2025 00:26:16 +0200 Subject: [PATCH 593/672] clarify EOF logic --- crates/symmetric_executor/wit/executor.wit | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/symmetric_executor/wit/executor.wit b/crates/symmetric_executor/wit/executor.wit index 1bb032a95..7af955a8e 100644 --- a/crates/symmetric_executor/wit/executor.wit +++ b/crates/symmetric_executor/wit/executor.wit @@ -82,17 +82,18 @@ interface symmetric-stream { // create a new instance e.g. for reading or tasks clone: func() -> stream-obj; // reading (in roughly chronological order) + /// indicates EOF is-write-closed: func() -> bool; start-reading: func(buffer: buffer); write-ready-activate: func(); read-ready-subscribe: func() -> event-subscription; - // none is EOF + /// none is EOF when read-ready, no data when polled read-result: func() -> option; // writing is-ready-to-write: func() -> bool; write-ready-subscribe: func() -> event-subscription; start-writing: func() -> buffer; - // none is EOF + /// none is EOF finish-writing: func(buffer: option); read-ready-activate: func(); } From 16833403af7fdc977a4eb043fc02610d95ff352e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 14 May 2025 00:28:03 +0200 Subject: [PATCH 594/672] handle no data when polling --- crates/symmetric_executor/symmetric_stream/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index 476576e4a..16c21e1c1 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -123,7 +123,7 @@ impl GuestStreamObj for StreamObj { "Stream::read_result {:x} {addr:x?} {size}", self.0.read_ready_event_send.handle() ); - if addr as usize == EOF_MARKER { + if addr as usize == EOF_MARKER || (addr == null_mut() && size == results::BLOCKED) { None } else { Some(symmetric_stream::Buffer::new(Buffer { From 4a2bd495409c6a0e554ee3f1b44569ff3861dfe9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 14 May 2025 00:29:06 +0200 Subject: [PATCH 595/672] handle negative difference in debug display --- crates/symmetric_executor/src/lib.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index da94d8faa..1b5f9518b 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -389,13 +389,13 @@ impl QueuedEvent { callback.1 as usize ), EventType::SystemTime(system_time) => { - let diff = system_time.duration_since(SystemTime::now()).unwrap(); + let diff = match system_time.duration_since(SystemTime::now()) { + Ok(diff) => format!("{}.{}", diff.as_secs(), diff.subsec_nanos()), + Err(err) => format!("{err}"), + }; println!( - "register(Time {}.{}, {:x},{:x})", - diff.as_secs(), - diff.subsec_nanos(), - callback.0 as usize, - callback.1 as usize + "register(Time {}, {:x},{:x})", + diff, callback.0 as usize, callback.1 as usize ); } } From 33525ad58cca09c7efe89c49cd2636ac186e5998 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 14 May 2025 00:32:52 +0200 Subject: [PATCH 596/672] link to either symmetric or canonical runtime --- crates/symmetric_executor/Cargo.toml | 4 +--- crates/symmetric_executor/dummy-bindgen/Cargo.toml | 11 +++++++++-- crates/symmetric_executor/dummy-bindgen/src/lib.rs | 6 +++++- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/crates/symmetric_executor/Cargo.toml b/crates/symmetric_executor/Cargo.toml index 1cfed0fe6..e31980e6d 100644 --- a/crates/symmetric_executor/Cargo.toml +++ b/crates/symmetric_executor/Cargo.toml @@ -1,7 +1,7 @@ [workspace] package.version = "0.1.0" package.edition = "2021" -members = [ "dummy-rt","symmetric_stream","rust-client" ] +members = [ "dummy-rt","symmetric_stream","rust-client","dummy-bindgen" ] [package] name = "symmetric_executor" @@ -11,8 +11,6 @@ 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" diff --git a/crates/symmetric_executor/dummy-bindgen/Cargo.toml b/crates/symmetric_executor/dummy-bindgen/Cargo.toml index 30173c5a7..b07850c0a 100644 --- a/crates/symmetric_executor/dummy-bindgen/Cargo.toml +++ b/crates/symmetric_executor/dummy-bindgen/Cargo.toml @@ -4,5 +4,12 @@ version.workspace = true edition.workspace = true [dependencies] -wit-bindgen-symmetric-rt = { path = "../rust-client" } -dummy-rt = { path = "../dummy-rt" } +wit-bindgen-symmetric-rt = { path = "../rust-client", optional = true } +dummy-rt = { path = "../dummy-rt", optional = true } +original = { path = "../../guest-rust", optional = true, package = "wit-bindgen" } + +[features] +# no default gives you the original wit-bindgen crate with rt +default = [ "symmetric" ] +symmetric = [ "dep:dummy-rt", "dep:wit-bindgen-symmetric-rt" ] +canonical = [ "dep:original" ] diff --git a/crates/symmetric_executor/dummy-bindgen/src/lib.rs b/crates/symmetric_executor/dummy-bindgen/src/lib.rs index 72695811a..3e4ea18cf 100644 --- a/crates/symmetric_executor/dummy-bindgen/src/lib.rs +++ b/crates/symmetric_executor/dummy-bindgen/src/lib.rs @@ -1,6 +1,10 @@ // this crate tries to minimize dependencies for symmetric bindings +#[cfg(feature = "symmetric")] pub mod rt { pub use dummy_rt::rt::maybe_link_cabi_realloc; - pub use wit_bindgen_symmetric_rt::async_support; + pub use wit_bindgen_symmetric_rt::{async_support, run, EventGenerator, EventSubscription}; } + +#[cfg(feature = "canonical")] +pub use original::rt; From 148b9eb49c0090378a0a2ba1b2f436bbbfa62226 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 May 2025 10:47:34 +0200 Subject: [PATCH 597/672] remove unnecessary move --- crates/cpp/src/lib.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 344616dc6..cb9a08713 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2202,6 +2202,15 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } +fn move_if_necessary(arg: &str) -> String { + // if it is a name of a variable move it + if arg.chars().all(char::is_alphanumeric) { + format!("std::move({arg})") + } else { + arg.into() + } +} + impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { type Operand = String; @@ -2467,7 +2476,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { )); } } else { - results.push(format!("std::move({result})")); + results.push(move_if_necessary(&result)); } } abi::Instruction::IterElem { .. } => results.push("IterElem".to_string()), @@ -2486,9 +2495,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // 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(&move_if_necessary(&val)); } result.push_str("}"); results.push(result); @@ -2916,13 +2923,15 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { if result.ok.is_none() { ok.clear(); } else { - ok_result = format!("std::move({})", ok_results[0]); + ok_result = move_if_necessary(&ok_results[0]); + // format!("std::move({})", ok_results[0]); } if result.err.is_none() { err.clear(); err_result = String::from("wit::Void{}"); } else { - err_result = format!("std::move({})", err_results[0]); + err_result = move_if_necessary(&err_results[0]); + // format!("std::move({})", err_results[0]); } let ok_type = self.gen.optional_type_name( result.ok.as_ref(), From b3ed2154f6ba8a54105808e0a28803528d016cc8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 May 2025 12:15:47 +0200 Subject: [PATCH 598/672] variant lowering --- crates/cpp/src/lib.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index cb9a08713..4fa4ed0af 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2664,11 +2664,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::VariantLower { variant, results: result_types, + ty: var_ty, + name: _var_name, .. } => { - //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()..) @@ -2689,7 +2688,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { variant_results.push(name); } - let expr_to_match = format!("({}).tag", operands[0]); + let expr_to_match = format!("({}).variants.index()", operands[0]); + let elem_ns = + self.gen + .type_name(&Type::Id(*var_ty), &self.namespace, Flavor::InStruct); uwriteln!(self.src, "switch ((int32_t) {}) {{", expr_to_match); for (i, ((case, (block, block_results)), payload)) in @@ -2698,17 +2700,16 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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!( + let case = format!("{elem_ns}::{}", case.name.to_pascal_case()); + uwriteln!( self.src, - "const {} *{} = &({}).val", + "const {} &{} = std::get<{case}>({}.variants).value;", 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) { From 3f340465f74db75a8eb26b70547d3c74dfa70394 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 May 2025 12:24:11 +0200 Subject: [PATCH 599/672] correct record lifting --- crates/cpp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 4fa4ed0af..468040c80 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2495,7 +2495,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { // self.typename_lift(*ty); result.push_str("{"); for (_field, val) in record.fields.iter().zip(operands) { - result.push_str(&move_if_necessary(&val)); + result.push_str(&(move_if_necessary(&val) + ", ")); } result.push_str("}"); results.push(result); From 6bd2c34fd6072b83c38099e3b0351d6f524a0ef2 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 May 2025 14:11:37 +0200 Subject: [PATCH 600/672] fix variant lifting --- crates/cpp/src/lib.rs | 76 +++++++++++++++--------------------------- crates/test/src/cpp.rs | 11 +++++- 2 files changed, 36 insertions(+), 51 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 468040c80..538e508e5 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2720,61 +2720,37 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src.push_str("}\n"); } abi::Instruction::VariantLift { variant, ty, .. } => { - let mut result = String::new(); - result.push_str("{"); + let blocks = self + .blocks + .drain(self.blocks.len() - variant.cases.len()..) + .collect::>(); - let named_enum = variant.cases.iter().all(|c| c.ty.is_none()); - // let blocks = self - // .blocks - // .drain(self.blocks.len() - variant.cases.len()..) - // .collect::>(); - let op0 = &operands[0]; + let ty = self + .gen + .type_name(&Type::Id(*ty), &self.namespace, Flavor::InStruct); + let resultno = self.tmp(); + let result = format!("variant{resultno}"); - 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("}"); - } + uwriteln!(self.src, "{ty} {result};"); - // if named_enum { - // result.push_str("#[cfg(debug_assertions)]"); - // } - let blocks: Vec = 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")); - // } + let op0 = &operands[0]; + + uwriteln!(self.src, "switch ({op0}) {{"); + for (i, (case, (block, block_results))) in + variant.cases.iter().zip(blocks).enumerate() + { + let tp = case.name.clone().to_pascal_case(); + uwriteln!(self.src, "case {i}: {block}"); + uwriteln!( + self.src, + "{result}.variants = {ty}::{tp}{{{}}};", + move_if_necessary(&block_results[0]) + ); + uwriteln!(self.src, "break;"); } - // result.push_str("#[cfg(debug_assertions)]"); - // result.push_str("_ => panic!(\"invalid enum discriminant\"),\n"); - result.push_str("}"); - result.push_str("}"); + uwriteln!(self.src, "}}"); + // uwriteln!(self.src, "}}"); - result.push_str("}"); results.push(result); } abi::Instruction::EnumLower { .. } => results.push(format!("int32_t({})", operands[0])), diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index 80467f0d8..4e81023d3 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -79,10 +79,15 @@ impl LanguageMethods for Cpp17 { let compiler = clangpp(runner); let config = compile.component.deserialize_lang_config::()?; let cwd = std::env::current_dir()?; - let mut helper_dir = cwd; + let mut helper_dir = cwd.clone(); helper_dir.push("crates"); helper_dir.push("cpp"); helper_dir.push("helper-types"); + // for expected + let mut helper_dir2 = cwd; + helper_dir2.push("crates"); + helper_dir2.push("cpp"); + helper_dir2.push("test_headers"); // Compile the C-based bindings to an object file. let bindings_object = compile.output.with_extension("bindings.o"); @@ -96,6 +101,8 @@ impl LanguageMethods for Cpp17 { .arg(&compile.bindings_dir) .arg("-I") .arg(helper_dir.to_str().unwrap().to_string()) + .arg("-I") + .arg(helper_dir2.to_str().unwrap().to_string()) .arg("-fno-exceptions") .arg("-Wall") .arg("-Wextra") @@ -120,6 +127,8 @@ impl LanguageMethods for Cpp17 { .arg(&compile.bindings_dir) .arg("-I") .arg(helper_dir.to_str().unwrap().to_string()) + .arg("-I") + .arg(helper_dir2.to_str().unwrap().to_string()) .arg("-fno-exceptions") .arg("-Wall") .arg("-Wextra") From 4c6cd5cefc1b3162c8bf7ed5e9b8ec11272b6e22 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 May 2025 14:50:40 +0200 Subject: [PATCH 601/672] results test passing (but incomplete) --- intermediate.cpp | 371 +++++++++++++++++++++++++++++++ tests/runtime/results/leaf.cpp | 84 ++++--- tests/runtime/results/runner.cpp | 92 ++++---- 3 files changed, 465 insertions(+), 82 deletions(-) create mode 100644 intermediate.cpp diff --git a/intermediate.cpp b/intermediate.cpp new file mode 100644 index 000000000..ca960a114 --- /dev/null +++ b/intermediate.cpp @@ -0,0 +1,371 @@ +// Generated by `wit-bindgen` 0.42.1. DO NOT EDIT! + +// Ensure that the *_component_type.o object is linked in +#ifdef __wasm32__ +extern void __component_type_object_force_link_intermediate(void); +void __component_type_object_force_link_intermediate_public_use_in_this_compilation_unit(void) { + __component_type_object_force_link_intermediate(); +} +#endif +#include "intermediate_cpp.h" +#include // 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("test:results/test"))) +__attribute__((import_name("string-error"))) +void testX3AresultsX2FtestX00string_error(float, uint8_t *); +extern "C" __attribute__((import_module("test:results/test"))) +__attribute__((import_name("enum-error"))) +void testX3AresultsX2FtestX00enum_error(float, uint8_t *); +extern "C" __attribute__((import_module("test:results/test"))) +__attribute__((import_name("record-error"))) +void testX3AresultsX2FtestX00record_error(float, uint8_t *); +extern "C" __attribute__((import_module("test:results/test"))) +__attribute__((import_name("variant-error"))) +void testX3AresultsX2FtestX00variant_error(float, uint8_t *); +extern "C" __attribute__((import_module("test:results/test"))) +__attribute__((import_name("empty-error"))) +void testX3AresultsX2FtestX00empty_error(int32_t, uint8_t *); +extern "C" __attribute__((import_module("test:results/test"))) +__attribute__((import_name("double-error"))) +void testX3AresultsX2FtestX00double_error(int32_t, uint8_t *); +std::expected test::results::test::StringError(float a) +{ + uintptr_t ret_area[((3*sizeof(void*))+sizeof(uintptr_t)-1)/sizeof(uintptr_t)]; + uint8_t* ptr0 = (uint8_t*)(&ret_area); + testX3AresultsX2FtestX00string_error((float(a)), ptr0); + std::expected result2; + if ((int32_t) (*((uint8_t*) (ptr0 + 0)))==0) { + + result2.emplace((float(*((float*) (ptr0 + sizeof(void*)))))); + } else { + auto len1 = *((size_t*) (ptr0 + (2*sizeof(void*)))); + + + result2=std::unexpected{wit::string((char const*)(*((uint8_t**) (ptr0 + sizeof(void*)))), len1)}; + } + auto result3 = result2; + return result3; +} +std::expected test::results::test::EnumError(float a) +{ + uint32_t ret_area[(8+sizeof(uint32_t)-1)/sizeof(uint32_t)]; + uint8_t* ptr0 = (uint8_t*)(&ret_area); + testX3AresultsX2FtestX00enum_error((float(a)), ptr0); + std::expected result1; + if ((int32_t) (*((uint8_t*) (ptr0 + 0)))==0) { + + result1.emplace((float(*((float*) (ptr0 + 4))))); + } else { + + result1=std::unexpected{(E)(int32_t) (*((uint8_t*) (ptr0 + 4)))}; + } + auto result2 = result1; + return result2; +} +std::expected test::results::test::RecordError(float a) +{ + uint32_t ret_area[(12+sizeof(uint32_t)-1)/sizeof(uint32_t)]; + uint8_t* ptr0 = (uint8_t*)(&ret_area); + testX3AresultsX2FtestX00record_error((float(a)), ptr0); + std::expected result3; + if ((int32_t) (*((uint8_t*) (ptr0 + 0)))==0) { + + result3.emplace((float(*((float*) (ptr0 + 4))))); + } else { + int32_t l1 = *((int32_t const*)(ptr0 + 4)); + int32_t l2 = *((int32_t const*)(ptr0 + 8)); + + result3=std::unexpected{E2{(uint32_t(l1)), (uint32_t(l2)), }}; + } + auto result4 = result3; + return result4; +} +std::expected test::results::test::VariantError(float a) +{ + uint32_t ret_area[(16+sizeof(uint32_t)-1)/sizeof(uint32_t)]; + uint8_t* ptr0 = (uint8_t*)(&ret_area); + testX3AresultsX2FtestX00variant_error((float(a)), ptr0); + std::expected result3; + if ((int32_t) (*((uint8_t*) (ptr0 + 0)))==0) { + float l1 = *((float const*)(ptr0 + 4)); + result3.emplace(l1); + } else { + switch (*((uint8_t const*)(ptr0 + 4))) { + case 0: { + result3 = std::unexpected{E3{std::variant(E3::E1{E(*(const uint8_t*)(ptr0 + 8))})}}; + break; + } + case 1: { + uint32_t l1 = *((uint32_t const*)(ptr0 + 8)); + uint32_t l2 = *((uint32_t const*)(ptr0 + 12)); + result3 = std::unexpected{E3{std::variant(E3::E2{test::E2{l1,l2}})}}; + break; + } + } + } + auto result4 = std::move(result3); + return result4; +} +std::expected test::results::test::EmptyError(uint32_t a) +{ + uint32_t ret_area[(8+sizeof(uint32_t)-1)/sizeof(uint32_t)]; + uint8_t* ptr0 = (uint8_t*)(&ret_area); + testX3AresultsX2FtestX00empty_error((int32_t(a)), ptr0); + std::expected result2; + if ((int32_t) (*((uint8_t*) (ptr0 + 0)))==0) { + int32_t l1 = *((int32_t const*)(ptr0 + 4)); + + result2.emplace((uint32_t(l1))); + } else { + + result2=std::unexpected{wit::Void{}}; + } + auto result3 = result2; + return result3; +} +std::expected, wit::string> test::results::test::DoubleError(uint32_t a) +{ + uintptr_t ret_area[((4*sizeof(void*))+sizeof(uintptr_t)-1)/sizeof(uintptr_t)]; + uint8_t* ptr0 = (uint8_t*)(&ret_area); + testX3AresultsX2FtestX00double_error((int32_t(a)), ptr0); + std::expected, wit::string> result4; + if ((int32_t) (*((uint8_t*) (ptr0 + 0)))==0) { + std::expected result2; + if ((int32_t) (*((uint8_t*) (ptr0 + sizeof(void*))))==0) { + + + } else { + auto len1 = *((size_t*) (ptr0 + (3*sizeof(void*)))); + + + result2=std::unexpected{wit::string((char const*)(*((uint8_t**) (ptr0 + (2*sizeof(void*))))), len1)}; + } + + result4.emplace(std::move(result2)); + } else { + auto len3 = *((size_t*) (ptr0 + (2*sizeof(void*)))); + + + result4=std::unexpected{wit::string((char const*)(*((uint8_t**) (ptr0 + sizeof(void*)))), len3)}; + } + auto result5 = result4; + return result5; +} +extern "C" __attribute__((__export_name__("test:results/test#string-error"))) +uint8_t * testX3AresultsX2FtestX23string_error(float arg0) +{ + std::vector _deallocate; + auto result0 = exports::test::results::test::StringError((float(arg0))); + for (auto i: _deallocate) { free(i); } + + _deallocate.clear(); + static uintptr_t ret_area[((3*sizeof(void*))+sizeof(uintptr_t)-1)/sizeof(uintptr_t)]; + uint8_t* ptr1 = (uint8_t*)(&ret_area); + if ((result0).has_value()) { + float payload2 = std::move(result0).value(); + *((int8_t*)(ptr1 + 0)) = (int32_t(0)); + *((float*)(ptr1 + sizeof(void*))) = (float(payload2)); + } else { + wit::string payload3 = std::move(result0).error(); + *((int8_t*)(ptr1 + 0)) = (int32_t(1)); + auto const&vec4 = payload3; + auto ptr4 = (uint8_t*)(vec4.data()); + auto len4 = (size_t)(vec4.size()); + payload3.leak(); + + *((size_t*)(ptr1 + (2*sizeof(void*)))) = len4; + *((uint8_t**)(ptr1 + sizeof(void*))) = ptr4; + } + return ptr1; +} +extern "C" __attribute__((__weak__, __export_name__("cabi_post_test:results/test#string-error"))) +void cabi_post_testX3AresultsX2FtestX23string_error(uint8_t * arg0) { + switch ((int32_t) (int32_t) (*((uint8_t*) (arg0 + 0)))) { + case 0: { + break; + } + case 1: { + if ((*((size_t*) (arg0 + (2*sizeof(void*))))) > 0) { + wit::string::drop_raw((void*) (*((uint8_t**) (arg0 + sizeof(void*))))); + } + break; + } + } +} +extern "C" __attribute__((__export_name__("test:results/test#enum-error"))) +uint8_t * testX3AresultsX2FtestX23enum_error(float arg0) +{ + std::vector _deallocate; + auto result0 = exports::test::results::test::EnumError((float(arg0))); + for (auto i: _deallocate) { free(i); } + + _deallocate.clear(); + static uint32_t ret_area[(8+sizeof(uint32_t)-1)/sizeof(uint32_t)]; + uint8_t* ptr1 = (uint8_t*)(&ret_area); + if ((result0).has_value()) { + float payload2 = std::move(result0).value(); + *((int8_t*)(ptr1 + 0)) = (int32_t(0)); + *((float*)(ptr1 + 4)) = (float(payload2)); + } else { + test::results::test::E payload3 = std::move(result0).error(); + *((int8_t*)(ptr1 + 0)) = (int32_t(1)); + *((int8_t*)(ptr1 + 4)) = int32_t(payload3); + } + return ptr1; +} +extern "C" __attribute__((__export_name__("test:results/test#record-error"))) +uint8_t * testX3AresultsX2FtestX23record_error(float arg0) +{ + std::vector _deallocate; + auto result0 = exports::test::results::test::RecordError((float(arg0))); + for (auto i: _deallocate) { free(i); } + + _deallocate.clear(); + static uint32_t ret_area[(12+sizeof(uint32_t)-1)/sizeof(uint32_t)]; + uint8_t* ptr1 = (uint8_t*)(&ret_area); + if ((result0).has_value()) { + float payload2 = std::move(result0).value(); + *((int8_t*)(ptr1 + 0)) = (int32_t(0)); + *((float*)(ptr1 + 4)) = (float(payload2)); + } else { + test::results::test::E2 payload3 = std::move(result0).error(); + *((int8_t*)(ptr1 + 0)) = (int32_t(1)); + *((int32_t*)(ptr1 + 4)) = (int32_t((payload3).line)); + *((int32_t*)(ptr1 + 8)) = (int32_t((payload3).column)); + } + return ptr1; +} +extern "C" __attribute__((__export_name__("test:results/test#variant-error"))) +uint8_t * testX3AresultsX2FtestX23variant_error(float arg0) +{ + std::vector _deallocate; + auto result0 = exports::test::results::test::VariantError((float(arg0))); + for (auto i: _deallocate) { free(i); } + + _deallocate.clear(); + static uint32_t ret_area[(16+sizeof(uint32_t)-1)/sizeof(uint32_t)]; + uint8_t* ptr1 = (uint8_t*)(&ret_area); + if ((result0).has_value()) { + float payload2 = std::move(result0).value(); + *((int8_t*)(ptr1 + 0)) = (int32_t(0)); + *((float*)(ptr1 + 4)) = (float(payload2)); + } else { + test::results::test::E3 payload3 = std::move(result0).error(); + *((int8_t*)(ptr1 + 0)) = (int32_t(1)); + switch ((int32_t) (payload3).variants.index()) { + case 0: { + const test::results::test::E &payload4 = std::get(payload3.variants).value; + *((int8_t*)(ptr1 + 4)) = (int32_t(0)); + *((int8_t*)(ptr1 + 8)) = int32_t(payload4); + break; + } + case 1: { + const test::results::test::E2 &payload5 = std::get(payload3.variants).value; + *((int8_t*)(ptr1 + 4)) = (int32_t(1)); + *((int32_t*)(ptr1 + 8)) = (int32_t((payload5).line)); + *((int32_t*)(ptr1 + 12)) = (int32_t((payload5).column)); + break; + } + } + } + return ptr1; +} +extern "C" __attribute__((__export_name__("test:results/test#empty-error"))) +uint8_t * testX3AresultsX2FtestX23empty_error(int32_t arg0) +{ + std::vector _deallocate; + auto result0 = exports::test::results::test::EmptyError((uint32_t(arg0))); + for (auto i: _deallocate) { free(i); } + + _deallocate.clear(); + static uint32_t ret_area[(8+sizeof(uint32_t)-1)/sizeof(uint32_t)]; + uint8_t* ptr1 = (uint8_t*)(&ret_area); + if ((result0).has_value()) { + uint32_t payload2 = std::move(result0).value(); + *((int8_t*)(ptr1 + 0)) = (int32_t(0)); + *((int32_t*)(ptr1 + 4)) = (int32_t(payload2)); + } else { + + *((int8_t*)(ptr1 + 0)) = (int32_t(1)); + } + return ptr1; +} +extern "C" __attribute__((__export_name__("test:results/test#double-error"))) +uint8_t * testX3AresultsX2FtestX23double_error(int32_t arg0) +{ + std::vector _deallocate; + auto result0 = exports::test::results::test::DoubleError((uint32_t(arg0))); + for (auto i: _deallocate) { free(i); } + + _deallocate.clear(); + static uintptr_t ret_area[((4*sizeof(void*))+sizeof(uintptr_t)-1)/sizeof(uintptr_t)]; + uint8_t* ptr1 = (uint8_t*)(&ret_area); + if ((result0).has_value()) { + std::expected payload2 = std::move(result0).value(); + *((int8_t*)(ptr1 + 0)) = (int32_t(0)); + if ((payload2).has_value()) { + + *((int8_t*)(ptr1 + sizeof(void*))) = (int32_t(0)); + } else { + wit::string payload4 = std::move(payload2).error(); + *((int8_t*)(ptr1 + sizeof(void*))) = (int32_t(1)); + auto const&vec5 = payload4; + auto ptr5 = (uint8_t*)(vec5.data()); + auto len5 = (size_t)(vec5.size()); + payload4.leak(); + + *((size_t*)(ptr1 + (3*sizeof(void*)))) = len5; + *((uint8_t**)(ptr1 + (2*sizeof(void*)))) = ptr5; + } + } else { + wit::string payload6 = std::move(result0).error(); + *((int8_t*)(ptr1 + 0)) = (int32_t(1)); + auto const&vec7 = payload6; + auto ptr7 = (uint8_t*)(vec7.data()); + auto len7 = (size_t)(vec7.size()); + payload6.leak(); + + *((size_t*)(ptr1 + (2*sizeof(void*)))) = len7; + *((uint8_t**)(ptr1 + sizeof(void*))) = ptr7; + } + return ptr1; +} +extern "C" __attribute__((__weak__, __export_name__("cabi_post_test:results/test#double-error"))) +void cabi_post_testX3AresultsX2FtestX23double_error(uint8_t * arg0) { + switch ((int32_t) (int32_t) (*((uint8_t*) (arg0 + 0)))) { + case 0: { + switch ((int32_t) (int32_t) (*((uint8_t*) (arg0 + sizeof(void*))))) { + case 0: { + break; + } + case 1: { + if ((*((size_t*) (arg0 + (3*sizeof(void*))))) > 0) { + wit::string::drop_raw((void*) (*((uint8_t**) (arg0 + (2*sizeof(void*)))))); + } + break; + } + } + break; + } + case 1: { + if ((*((size_t*) (arg0 + (2*sizeof(void*))))) > 0) { + wit::string::drop_raw((void*) (*((uint8_t**) (arg0 + sizeof(void*))))); + } + break; + } + } +} + +// Component Adapters diff --git a/tests/runtime/results/leaf.cpp b/tests/runtime/results/leaf.cpp index 68ea1e46a..edc91da94 100644 --- a/tests/runtime/results/leaf.cpp +++ b/tests/runtime/results/leaf.cpp @@ -1,5 +1,5 @@ #include -#include +#include template bool equal(T const&a, T const&b) { @@ -7,50 +7,70 @@ bool equal(T const&a, T const&b) { } std::expected exports::test::results::test::StringError(float a) { - return ::test::results::test::StringError(a); + if (a==0.0) { + return std::unexpected(wit::string::from_view("zero")); + } + else { + return a; + } } std::expected 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); } + if (a==0.0) { + return std::unexpected(::test::results::test::E::kA); + } + else { + return a; + } } std::expected 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 }); + if (a==0.0) { + return std::unexpected(::test::results::test::E2{420, 0}); + } + else if (a==1.0) { + return std::unexpected(::test::results::test::E2{77, 2}); + } + else { + return a; + } } std::expected 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 })) - // } - // } + if (a==0.0) { + return std::unexpected(::test::results::test::E3{::test::results::test::E3::E2{::test::results::test::E2{420, 0}}}); + } + else if (a==1.0) { + return std::unexpected(::test::results::test::E3{::test::results::test::E3::E1{::test::results::test::E::kB}}); + } + else if (a==2.0) { + return std::unexpected(::test::results::test::E3{::test::results::test::E3::E1{::test::results::test::E::kC}}); + } + else { + return a; + } } std::expected exports::test::results::test::EmptyError(uint32_t a) { - return ::test::results::test::EmptyError(a); + if (a==0) { + return std::unexpected(wit::Void{}); + } + else if (a==1) { + return 42; + } + else { + return a; + } } std::expected, wit::string> exports::test::results::test::DoubleError(uint32_t a) { - return ::test::results::test::DoubleError(a); + if (a==0) { + return std::expected(); + } + else if (a==1) { + return std::expected(std::unexpected(wit::string::from_view("one"))); + } + else { + return std::unexpected(wit::string::from_view("two")); + } } diff --git a/tests/runtime/results/runner.cpp b/tests/runtime/results/runner.cpp index 3cda4d923..62376e875 100644 --- a/tests/runtime/results/runner.cpp +++ b/tests/runtime/results/runner.cpp @@ -6,67 +6,59 @@ template 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(::test::results::test::E2 a, ::test::results::test::E2 b) { + return a.line==b.line && a.column==b.column; } -static bool equal(std::optional const& a, std::optional const& b) { - if (a.has_value() != b.has_value()) return false; +template +static bool equal(std::expected const& a, std::expected const& b) { if (a.has_value()) { - return equal(a.value(), b.value()); + if (!b.has_value()) return false; + return equal(*a, *b); + } else { + if (b.has_value()) return false; + return equal(a.error(), b.error()); + } +} +template +static bool equal(std::expected const& a, std::expected const& b) { + if (a.has_value()) { + if (!b.has_value()) return false; + return equal(*a, *b); + } else { + if (b.has_value()) return false; + return equal(a.error().get_view(), b.error()); } - return true; } int main() { - using namespace ::test::results::to_test; - - assert(equal(string_error(0.0), std::unexpected("zero"))); - assert(equal(string_error(1.0), std::expected<>(1.0)); + using namespace ::test::results::test; - assert_eq!(enum_error(0.0), Err(E::A)); - assert_eq!(enum_error(1.0), Ok(1.0)); + assert(equal(StringError(0.0), std::expected(std::unexpected("zero")))); + assert(equal(StringError(1.0), std::expected(1.0))); - 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(equal(EnumError(0.0), std::expected(std::unexpected(E::kA)))); + assert(equal(EnumError(1.0), std::expected(1.0))); - 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(equal(RecordError(0.0), std::expected(std::unexpected(E2{420,0})))); + assert(equal(RecordError(1.0), std::expected(std::unexpected(E2{77,2})))); + assert(equal(RecordError(2.0), std::expected(2.0))); - assert_eq!(empty_error(0), Err(())); - assert_eq!(empty_error(1), Ok(42)); - assert_eq!(empty_error(2), Ok(2)); + // 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!(double_error(0), Ok(Ok(()))); - assert_eq!(double_error(1), Ok(Err("one".into()))); - assert_eq!(double_error(2), Err("two".into())); + // assert_eq!(empty_error(0), Err(())); + // assert_eq!(empty_error(1), Ok(42)); + // assert_eq!(empty_error(2), Ok(2)); - OptionNoneParam(std::optional()); - OptionSomeParam(std::optional("foo")); - assert(!OptionNoneResult()); - assert(equal(OptionSomeResult(), std::optional("foo"))); - assert(equal(OptionRoundtrip(std::optional("foo")), std::optional("foo"))); - assert(equal(DoubleOptionRoundtrip(std::optional>(std::optional(42))), std::optional>(std::optional(42)))); - assert(equal(DoubleOptionRoundtrip(std::optional>(std::optional())), std::optional>(std::optional()))); - assert(equal(DoubleOptionRoundtrip(std::optional>()), std::optional>())); + // assert_eq!(double_error(0), Ok(Ok(()))); + // assert_eq!(double_error(1), Ok(Err("one".into()))); + // assert_eq!(double_error(2), Err("two".into())); } From 502d35eab220df33ec110179dcfabb06f69e7d1c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 May 2025 15:13:31 +0200 Subject: [PATCH 602/672] results test fully operational --- tests/runtime/results/runner.cpp | 63 ++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/tests/runtime/results/runner.cpp b/tests/runtime/results/runner.cpp index 62376e875..5b52f080c 100644 --- a/tests/runtime/results/runner.cpp +++ b/tests/runtime/results/runner.cpp @@ -9,6 +9,17 @@ static bool equal(T const& a, T const& b) { static bool equal(::test::results::test::E2 a, ::test::results::test::E2 b) { return a.line==b.line && a.column==b.column; } +static bool equal(::test::results::test::E3 a, ::test::results::test::E3 b) { + if (a.variants.index()!=b.variants.index()) { return false; } + switch (a.variants.index()) { + case 0: return equal(std::get<::test::results::test::E3::E1>(a.variants).value, std::get<::test::results::test::E3::E1>(b.variants).value); + case 1: return equal(std::get<::test::results::test::E3::E2>(a.variants).value, std::get<::test::results::test::E3::E2>(b.variants).value); + } + return false; +} +static bool equal(wit::Void a, wit::Void b) { + return true; +} template static bool equal(std::expected const& a, std::expected const& b) { if (a.has_value()) { @@ -19,6 +30,25 @@ static bool equal(std::expected const& a, std::expected const& b) { return equal(a.error(), b.error()); } } +template +static bool equal(std::expected const& a, std::expected const& b) { + if (a.has_value()) { + if (!b.has_value()) return false; + return true; + } else { + if (b.has_value()) return false; + return equal(a.error(), b.error()); + } +} +static bool equal(std::expected const& a, std::expected const& b) { + if (a.has_value()) { + if (!b.has_value()) return false; + return true; + } else { + if (b.has_value()) return false; + return equal(a.error().get_view(), b.error()); + } +} template static bool equal(std::expected const& a, std::expected const& b) { if (a.has_value()) { @@ -29,6 +59,15 @@ static bool equal(std::expected const& a, std::expected, wit::string> const& a, std::expected, std::string_view> const& b) { + if (a.has_value()) { + if (!b.has_value()) return false; + return equal(*a, *b); + } else { + if (b.has_value()) return false; + return equal(a.error().get_view(), b.error()); + } +} int main() { @@ -44,21 +83,15 @@ int main() assert(equal(RecordError(1.0), std::expected(std::unexpected(E2{77,2})))); assert(equal(RecordError(2.0), std::expected(2.0))); - // 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(equal(VariantError(0.0), std::expected(std::unexpected(E3{E3::E2{E2{420,0}}})))); + assert(equal(VariantError(1.0), std::expected(std::unexpected(E3{E3::E1{E::kB}})))); + assert(equal(VariantError(2.0), std::expected(std::unexpected(E3{E3::E1{E::kC}})))); - // assert_eq!(empty_error(0), Err(())); - // assert_eq!(empty_error(1), Ok(42)); - // assert_eq!(empty_error(2), Ok(2)); + assert(equal(EmptyError(0), std::expected(std::unexpected(wit::Void{})))); + assert(equal(EmptyError(1), std::expected(42))); + assert(equal(EmptyError(2), std::expected(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())); + assert(equal(DoubleError(0), std::expected, std::string_view>(std::expected()))); + assert(equal(DoubleError(1), std::expected, std::string_view>(std::expected(std::unexpected("one"))))); + assert(equal(DoubleError(2), std::expected, std::string_view>(std::unexpected("two")))); } From 7c2e9f28b86bd3a3fd62fea9aeb636389a574bd5 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 May 2025 18:00:22 +0200 Subject: [PATCH 603/672] Opt out of language C++17 for cpp extension --- crates/test/src/lib.rs | 12 +++++++++++- tests/runtime/demo/runner.cpp | 2 ++ tests/runtime/demo/test.cpp | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 9edfb025d..d207b2721 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -410,7 +410,7 @@ impl Runner<'_> { .and_then(|s| s.to_str()) .context("non-utf-8 path extension")?; - let language = match extension { + let mut language = match extension { "rs" => Language::Rust, "c" => Language::C, "cpp" => Language::Cpp17, @@ -429,6 +429,16 @@ impl Runner<'_> { }; assert!(bindgen.args.is_empty()); bindgen.args = config.args.into(); + if language == Language::Cpp17 { + bindgen.args.retain(|elem| { + if elem == "--language=Cpp" { + language = Language::Cpp; + false + } else { + true + } + }); + } Ok(Component { name: path.file_stem().unwrap().to_str().unwrap().to_string(), diff --git a/tests/runtime/demo/runner.cpp b/tests/runtime/demo/runner.cpp index 5eb5722ad..14d5569b1 100644 --- a/tests/runtime/demo/runner.cpp +++ b/tests/runtime/demo/runner.cpp @@ -1,3 +1,5 @@ +//@ args = '--language=Cpp' + #include int main() { diff --git a/tests/runtime/demo/test.cpp b/tests/runtime/demo/test.cpp index 3791c619f..45db8d91e 100644 --- a/tests/runtime/demo/test.cpp +++ b/tests/runtime/demo/test.cpp @@ -1,3 +1,5 @@ +//@ args = '--language=Cpp' + #include void exports_a_b_the_test_x() { From dca3cd8374004d5d719bc6581198b667da53d152 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 May 2025 21:38:28 +0200 Subject: [PATCH 604/672] variants work --- crates/cpp/helper-types/wit-common.h | 6 ++++ crates/cpp/src/lib.rs | 17 +++++++---- tests/runtime/variants/test.cpp | 43 ++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 tests/runtime/variants/test.cpp diff --git a/crates/cpp/helper-types/wit-common.h b/crates/cpp/helper-types/wit-common.h index 68957569b..ba5f2b87e 100644 --- a/crates/cpp/helper-types/wit-common.h +++ b/crates/cpp/helper-types/wit-common.h @@ -66,4 +66,10 @@ template class ResourceTable { /// @brief Replaces void in the error position of a result struct Void {}; + +template +constexpr To bit_cast(const From& from) noexcept { + union Bits { From from; To to; }; + Bits b; b.from = from; return b.to; +} } // namespace wit diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 538e508e5..2549303d8 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -312,16 +312,20 @@ impl Cpp { 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) + self.dependencies.needs_wit = true; + format!("wit::bit_cast({})", op) } Bitcast::F32ToI32 | Bitcast::F32ToI64 => { - format!("((union {{ float a; int32_t b; }}){{ {} }}).b", op) + self.dependencies.needs_wit = true; + format!("wit::bit_cast({})", op) } Bitcast::I64ToF64 => { - format!("((union {{ int64_t a; double b; }}){{ {} }}).b", op) + self.dependencies.needs_wit = true; + format!("wit::bit_cast({})", op) } Bitcast::F64ToI64 => { - format!("((union {{ double a; int64_t b; }}){{ {} }}).b", op) + self.dependencies.needs_wit = true; + format!("wit::bit_cast({})", op) } Bitcast::I32ToI64 | Bitcast::LToI64 | Bitcast::PToP64 => { format!("(int64_t) {}", op) @@ -2204,7 +2208,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { fn move_if_necessary(arg: &str) -> String { // if it is a name of a variable move it - if arg.chars().all(char::is_alphanumeric) { + if !arg.is_empty() && arg.chars().all(char::is_alphanumeric) { format!("std::move({arg})") } else { arg.into() @@ -2744,7 +2748,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { uwriteln!( self.src, "{result}.variants = {ty}::{tp}{{{}}};", - move_if_necessary(&block_results[0]) + move_if_necessary(&block_results.get(0).cloned().unwrap_or_default()) ); uwriteln!(self.src, "break;"); } @@ -2905,6 +2909,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } if result.err.is_none() { err.clear(); + self.gen.gen.dependencies.needs_wit = true; err_result = String::from("wit::Void{}"); } else { err_result = move_if_necessary(&err_results[0]); diff --git a/tests/runtime/variants/test.cpp b/tests/runtime/variants/test.cpp new file mode 100644 index 000000000..c1eda6b9c --- /dev/null +++ b/tests/runtime/variants/test.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +std::optional exports::test::variants::to_test::RoundtripOption(std::optional a) { + if (a.has_value()) { + return std::optional(a); + } else { + return std::optional(); + } +} + +std::expected exports::test::variants::to_test::RoundtripResult(std::expected a) { + if (a.has_value()) { + return std::expected(double(a.value())); + } else { + return std::expected(std::unexpected(uint8_t(a.error()))); + } +} + +::test::variants::to_test::E1 exports::test::variants::to_test::RoundtripEnum(::test::variants::to_test::E1 a) { + return a; +} + +bool exports::test::variants::to_test::InvertBool(bool a) { + return !a; +} + +std::tuple<::test::variants::to_test::C1, ::test::variants::to_test::C2, ::test::variants::to_test::C3, ::test::variants::to_test::C4, ::test::variants::to_test::C5, ::test::variants::to_test::C6> exports::test::variants::to_test::VariantCasts(std::tuple<::test::variants::to_test::C1, ::test::variants::to_test::C2, ::test::variants::to_test::C3, ::test::variants::to_test::C4, ::test::variants::to_test::C5, ::test::variants::to_test::C6> a) { + return a; +} + +std::tuple<::test::variants::to_test::Z1, ::test::variants::to_test::Z2, ::test::variants::to_test::Z3, ::test::variants::to_test::Z4> exports::test::variants::to_test::VariantZeros(std::tuple<::test::variants::to_test::Z1, ::test::variants::to_test::Z2, ::test::variants::to_test::Z3, ::test::variants::to_test::Z4> a) { + return a; +} + +void exports::test::variants::to_test::VariantTypedefs(std::optional a, bool b, std::expected c) { + +} + +std::tuple, ::test::variants::to_test::MyErrno> exports::test::variants::to_test::VariantEnums(bool a, std::expected b, ::test::variants::to_test::MyErrno c) { + return std::tuple, ::test::variants::to_test::MyErrno>(a, b, c); +} From fcd9cea721bc9e598ab98048639c0992f5150005 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 May 2025 22:39:50 +0200 Subject: [PATCH 605/672] variants runner works --- crates/cpp/src/lib.rs | 4 +- tests/runtime/variants/runner.cpp | 182 ++++++++++++++++++++++++++++++ 2 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 tests/runtime/variants/runner.cpp diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 2549303d8..6bf42e726 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2744,13 +2744,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { variant.cases.iter().zip(blocks).enumerate() { let tp = case.name.clone().to_pascal_case(); - uwriteln!(self.src, "case {i}: {block}"); + uwriteln!(self.src, "case {i}: {{ {block}"); uwriteln!( self.src, "{result}.variants = {ty}::{tp}{{{}}};", move_if_necessary(&block_results.get(0).cloned().unwrap_or_default()) ); - uwriteln!(self.src, "break;"); + uwriteln!(self.src, "}} break;"); } uwriteln!(self.src, "}}"); // uwriteln!(self.src, "}}"); diff --git a/tests/runtime/variants/runner.cpp b/tests/runtime/variants/runner.cpp new file mode 100644 index 000000000..f3ef2a1a3 --- /dev/null +++ b/tests/runtime/variants/runner.cpp @@ -0,0 +1,182 @@ +#include +#include +#include + +template +static bool equal(T const& a, T const& b) { + return a==b; +} +static bool equal(wit::Void a, wit::Void b) { + return true; +} +static bool equal(::test::variants::to_test::C1 a, ::test::variants::to_test::C1 b) { + if (a.variants.index()!=b.variants.index()) { return false; } + switch (a.variants.index()) { + case 0: return equal(std::get<::test::variants::to_test::C1::A>(a.variants).value, std::get<::test::variants::to_test::C1::A>(b.variants).value); + case 1: return equal(std::get<::test::variants::to_test::C1::B>(a.variants).value, std::get<::test::variants::to_test::C1::B>(b.variants).value); + } + return false; +} +static bool equal(::test::variants::to_test::C2 a, ::test::variants::to_test::C2 b) { + if (a.variants.index()!=b.variants.index()) { return false; } + switch (a.variants.index()) { + case 0: return equal(std::get<::test::variants::to_test::C2::A>(a.variants).value, std::get<::test::variants::to_test::C2::A>(b.variants).value); + case 1: return equal(std::get<::test::variants::to_test::C2::B>(a.variants).value, std::get<::test::variants::to_test::C2::B>(b.variants).value); + } + return false; +} +static bool equal(::test::variants::to_test::C3 a, ::test::variants::to_test::C3 b) { + if (a.variants.index()!=b.variants.index()) { return false; } + switch (a.variants.index()) { + case 0: return equal(std::get<::test::variants::to_test::C3::A>(a.variants).value, std::get<::test::variants::to_test::C3::A>(b.variants).value); + case 1: return equal(std::get<::test::variants::to_test::C3::B>(a.variants).value, std::get<::test::variants::to_test::C3::B>(b.variants).value); + } + return false; +} +static bool equal(::test::variants::to_test::C4 a, ::test::variants::to_test::C4 b) { + if (a.variants.index()!=b.variants.index()) { return false; } + switch (a.variants.index()) { + case 0: return equal(std::get<::test::variants::to_test::C4::A>(a.variants).value, std::get<::test::variants::to_test::C4::A>(b.variants).value); + case 1: return equal(std::get<::test::variants::to_test::C4::B>(a.variants).value, std::get<::test::variants::to_test::C4::B>(b.variants).value); + } + return false; +} +static bool equal(::test::variants::to_test::C5 a, ::test::variants::to_test::C5 b) { + if (a.variants.index()!=b.variants.index()) { return false; } + switch (a.variants.index()) { + case 0: return equal(std::get<::test::variants::to_test::C5::A>(a.variants).value, std::get<::test::variants::to_test::C5::A>(b.variants).value); + case 1: return equal(std::get<::test::variants::to_test::C5::B>(a.variants).value, std::get<::test::variants::to_test::C5::B>(b.variants).value); + } + return false; +} +static bool equal(::test::variants::to_test::C6 a, ::test::variants::to_test::C6 b) { + if (a.variants.index()!=b.variants.index()) { return false; } + switch (a.variants.index()) { + case 0: return equal(std::get<::test::variants::to_test::C6::A>(a.variants).value, std::get<::test::variants::to_test::C6::A>(b.variants).value); + case 1: return equal(std::get<::test::variants::to_test::C6::B>(a.variants).value, std::get<::test::variants::to_test::C6::B>(b.variants).value); + } + return false; +} +static bool equal(::test::variants::to_test::Z1 a, ::test::variants::to_test::Z1 b) { + if (a.variants.index()!=b.variants.index()) { return false; } + switch (a.variants.index()) { + case 0: return equal(std::get<::test::variants::to_test::Z1::A>(a.variants).value, std::get<::test::variants::to_test::Z1::A>(b.variants).value); + case 1: return true; + } + return false; +} +static bool equal(::test::variants::to_test::Z2 a, ::test::variants::to_test::Z2 b) { + if (a.variants.index()!=b.variants.index()) { return false; } + switch (a.variants.index()) { + case 0: return equal(std::get<::test::variants::to_test::Z2::A>(a.variants).value, std::get<::test::variants::to_test::Z2::A>(b.variants).value); + case 1: return true; + } + return false; +} +static bool equal(::test::variants::to_test::Z3 a, ::test::variants::to_test::Z3 b) { + if (a.variants.index()!=b.variants.index()) { return false; } + switch (a.variants.index()) { + case 0: return equal(std::get<::test::variants::to_test::Z3::A>(a.variants).value, std::get<::test::variants::to_test::Z3::A>(b.variants).value); + case 1: return true; + } + return false; +} +static bool equal(::test::variants::to_test::Z4 a, ::test::variants::to_test::Z4 b) { + if (a.variants.index()!=b.variants.index()) { return false; } + switch (a.variants.index()) { + case 0: return equal(std::get<::test::variants::to_test::Z4::A>(a.variants).value, std::get<::test::variants::to_test::Z4::A>(b.variants).value); + case 1: return true; + } + return false; +} +template +static bool equal(std::expected const& a, std::expected const& b) { + if (a.has_value()) { + if (!b.has_value()) return false; + return equal(*a, *b); + } else { + if (b.has_value()) return false; + return equal(a.error(), b.error()); + } +} +template +static bool equal(std::expected const& a, std::expected const& b) { + if (a.has_value()) { + if (!b.has_value()) return false; + return true; + } else { + if (b.has_value()) return false; + return equal(a.error(), b.error()); + } +} +template +static bool equal(std::expected const& a, std::expected const& b) { + if (a.has_value()) { + if (!b.has_value()) return false; + return equal(*a, *b); + } else { + if (b.has_value()) return false; + return equal(a.error().get_view(), b.error()); + } +} +template +static bool equal(std::optional const& a, std::optional const& b) { + if (a.has_value() != b.has_value()) return false; + if (a.has_value()) { + return equal(a.value(), b.value()); + } + return true; +} +template +static bool equal(std::tuple a, std::tuple b) { + return equal(std::get<0>(a), std::get<0>(b)) && + equal(std::get<1>(a), std::get<1>(b)) && + equal(std::get<2>(a), std::get<2>(b)) && + equal(std::get<3>(a), std::get<3>(b)) && + equal(std::get<4>(a), std::get<4>(b)) && + equal(std::get<5>(a), std::get<5>(b)); +} +template +static bool equal(std::tuple a, std::tuple b) { + return equal(std::get<0>(a), std::get<0>(b)) && + equal(std::get<1>(a), std::get<1>(b)) && + equal(std::get<2>(a), std::get<2>(b)) && + equal(std::get<3>(a), std::get<3>(b)); +} +template +static bool equal(std::tuple a, std::tuple b) { + return equal(std::get<0>(a), std::get<0>(b)) && + equal(std::get<1>(a), std::get<1>(b)) && + equal(std::get<2>(a), std::get<2>(b)); +} + +int main() +{ + using namespace ::test::variants::to_test; + + assert(equal(RoundtripOption(1.0), std::optional(1))); + assert(equal(RoundtripOption(std::optional()), std::optional())); + assert(equal(RoundtripOption(2.0), std::optional(2))); + assert(equal(RoundtripResult(2), std::expected(2.0))); + assert(equal(RoundtripResult(4), std::expected(4.0))); + assert(equal(RoundtripResult(std::unexpected(5.3)), std::expected(std::unexpected(5)))); + + assert(equal(RoundtripEnum(E1::kA), E1::kA)); + assert(equal(RoundtripEnum(E1::kB), E1::kB)); + + assert(equal(InvertBool(true), false)); + assert(equal(InvertBool(false), true)); + + assert(equal(VariantCasts(std::tuple(C1{C1::A{1}}, C2{C2::A{2}}, C3{C3::A{3}}, C4{C4::A{4}}, C5{C5::A{5}}, C6{C6::A{6.0}})), + std::tuple(C1{C1::A{1}}, C2{C2::A{2}}, C3{C3::A{3}}, C4{C4::A{4}}, C5{C5::A{5}}, C6{C6::A{6.0}}) )); + assert(equal(VariantCasts(std::tuple(C1{C1::B{1}}, C2{C2::B{2.0}}, C3{C3::B{3.0}}, C4{C4::B{4.0}}, C5{C5::B{5.0}}, C6{C6::B{6.0}})), + std::tuple(C1{C1::B{1}}, C2{C2::B{2.0}}, C3{C3::B{3.0}}, C4{C4::B{4.0}}, C5{C5::B{5.0}}, C6{C6::B{6.0}}) )); + + assert(equal(VariantZeros(std::tuple(Z1{Z1::A{1}}, Z2{Z2::A{2}}, Z3{Z3::A{3.0}}, Z4{Z4::A{4.0}})), std::tuple(Z1{Z1::A{1}}, Z2{Z2::A{2}}, Z3{Z3::A{3.0}}, Z4{Z4::A{4.0}}))); + assert(equal(VariantZeros(std::tuple(Z1{Z1::B{}}, Z2{Z2::B{}}, Z3{Z3::B{}}, Z4{Z4::B{}})), std::tuple(Z1{Z1::B{}}, Z2{Z2::B{}}, Z3{Z3::B{}}, Z4{Z4::B{}}))); + + VariantTypedefs(std::optional(), false, std::unexpected(wit::Void{})); + + assert(equal(VariantEnums(true, std::expected(), MyErrno::kSuccess) + , std::tuple, MyErrno>(true, std::expected(), MyErrno::kSuccess))); +} From 04637a65ea44382027bed4d1178a0d1d7c1ca30a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 18 May 2025 23:57:58 +0200 Subject: [PATCH 606/672] clean up lists test --- tests/runtime/lists/runner.cpp | 10 ---------- tests/runtime/lists/test.cpp | 9 --------- 2 files changed, 19 deletions(-) diff --git a/tests/runtime/lists/runner.cpp b/tests/runtime/lists/runner.cpp index 49ce35c4c..e1778d47d 100644 --- a/tests/runtime/lists/runner.cpp +++ b/tests/runtime/lists/runner.cpp @@ -49,16 +49,6 @@ template 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 const& a, std::optional const& b) { -// if (a.has_value() != b.has_value()) return false; -// if (a.has_value()) { -// return equal(a.value(), b.value()); -// } -// return true; -// } int main() { diff --git a/tests/runtime/lists/test.cpp b/tests/runtime/lists/test.cpp index 830ad2cb6..b4c82a782 100644 --- a/tests/runtime/lists/test.cpp +++ b/tests/runtime/lists/test.cpp @@ -7,12 +7,6 @@ uint32_t exports::test::lists::to_test::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 static bool equal(T const&a, S const& b) { return a == b; @@ -41,9 +35,6 @@ template static bool equal(wit::vector const&a, std::vector const& b) { return equal(a.get_view(), wit::span(b)); } -// static bool equal(wit::vector const&a, std::vector const& b) { -// return equal(a.get_view(), wit::span(b)); -// } template static bool equal(std::tuple const&a, std::tuple const& b) { return equal(std::get<0>(a), std::get<0>(b)) && equal(std::get<1>(a), std::get<1>(b)); From fe8c0de4ed73b09580cda47ec3d7a787ba00d628 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 20 May 2025 00:35:18 +0200 Subject: [PATCH 607/672] I need a better strategy to copy the headers to the target --- crates/cpp/src/lib.rs | 43 +++++++++++++------ crates/test/src/cpp.rs | 7 +++ .../cpp17/exports-test-resources-KebabCase.h | 24 +++++++++++ .../cpp17/exports-test-resources-X.h | 28 ++++++++++++ .../cpp17/exports-test-resources-Z.h | 24 +++++++++++ tests/runtime/resources/resources.cpp | 14 ++++++ 6 files changed, 127 insertions(+), 13 deletions(-) create mode 100644 tests/runtime/resources/cpp17/exports-test-resources-KebabCase.h create mode 100644 tests/runtime/resources/cpp17/exports-test-resources-X.h create mode 100644 tests/runtime/resources/cpp17/exports-test-resources-Z.h create mode 100644 tests/runtime/resources/resources.cpp diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 6bf42e726..7b0a694e7 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1009,12 +1009,18 @@ impl CppInterfaceGenerator<'_> { && !(matches!(is_drop, SpecialMethod::ResourceDrop) && matches!(abi_variant, AbiVariant::GuestImport)) { - if let Some(ty) = &func.result { - res.result.push_str(&self.type_name( - ty, - outer_namespace, - Flavor::Result(abi_variant), - )); + if matches!(is_drop, SpecialMethod::Allocate) { + // func.name == "$alloc" + res.result.push_str("Owned"); + } else if let Some(ty) = &func.result { + res.result.push_str( + &(self.type_name(ty, outer_namespace, Flavor::Result(abi_variant)) + + if matches!(is_drop, SpecialMethod::ResourceRep) { + "*" + } else { + "" + }), + ); } else { res.result = "void".into(); } @@ -1031,16 +1037,27 @@ impl CppInterfaceGenerator<'_> { 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)) + if i == 0 + && name == "self" + && (matches!(&func.kind, FunctionKind::Method(_)) + || (matches!(&is_drop, SpecialMethod::ResourceDrop) + && matches!(abi_variant, AbiVariant::GuestImport))) { res.implicit_self = true; continue; } + let is_pointer = if i == 0 + && name == "self" + && matches!(&is_drop, SpecialMethod::Dtor | SpecialMethod::ResourceNew) + && matches!(abi_variant, AbiVariant::GuestExport) + { + "*" + } else { + "" + }; res.arguments.push(( name.to_snake_case(), - self.type_name(param, &res.namespace, Flavor::Argument(abi_variant)), + self.type_name(param, &res.namespace, Flavor::Argument(abi_variant)) + is_pointer, )); } // default to non-const when exporting a method @@ -1083,7 +1100,7 @@ impl CppInterfaceGenerator<'_> { } match (&is_special, false, &variant) { (SpecialMethod::Allocate, _, _) => { - uwrite!( + uwriteln!( self.gen.h_src.src, "{{\ return {OWNED_CLASS_NAME}(new {}({}));\ @@ -1099,9 +1116,9 @@ impl CppInterfaceGenerator<'_> { // body is inside the header return Vec::default(); } - (SpecialMethod::Dtor, _, AbiVariant::GuestImport) + (SpecialMethod::Dtor, _, _ /*AbiVariant::GuestImport*/) | (SpecialMethod::ResourceDrop, true, _) => { - uwrite!( + uwriteln!( self.gen.h_src.src, "{{\ delete {};\ diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index 4e81023d3..ac1a3dab8 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -78,6 +78,9 @@ impl LanguageMethods for Cpp17 { fn compile(&self, runner: &crate::Runner<'_>, compile: &crate::Compile) -> anyhow::Result<()> { let compiler = clangpp(runner); let config = compile.component.deserialize_lang_config::()?; + let mut export_header_dir = compile.component.path.clone(); + export_header_dir.pop(); + export_header_dir.push("cpp17"); let cwd = std::env::current_dir()?; let mut helper_dir = cwd.clone(); helper_dir.push("crates"); @@ -98,6 +101,8 @@ impl LanguageMethods for Cpp17 { .join(format!("{}.cpp", compile.component.bindgen.world)), ) .arg("-I") + .arg(export_header_dir.clone()) + .arg("-I") .arg(&compile.bindings_dir) .arg("-I") .arg(helper_dir.to_str().unwrap().to_string()) @@ -124,6 +129,8 @@ impl LanguageMethods for Cpp17 { compile.component.bindgen.world ))) .arg("-I") + .arg(export_header_dir) + .arg("-I") .arg(&compile.bindings_dir) .arg("-I") .arg(helper_dir.to_str().unwrap().to_string()) diff --git a/tests/runtime/resources/cpp17/exports-test-resources-KebabCase.h b/tests/runtime/resources/cpp17/exports-test-resources-KebabCase.h new file mode 100644 index 000000000..7e7ef3b28 --- /dev/null +++ b/tests/runtime/resources/cpp17/exports-test-resources-KebabCase.h @@ -0,0 +1,24 @@ +#pragma once +#include +#include +#include +#include +#include +/* User class definition file, autogenerated once, then user modified +* Updated versions of this file are generated into KebabCase.template. +*/ +namespace exports {namespace test {namespace resources {class KebabCase : public wit::ResourceExportBase{ + int32_t value; + public: + + static void Dtor(resources::KebabCase* self){delete self;} + KebabCase(uint32_t a) : value(a) {} + static Owned New(uint32_t a){return Owned(new KebabCase(a));} + uint32_t GetA() { return value; } + static uint32_t TakeOwned(resources::KebabCase::Owned k); + static int32_t ResourceNew(resources::KebabCase* self); + static KebabCase* ResourceRep(int32_t id); + static void ResourceDrop(int32_t id); + }; + + }}} diff --git a/tests/runtime/resources/cpp17/exports-test-resources-X.h b/tests/runtime/resources/cpp17/exports-test-resources-X.h new file mode 100644 index 000000000..9950a29b8 --- /dev/null +++ b/tests/runtime/resources/cpp17/exports-test-resources-X.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include +#include +#include +/* User class definition file, autogenerated once, then user modified +* Updated versions of this file are generated into X.template. +*/ +namespace exports {namespace test {namespace resources {class X : public wit::ResourceExportBase{ + int32_t value; + public: + + static void Dtor(resources::X* self){delete self;} + X(int32_t a) : value(a) {} + static Owned New(int32_t a){return Owned(new X(a));} + int32_t GetA() { return value; } + void SetA(int32_t a) { value = a; } + static X::Owned Add(resources::X::Owned x, int32_t a) { + x->value += a; + return x; + } + static int32_t ResourceNew(resources::X* self); + static X* ResourceRep(int32_t id); + static void ResourceDrop(int32_t id); + }; + + }}} diff --git a/tests/runtime/resources/cpp17/exports-test-resources-Z.h b/tests/runtime/resources/cpp17/exports-test-resources-Z.h new file mode 100644 index 000000000..c11695e97 --- /dev/null +++ b/tests/runtime/resources/cpp17/exports-test-resources-Z.h @@ -0,0 +1,24 @@ +#pragma once +#include +#include +#include +#include +#include +/* User class definition file, autogenerated once, then user modified +* Updated versions of this file are generated into Z.template. +*/ +namespace exports {namespace test {namespace resources {class Z : public wit::ResourceExportBase{ + int32_t value; + public: + + static void Dtor(resources::Z* self){delete self;} + Z(int32_t a) : value(a) {} + static Owned New(int32_t a){return Owned(new Z(a));} + int32_t GetA() const { return value; } + static uint32_t NumDropped(); + static int32_t ResourceNew(resources::Z* self); + static Z* ResourceRep(int32_t id); + static void ResourceDrop(int32_t id); + }; + + }}} diff --git a/tests/runtime/resources/resources.cpp b/tests/runtime/resources/resources.cpp new file mode 100644 index 000000000..54cbe9e3e --- /dev/null +++ b/tests/runtime/resources/resources.cpp @@ -0,0 +1,14 @@ +#include + +exports::test::resources::Z::Owned exports::test::resources::Add(std::reference_wrapper a, std::reference_wrapper b) { + return exports::test::resources::Z::New(a.get().GetA() + b.get().GetA()); +} + +void exports::test::resources::Consume(X::Owned x) { + +} + +std::expected exports::test::resources::TestImports() { + auto y = ::test::resources::Y(10); + return std::expected(); +} From 13c97af7c7fd0b10d807c193d7eaf57a889ad9d8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 20 May 2025 00:59:16 +0200 Subject: [PATCH 608/672] not elegant but working solution --- crates/test/src/cpp.rs | 14 ++++++++++++++ .../cpp17/exports-test-resources-KebabCase.h | 4 ++-- .../resources/cpp17/exports-test-resources-Z.h | 4 +++- tests/runtime/resources/resources.cpp | 2 ++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index ac1a3dab8..049f142e1 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -78,9 +78,23 @@ impl LanguageMethods for Cpp17 { fn compile(&self, runner: &crate::Runner<'_>, compile: &crate::Compile) -> anyhow::Result<()> { let compiler = clangpp(runner); let config = compile.component.deserialize_lang_config::()?; + let mut export_header_dir = compile.component.path.clone(); export_header_dir.pop(); export_header_dir.push("cpp17"); + + let mut source_files = export_header_dir.clone(); + source_files.push("*"); + let mut copycmd = Command::new("sh"); + copycmd.arg("-c"); + copycmd.arg( + String::from("cp ") + + source_files.to_str().unwrap() + + " " + + compile.bindings_dir.to_str().unwrap(), + ); + runner.run_command(&mut copycmd)?; + let cwd = std::env::current_dir()?; let mut helper_dir = cwd.clone(); helper_dir.push("crates"); diff --git a/tests/runtime/resources/cpp17/exports-test-resources-KebabCase.h b/tests/runtime/resources/cpp17/exports-test-resources-KebabCase.h index 7e7ef3b28..81c228c0c 100644 --- a/tests/runtime/resources/cpp17/exports-test-resources-KebabCase.h +++ b/tests/runtime/resources/cpp17/exports-test-resources-KebabCase.h @@ -8,14 +8,14 @@ * Updated versions of this file are generated into KebabCase.template. */ namespace exports {namespace test {namespace resources {class KebabCase : public wit::ResourceExportBase{ - int32_t value; + uint32_t value; public: static void Dtor(resources::KebabCase* self){delete self;} KebabCase(uint32_t a) : value(a) {} static Owned New(uint32_t a){return Owned(new KebabCase(a));} uint32_t GetA() { return value; } - static uint32_t TakeOwned(resources::KebabCase::Owned k); + static uint32_t TakeOwned(resources::KebabCase::Owned k) { return k->value; } static int32_t ResourceNew(resources::KebabCase* self); static KebabCase* ResourceRep(int32_t id); static void ResourceDrop(int32_t id); diff --git a/tests/runtime/resources/cpp17/exports-test-resources-Z.h b/tests/runtime/resources/cpp17/exports-test-resources-Z.h index c11695e97..e8224f220 100644 --- a/tests/runtime/resources/cpp17/exports-test-resources-Z.h +++ b/tests/runtime/resources/cpp17/exports-test-resources-Z.h @@ -10,12 +10,14 @@ namespace exports {namespace test {namespace resources {class Z : public wit::ResourceExportBase{ int32_t value; public: + static uint32_t num_dropped; static void Dtor(resources::Z* self){delete self;} Z(int32_t a) : value(a) {} static Owned New(int32_t a){return Owned(new Z(a));} int32_t GetA() const { return value; } - static uint32_t NumDropped(); + ~Z() { num_dropped+=1; } + static uint32_t NumDropped() { return num_dropped+1; } static int32_t ResourceNew(resources::Z* self); static Z* ResourceRep(int32_t id); static void ResourceDrop(int32_t id); diff --git a/tests/runtime/resources/resources.cpp b/tests/runtime/resources/resources.cpp index 54cbe9e3e..65d947484 100644 --- a/tests/runtime/resources/resources.cpp +++ b/tests/runtime/resources/resources.cpp @@ -12,3 +12,5 @@ std::expected exports::test::resources::TestImports() { auto y = ::test::resources::Y(10); return std::expected(); } + +uint32_t exports::test::resources::Z::num_dropped = 0; From dbdfb71049c08f31cdf3c297cf3cc2126e447b1c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 23 May 2025 23:47:31 +0200 Subject: [PATCH 609/672] working resources --- crates/cpp/src/lib.rs | 2 +- crates/test/src/cpp.rs | 27 ++++++++++++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7b0a694e7..668f235bf 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2587,7 +2587,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { "auto {var} = {tname}::Owned({tname}::ResourceRep({op}));" ); - uwriteln!(self.src, "{var}->into_handle();"); + // uwriteln!(self.src, "{var}->into_handle();"); results.push(format!("std::move({var})")) } diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index 049f142e1..2ac76106d 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -1,11 +1,8 @@ use crate::config::StringList; use crate::{Kind, LanguageMethods, Runner}; -// use anyhow::bail; -// use anyhow::Result; -// use clap::Parser; +use anyhow::Context; use heck::ToSnakeCase; use serde::Deserialize; -// use std::env; use std::path::PathBuf; use std::process::Command; @@ -83,17 +80,16 @@ impl LanguageMethods for Cpp17 { export_header_dir.pop(); export_header_dir.push("cpp17"); - let mut source_files = export_header_dir.clone(); - source_files.push("*"); - let mut copycmd = Command::new("sh"); - copycmd.arg("-c"); - copycmd.arg( - String::from("cp ") - + source_files.to_str().unwrap() - + " " - + compile.bindings_dir.to_str().unwrap(), - ); - runner.run_command(&mut copycmd)?; + // copy resource implementation in header files to target dir + if export_header_dir.is_dir() { + for entry in export_header_dir.read_dir().context("failed to read test header directory")? { + let entry = entry.context("failed to read test header directory entry")?; + let path = entry.path(); + let mut dest = PathBuf::from(compile.bindings_dir); + dest.push(path.file_name().unwrap()); + std::fs::copy(path, dest).context("failed to copy header file")?; + } + } let cwd = std::env::current_dir()?; let mut helper_dir = cwd.clone(); @@ -129,6 +125,7 @@ impl LanguageMethods for Cpp17 { .arg("-Wno-unused-parameter") .arg("-std=c++17") .arg("-c") + .arg("-g") .arg("-o") .arg(&bindings_object); runner.run_command(&mut cmd)?; From 3d0c1fe37d6513ab3b7b8727b13deed9f7b0c5b8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 23 May 2025 23:57:50 +0200 Subject: [PATCH 610/672] more complex resource test --- tests/runtime/resources/resources.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/runtime/resources/resources.cpp b/tests/runtime/resources/resources.cpp index 65d947484..158a11e08 100644 --- a/tests/runtime/resources/resources.cpp +++ b/tests/runtime/resources/resources.cpp @@ -1,5 +1,10 @@ #include +template +static bool equal(T const& a, T const& b) { + return a==b; +} + exports::test::resources::Z::Owned exports::test::resources::Add(std::reference_wrapper a, std::reference_wrapper b) { return exports::test::resources::Z::New(a.get().GetA() + b.get().GetA()); } @@ -10,6 +15,25 @@ void exports::test::resources::Consume(X::Owned x) { std::expected exports::test::resources::TestImports() { auto y = ::test::resources::Y(10); + assert(equal(y.GetA(), 10)); + y.SetA(20); + assert(equal(y.GetA(), 20)); + auto y2a = ::test::resources::Y::Add(std::move(y), 20); + assert(equal(y2a.GetA(), 40)); + + // test multiple instances + auto y1 = ::test::resources::Y(1); + auto y2 = ::test::resources::Y(2); + assert(equal(y1.GetA(), 1)); + assert(equal(y2.GetA(), 2)); + y1.SetA(10); + y2.SetA(20); + assert(equal(y1.GetA(), 10)); + assert(equal(y2.GetA(), 20)); + auto y3 = ::test::resources::Y::Add(std::move(y1), 20); + auto y4 = ::test::resources::Y::Add(std::move(y2), 30); + assert(equal(y3.GetA(), 30)); + assert(equal(y4.GetA(), 50)); return std::expected(); } From 1ca600d6ab2a22ca4aa2fea7ee325cc6fbeb1c6a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 24 May 2025 00:00:02 +0200 Subject: [PATCH 611/672] remove unused functions --- crates/cpp/src/lib.rs | 92 +++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 668f235bf..45d3332b7 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1395,49 +1395,49 @@ impl CppInterfaceGenerator<'_> { } } - 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 { - 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() - } + // 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 { + // 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( @@ -2121,9 +2121,9 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { self.src.push_str(s); } - fn typename_lift(&self, id: TypeId) -> String { - self.gen.type_path(id, true) - } + // fn typename_lift(&self, id: TypeId) -> String { + // self.gen.type_path(id, true) + // } fn let_results(&mut self, amt: usize, results: &mut Vec) { if amt > 0 { From f875e2b91dfec2f3c13cb4ebe8d90a4789eceeb8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 24 May 2025 01:23:06 +0200 Subject: [PATCH 612/672] generate template files in the output directory --- crates/cpp/src/lib.rs | 14 ++++++++++++-- crates/test/src/cpp.rs | 36 +++++++++++++++++++++--------------- crates/test/src/lib.rs | 11 +++++++++++ src/bin/wit-bindgen.rs | 2 +- 4 files changed, 45 insertions(+), 18 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 45d3332b7..be3881777 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -3,6 +3,7 @@ use std::{ collections::{HashMap, HashSet}, fmt::Write as FmtWrite, io::{Read, Write}, + path::PathBuf, process::{Command, Stdio}, str::FromStr, }; @@ -225,11 +226,16 @@ pub struct Opts { /// Reduces the allocation overhead for canonical ABI. #[cfg_attr(feature = "clap", arg(long))] pub _old_api: bool, + + /// Where to place output files + #[cfg_attr(feature = "clap", arg(skip))] + out_dir: Option, } impl Opts { - pub fn build(mut self) -> Box { + pub fn build(mut self, out_dir: Option<&PathBuf>) -> Box { let mut r = Cpp::new(); + self.out_dir = out_dir.cloned(); if self._old_api { self.new_api = false; } @@ -667,7 +673,11 @@ impl WorldGenerator for Cpp { files.push(&format!("{snake}_cpp.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)) { + let dst = match &self.opts.out_dir { + Some(path) => path.join(name), + None => name.into(), + }; + if std::path::Path::exists(&std::path::PathBuf::from(dst)) { files.push(&(String::from(name) + ".template"), content.as_bytes()); } else { files.push(name, content.as_bytes()); diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index 2ac76106d..917d4da41 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -48,10 +48,6 @@ impl LanguageMethods for Cpp17 { false } - // fn default_bindgen_args(&self) -> &[&str] { - // &[] - // } - fn prepare(&self, runner: &mut crate::Runner<'_>) -> anyhow::Result<()> { let compiler = clangpp(runner); let cwd = std::env::current_dir()?; @@ -72,24 +68,38 @@ impl LanguageMethods for Cpp17 { Ok(()) } - fn compile(&self, runner: &crate::Runner<'_>, compile: &crate::Compile) -> anyhow::Result<()> { - let compiler = clangpp(runner); - let config = compile.component.deserialize_lang_config::()?; - - let mut export_header_dir = compile.component.path.clone(); + fn generate_bindings_prepare( + &self, + _runner: &Runner<'_>, + bindgen: &crate::Bindgen, + dir: &std::path::Path, + ) -> anyhow::Result<()> { + let mut export_header_dir = bindgen.wit_path.clone(); export_header_dir.pop(); export_header_dir.push("cpp17"); // copy resource implementation in header files to target dir if export_header_dir.is_dir() { - for entry in export_header_dir.read_dir().context("failed to read test header directory")? { + if !dir.exists() { + std::fs::create_dir_all(dir).context("failed to create bindings dir")?; + } + for entry in export_header_dir + .read_dir() + .context("failed to read test header directory")? + { let entry = entry.context("failed to read test header directory entry")?; let path = entry.path(); - let mut dest = PathBuf::from(compile.bindings_dir); + let mut dest = PathBuf::from(dir); dest.push(path.file_name().unwrap()); std::fs::copy(path, dest).context("failed to copy header file")?; } } + Ok(()) + } + + fn compile(&self, runner: &crate::Runner<'_>, compile: &crate::Compile) -> anyhow::Result<()> { + let compiler = clangpp(runner); + let config = compile.component.deserialize_lang_config::()?; let cwd = std::env::current_dir()?; let mut helper_dir = cwd.clone(); @@ -111,8 +121,6 @@ impl LanguageMethods for Cpp17 { .join(format!("{}.cpp", compile.component.bindgen.world)), ) .arg("-I") - .arg(export_header_dir.clone()) - .arg("-I") .arg(&compile.bindings_dir) .arg("-I") .arg(helper_dir.to_str().unwrap().to_string()) @@ -140,8 +148,6 @@ impl LanguageMethods for Cpp17 { compile.component.bindgen.world ))) .arg("-I") - .arg(export_header_dir) - .arg("-I") .arg(&compile.bindings_dir) .arg("-I") .arg(helper_dir.to_str().unwrap().to_string()) diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index d207b2721..c41a35004 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -1140,6 +1140,16 @@ trait LanguageMethods { /// downloading or caching dependencies. fn prepare(&self, runner: &mut Runner<'_>) -> Result<()>; + /// Add some files to the generated directory _before_ calling bindgen + fn generate_bindings_prepare( + &self, + _runner: &Runner<'_>, + _bindgen: &Bindgen, + _dir: &Path, + ) -> Result<()> { + Ok(()) + } + /// Generates bindings for `component` into `dir`. /// /// Runs `wit-bindgen` in aa subprocess to catch failures such as panics. @@ -1148,6 +1158,7 @@ trait LanguageMethods { Some(name) => name, None => return Ok(()), }; + self.generate_bindings_prepare(runner, bindgen, dir)?; let mut cmd = Command::new(runner.wit_bindgen); cmd.arg(name) .arg(&bindgen.wit_path) diff --git a/src/bin/wit-bindgen.rs b/src/bin/wit-bindgen.rs index e232c15f6..0c909439e 100644 --- a/src/bin/wit-bindgen.rs +++ b/src/bin/wit-bindgen.rs @@ -137,7 +137,7 @@ fn main() -> Result<()> { #[cfg(feature = "c")] Opt::C { opts, args } => (opts.build(), args), #[cfg(feature = "cpp")] - Opt::Cpp { opts, args } => (opts.build(), args), + Opt::Cpp { opts, args } => (opts.build(args.out_dir.as_ref()), args), #[cfg(feature = "rust")] Opt::Rust { opts, args } => (opts.build(), args), #[cfg(feature = "go")] From cd6c8f984d1b2019edf4fd986355430a5b5d5ab4 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 3 Jun 2025 01:02:56 +0200 Subject: [PATCH 613/672] simplifications and clippy fixes --- Cargo.lock | 4 +- crates/cpp/src/lib.rs | 275 ++++++++---------------------------------- 2 files changed, 55 insertions(+), 224 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 811a45f7d..bf6e603a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1256,8 +1256,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.230.0", - "wasm-metadata 0.230.0", + "wasm-encoder 0.232.0", + "wasm-metadata 0.232.0", "wit-bindgen-c", "wit-bindgen-core", "wit-component", diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index be3881777..adc88638c 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -74,7 +74,6 @@ struct Includes { 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, @@ -103,12 +102,9 @@ struct Cpp { c_src: SourceWithState, h_src: SourceWithState, c_src_head: Source, - // interface_includes: Vec, - // interface_header: SourceWithState, extern_c_decls: Source, dependencies: Includes, includes: Vec, - // host_functions: HashMap>, world: String, world_id: Option, imported_interfaces: HashSet, @@ -202,14 +198,14 @@ pub struct Opts { /// 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. + /// 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. + /// "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. + /// types for borrowing and owning, if necessary. #[cfg_attr(feature = "clap", arg(long, default_value_t = Ownership::Owning))] pub ownership: Ownership, @@ -244,7 +240,7 @@ impl Opts { } fn is_only_handle(&self, variant: AbiVariant) -> bool { - false == matches!(variant, AbiVariant::GuestExport) + !matches!(variant, AbiVariant::GuestExport) } fn ptr_type(&self) -> &'static str { @@ -288,11 +284,7 @@ impl Cpp { 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, } } @@ -398,9 +390,9 @@ impl Cpp { fn start_new_file(&mut self, condition: Option) -> 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()), + includes: std::mem::take(&mut self.includes), + src: std::mem::take(&mut self.h_src), + dependencies: std::mem::take(&mut self.dependencies), } } else { Default::default() @@ -409,10 +401,9 @@ impl Cpp { 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()); + self.h_src.change_namespace(&[]); uwriteln!(header, "#pragma once"); for include in self.includes.iter() { uwriteln!(header, "#include {include}"); @@ -445,7 +436,6 @@ impl WorldGenerator for Cpp { let name = &resolve.worlds[world].name; self.world = name.to_string(); self.world_id = Some(world); - // self.sizes.fill(resolve); uwriteln!( self.c_src_head, r#"#include "{}_cpp.h" @@ -568,7 +558,6 @@ impl WorldGenerator for Cpp { _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); @@ -665,8 +654,8 @@ impl WorldGenerator for Cpp { ); if self.opts.format { - Self::clang_format(&mut c_str.src.as_mut_string()); - Self::clang_format(&mut h_str.src.as_mut_string()); + Self::clang_format(c_str.src.as_mut_string()); + Self::clang_format(h_str.src.as_mut_string()); } files.push(&format!("{snake}.cpp"), c_str.src.as_bytes()); @@ -677,7 +666,7 @@ impl WorldGenerator for Cpp { Some(path) => path.join(name), None => name.into(), }; - if std::path::Path::exists(&std::path::PathBuf::from(dst)) { + if std::path::Path::exists(&dst) { files.push(&(String::from(name) + ".template"), content.as_bytes()); } else { files.push(name, content.as_bytes()); @@ -725,7 +714,7 @@ fn namespace(resolve: &Resolve, owner: &TypeOwner, guest_export: bool, opts: &Op } impl SourceWithState { - fn change_namespace(&mut self, target: &Vec) { + fn change_namespace(&mut self, target: &[String]) { let mut same = 0; // itertools::fold_while? for (a, b) in self.namespace.iter().zip(target.iter()) { @@ -749,9 +738,8 @@ impl SourceWithState { } } - fn qualify(&mut self, target: &Vec) { + fn qualify(&mut self, target: &[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 { @@ -762,7 +750,7 @@ impl SourceWithState { } 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()) { + if self.namespace.contains(target.first().unwrap()) { self.src.push_str("::"); } } @@ -785,16 +773,9 @@ struct CppInterfaceGenerator<'a> { _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, } -// I wish this was possible -// impl Equivalent<(Vec, String)> for (&Vec, &str) { - -// } - impl CppInterfaceGenerator<'_> { fn types(&mut self, iface: InterfaceId) { let iface = &self.resolve().interfaces[iface]; @@ -847,7 +828,7 @@ impl CppInterfaceGenerator<'_> { .unwrap_or(( Default::default(), self.interface - .map(|id| TypeOwner::Interface(id)) + .map(TypeOwner::Interface) .unwrap_or(TypeOwner::World(self.gen.world_id.unwrap())), )); let mut namespace = namespace(self.resolve, &owner, guest_export, &self.gen.opts); @@ -873,7 +854,6 @@ impl CppInterfaceGenerator<'_> { 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(), } } @@ -923,7 +903,7 @@ impl CppInterfaceGenerator<'_> { retptr: false, }, }; - let mut module_name = self.wasm_import_module.as_ref().map(|e| e.clone()); + let mut module_name = self.wasm_import_module.clone(); let symbol_variant = variant; if matches!(variant, AbiVariant::GuestExport) && matches!( @@ -956,7 +936,7 @@ impl CppInterfaceGenerator<'_> { }); 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), + 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() { @@ -982,9 +962,6 @@ impl CppInterfaceGenerator<'_> { 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()); @@ -997,15 +974,9 @@ impl CppInterfaceGenerator<'_> { &mut self, func: &Function, abi_variant: AbiVariant, - // import: bool, - outer_namespace: &Vec, + outer_namespace: &[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); @@ -1020,7 +991,6 @@ impl CppInterfaceGenerator<'_> { && matches!(abi_variant, AbiVariant::GuestImport)) { if matches!(is_drop, SpecialMethod::Allocate) { - // func.name == "$alloc" res.result.push_str("Owned"); } else if let Some(ty) = &func.result { res.result.push_str( @@ -1133,12 +1103,9 @@ impl CppInterfaceGenerator<'_> { "{{\ delete {};\ }}", - cpp_sig.arguments.get(0).unwrap().0 + cpp_sig.arguments.first().unwrap().0 ); } - // SpecialMethod::None => todo!(), - // SpecialMethod::ResourceDrop => todo!(), - // SpecialMethod::ResourceNew => todo!(), _ => self.gen.h_src.src.push_str(";\n"), } @@ -1189,7 +1156,6 @@ impl CppInterfaceGenerator<'_> { &mut self, func: &Function, owner: &TypeOwner, - //interface: InterfaceId, variant: AbiVariant, ) { fn class_namespace( @@ -1242,18 +1208,18 @@ impl CppInterfaceGenerator<'_> { SpecialMethod::ResourceDrop => match lift_lower { LiftLower::LiftArgsLowerResults => { let module_name = String::from("[export]") - + &self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + + &self.wasm_import_module.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 + func.params.first().unwrap().0 ); } LiftLower::LowerArgsLiftResults => { let module_name = - self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + self.wasm_import_module.clone().unwrap(); let name = self.declare_import(&module_name, &func.name, &[WasmType::I32], &[]); uwriteln!( @@ -1271,7 +1237,7 @@ impl CppInterfaceGenerator<'_> { } SpecialMethod::ResourceNew => { let module_name = String::from("[export]") - + &self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + + &self.wasm_import_module.clone().unwrap(); let wasm_sig = self.declare_import( &module_name, &func.name, @@ -1282,12 +1248,12 @@ impl CppInterfaceGenerator<'_> { self.gen.c_src.src, "return {wasm_sig}(({}){});", self.gen.opts.ptr_type(), - func.params.get(0).unwrap().0 + func.params.first().unwrap().0 ); } SpecialMethod::ResourceRep => { let module_name = String::from("[export]") - + &self.wasm_import_module.as_ref().map(|e| e.clone()).unwrap(); + + &self.wasm_import_module.clone().unwrap(); let wasm_sig = self.declare_import( &module_name, &func.name, @@ -1299,7 +1265,7 @@ impl CppInterfaceGenerator<'_> { self.gen.c_src.src, "return ({}*){wasm_sig}({});", classname, - func.params.get(0).unwrap().0 + func.params.first().unwrap().0 ); } SpecialMethod::Allocate => unreachable!(), @@ -1335,7 +1301,6 @@ impl CppInterfaceGenerator<'_> { 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; @@ -1351,32 +1316,19 @@ impl CppInterfaceGenerator<'_> { && abi::guest_export_needs_post_return(self.resolve, func) { let sig = self.resolve.wasm_signature(variant, func); - let module_name = self.wasm_import_module.as_ref().map(|e| e.clone()); + let module_name = self.wasm_import_module.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, 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}\")))" @@ -1403,57 +1355,14 @@ impl CppInterfaceGenerator<'_> { 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 { - // 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, + from_namespace: &[String], flavor: Flavor, ) -> String { match ty { @@ -1465,13 +1374,13 @@ impl CppInterfaceGenerator<'_> { fn scoped_type_name( &self, id: TypeId, - from_namespace: &Vec, + from_namespace: &[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.namespace = Vec::from(from_namespace); relative.qualify(&namespc); format!( "{}{}", @@ -1480,7 +1389,7 @@ impl CppInterfaceGenerator<'_> { ) } - fn type_name(&mut self, ty: &Type, from_namespace: &Vec, flavor: Flavor) -> String { + fn type_name(&mut self, ty: &Type, from_namespace: &[String], flavor: Flavor) -> String { match ty { Type::Bool => "bool".into(), Type::Char => "uint32_t".into(), @@ -1588,7 +1497,6 @@ impl CppInterfaceGenerator<'_> { 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 => { @@ -1712,17 +1620,11 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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 @@ -1738,8 +1640,6 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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, false) { (true, false) => format!("wit::{RESOURCE_EXPORT_BASE_CLASS_NAME}<{pascal}>"), @@ -1768,16 +1668,10 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> 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; + + name; let func = Function { - name: name, + name, kind: FunctionKind::Static(id), params: vec![("self".into(), Type::Id(id))], result: None, @@ -1786,10 +1680,6 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> }; 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 { @@ -1803,7 +1693,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } { self.generate_function(func, &TypeOwner::Interface(intf), variant); if matches!(func.kind, FunctionKind::Constructor(_)) - && matches!(variant, AbiVariant::GuestExport) != false + && matches!(variant, AbiVariant::GuestExport) { // functional safety requires the option to use a different allocator, so move new into the implementation let func2 = Function { @@ -1837,7 +1727,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> if matches!(variant, AbiVariant::GuestExport) { let id_type = Type::S32; let func = Function { - name: "[resource-new]".to_string() + &name, + name: "[resource-new]".to_string() + name, kind: FunctionKind::Static(id), params: vec![("self".into(), Type::Id(id))], result: Some(id_type), @@ -1847,7 +1737,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> self.generate_function(&func, &TypeOwner::Interface(intf), variant); let func1 = Function { - name: "[resource-rep]".to_string() + &name, + name: "[resource-rep]".to_string() + name, kind: FunctionKind::Static(id), params: vec![("id".into(), id_type)], result: Some(Type::Id(id)), @@ -1857,7 +1747,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> self.generate_function(&func1, &TypeOwner::Interface(intf), variant); let func2 = Function { - name: "[resource-drop]".to_string() + &name, + name: "[resource-drop]".to_string() + name, kind: FunctionKind::Static(id), params: vec![("id".into(), id_type)], result: None, @@ -1868,18 +1758,6 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> } 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()); - // } } } @@ -1977,7 +1855,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> _payload: &wit_bindgen_core::wit_parser::Type, _docs: &wit_bindgen_core::wit_parser::Docs, ) { - // I assume I don't need to do anything ... + // nothing to do here } fn type_result( @@ -1987,7 +1865,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> _result: &wit_bindgen_core::wit_parser::Result_, _docs: &wit_bindgen_core::wit_parser::Docs, ) { - // I assume I don't need to do anything ... + // nothing to do here } fn type_enum( @@ -2050,7 +1928,7 @@ impl<'a> wit_bindgen_core::InterfaceGenerator<'a> for CppInterfaceGenerator<'a> _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 + // nothing to do here } fn type_builtin( @@ -2082,8 +1960,6 @@ struct FunctionBindgen<'a, 'b> { gen: &'b mut CppInterfaceGenerator<'a>, params: Vec, tmp: usize, - // import_return_pointer_area_size: usize, - // import_return_pointer_area_align: usize, namespace: Vec, src: Source, block_storage: Vec, @@ -2103,8 +1979,6 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { 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(), @@ -2131,10 +2005,6 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { 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) { if amt > 0 { let tmp = self.tmp(); @@ -2349,7 +2219,6 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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])); self.push_str(&format!( "auto {} = ({})({}.data());\n", @@ -2371,7 +2240,6 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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])); self.push_str(&format!( "auto {} = ({})({}.data());\n", @@ -2519,16 +2387,14 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } 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("{"); + result.push('{'); for (_field, val) in record.fields.iter().zip(operands) { - result.push_str(&(move_if_necessary(&val) + ", ")); + result.push_str(&(move_if_necessary(val) + ", ")); } - result.push_str("}"); + result.push('}'); results.push(result); } abi::Instruction::HandleLower { @@ -2597,8 +2463,6 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { "auto {var} = {tname}::Owned({tname}::ResourceRep({op}));" ); - // uwriteln!(self.src, "{var}->into_handle();"); - results.push(format!("std::move({var})")) } AbiVariant::GuestImportAsync => todo!(), @@ -2775,12 +2639,11 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { uwriteln!( self.src, "{result}.variants = {ty}::{tp}{{{}}};", - move_if_necessary(&block_results.get(0).cloned().unwrap_or_default()) + move_if_necessary(&block_results.first().cloned().unwrap_or_default()) ); uwriteln!(self.src, "}} break;"); } uwriteln!(self.src, "}}"); - // uwriteln!(self.src, "}}"); results.push(result); } @@ -2839,9 +2702,8 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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!(none_results.is_empty()); 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) { @@ -2849,7 +2711,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } else { Flavor::InStruct }; - let type_name = self.gen.type_name(*payload, &self.namespace, flavor); + let type_name = self.gen.type_name(payload, &self.namespace, flavor); let full_type = format!("std::optional<{type_name}>"); let op0 = &operands[0]; @@ -2932,7 +2794,6 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { ok.clear(); } else { ok_result = move_if_necessary(&ok_results[0]); - // format!("std::move({})", ok_results[0]); } if result.err.is_none() { err.clear(); @@ -2940,7 +2801,6 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { err_result = String::from("wit::Void{}"); } else { err_result = move_if_necessary(&err_results[0]); - // format!("std::move({})", err_results[0]); } let ok_type = self.gen.optional_type_name( result.ok.as_ref(), @@ -2995,7 +2855,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { .declare_import(&module_name, name, &sig.params, &sig.results); // ... then call the function with all our operands - if sig.results.len() > 0 { + if !sig.results.is_empty() { self.src.push_str("auto ret = "); results.push("ret".to_string()); } @@ -3010,29 +2870,16 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { let (namespace, func_name_h) = self.gen.func_namespace_name(func, true, true); if matches!(func.kind, FunctionKind::Method(_)) { let this = operands.remove(0); - //let objtype = namespace.join("::"); uwrite!(self.src, "({this}).get()."); - // uwrite!(self.src, "(({objtype}*){this})->",); } else { 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); 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"); - } + self.push_str(");\n"); if self.needs_dealloc { uwriteln!( self.src, @@ -3042,7 +2889,6 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } } abi::Instruction::Return { amt, func } => { - // let guest_import = matches!(self.variant, AbiVariant::GuestImport); match amt { 0 => {} _ => { @@ -3067,7 +2913,7 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { }) = self.cabi_post.as_ref() { self.src.push_str("wit::guest_owned<"); - self.src.push_str(&cabi_post_type); + self.src.push_str(cabi_post_type); self.src.push_str(">("); } if *amt == 1 { @@ -3175,10 +3021,10 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { abi::Instruction::ErrorContextLower { .. } => todo!(), abi::Instruction::ErrorContextLift { .. } => todo!(), abi::Instruction::Flush { amt } => { - for i in 0..*amt { + for i in operands.iter().take(*amt) { let tmp = self.tmp(); let result = format!("result{}", tmp); - uwriteln!(self.src, "auto {result} = {};", operands[i]); + uwriteln!(self.src, "auto {result} = {};", i); results.push(result); } } @@ -3190,7 +3036,6 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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", @@ -3222,14 +3067,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { 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) { 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 { @@ -3280,15 +3123,3 @@ fn is_special_method(func: &Function) -> SpecialMethod { 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), -// } -// } From bb600b8afe0827a4747e06e8f488707dc2c820a3 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 3 Jun 2025 01:07:09 +0200 Subject: [PATCH 614/672] this was never meant to be checked in --- intermediate.cpp | 371 ----------------------------------------------- 1 file changed, 371 deletions(-) delete mode 100644 intermediate.cpp diff --git a/intermediate.cpp b/intermediate.cpp deleted file mode 100644 index ca960a114..000000000 --- a/intermediate.cpp +++ /dev/null @@ -1,371 +0,0 @@ -// Generated by `wit-bindgen` 0.42.1. DO NOT EDIT! - -// Ensure that the *_component_type.o object is linked in -#ifdef __wasm32__ -extern void __component_type_object_force_link_intermediate(void); -void __component_type_object_force_link_intermediate_public_use_in_this_compilation_unit(void) { - __component_type_object_force_link_intermediate(); -} -#endif -#include "intermediate_cpp.h" -#include // 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("test:results/test"))) -__attribute__((import_name("string-error"))) -void testX3AresultsX2FtestX00string_error(float, uint8_t *); -extern "C" __attribute__((import_module("test:results/test"))) -__attribute__((import_name("enum-error"))) -void testX3AresultsX2FtestX00enum_error(float, uint8_t *); -extern "C" __attribute__((import_module("test:results/test"))) -__attribute__((import_name("record-error"))) -void testX3AresultsX2FtestX00record_error(float, uint8_t *); -extern "C" __attribute__((import_module("test:results/test"))) -__attribute__((import_name("variant-error"))) -void testX3AresultsX2FtestX00variant_error(float, uint8_t *); -extern "C" __attribute__((import_module("test:results/test"))) -__attribute__((import_name("empty-error"))) -void testX3AresultsX2FtestX00empty_error(int32_t, uint8_t *); -extern "C" __attribute__((import_module("test:results/test"))) -__attribute__((import_name("double-error"))) -void testX3AresultsX2FtestX00double_error(int32_t, uint8_t *); -std::expected test::results::test::StringError(float a) -{ - uintptr_t ret_area[((3*sizeof(void*))+sizeof(uintptr_t)-1)/sizeof(uintptr_t)]; - uint8_t* ptr0 = (uint8_t*)(&ret_area); - testX3AresultsX2FtestX00string_error((float(a)), ptr0); - std::expected result2; - if ((int32_t) (*((uint8_t*) (ptr0 + 0)))==0) { - - result2.emplace((float(*((float*) (ptr0 + sizeof(void*)))))); - } else { - auto len1 = *((size_t*) (ptr0 + (2*sizeof(void*)))); - - - result2=std::unexpected{wit::string((char const*)(*((uint8_t**) (ptr0 + sizeof(void*)))), len1)}; - } - auto result3 = result2; - return result3; -} -std::expected test::results::test::EnumError(float a) -{ - uint32_t ret_area[(8+sizeof(uint32_t)-1)/sizeof(uint32_t)]; - uint8_t* ptr0 = (uint8_t*)(&ret_area); - testX3AresultsX2FtestX00enum_error((float(a)), ptr0); - std::expected result1; - if ((int32_t) (*((uint8_t*) (ptr0 + 0)))==0) { - - result1.emplace((float(*((float*) (ptr0 + 4))))); - } else { - - result1=std::unexpected{(E)(int32_t) (*((uint8_t*) (ptr0 + 4)))}; - } - auto result2 = result1; - return result2; -} -std::expected test::results::test::RecordError(float a) -{ - uint32_t ret_area[(12+sizeof(uint32_t)-1)/sizeof(uint32_t)]; - uint8_t* ptr0 = (uint8_t*)(&ret_area); - testX3AresultsX2FtestX00record_error((float(a)), ptr0); - std::expected result3; - if ((int32_t) (*((uint8_t*) (ptr0 + 0)))==0) { - - result3.emplace((float(*((float*) (ptr0 + 4))))); - } else { - int32_t l1 = *((int32_t const*)(ptr0 + 4)); - int32_t l2 = *((int32_t const*)(ptr0 + 8)); - - result3=std::unexpected{E2{(uint32_t(l1)), (uint32_t(l2)), }}; - } - auto result4 = result3; - return result4; -} -std::expected test::results::test::VariantError(float a) -{ - uint32_t ret_area[(16+sizeof(uint32_t)-1)/sizeof(uint32_t)]; - uint8_t* ptr0 = (uint8_t*)(&ret_area); - testX3AresultsX2FtestX00variant_error((float(a)), ptr0); - std::expected result3; - if ((int32_t) (*((uint8_t*) (ptr0 + 0)))==0) { - float l1 = *((float const*)(ptr0 + 4)); - result3.emplace(l1); - } else { - switch (*((uint8_t const*)(ptr0 + 4))) { - case 0: { - result3 = std::unexpected{E3{std::variant(E3::E1{E(*(const uint8_t*)(ptr0 + 8))})}}; - break; - } - case 1: { - uint32_t l1 = *((uint32_t const*)(ptr0 + 8)); - uint32_t l2 = *((uint32_t const*)(ptr0 + 12)); - result3 = std::unexpected{E3{std::variant(E3::E2{test::E2{l1,l2}})}}; - break; - } - } - } - auto result4 = std::move(result3); - return result4; -} -std::expected test::results::test::EmptyError(uint32_t a) -{ - uint32_t ret_area[(8+sizeof(uint32_t)-1)/sizeof(uint32_t)]; - uint8_t* ptr0 = (uint8_t*)(&ret_area); - testX3AresultsX2FtestX00empty_error((int32_t(a)), ptr0); - std::expected result2; - if ((int32_t) (*((uint8_t*) (ptr0 + 0)))==0) { - int32_t l1 = *((int32_t const*)(ptr0 + 4)); - - result2.emplace((uint32_t(l1))); - } else { - - result2=std::unexpected{wit::Void{}}; - } - auto result3 = result2; - return result3; -} -std::expected, wit::string> test::results::test::DoubleError(uint32_t a) -{ - uintptr_t ret_area[((4*sizeof(void*))+sizeof(uintptr_t)-1)/sizeof(uintptr_t)]; - uint8_t* ptr0 = (uint8_t*)(&ret_area); - testX3AresultsX2FtestX00double_error((int32_t(a)), ptr0); - std::expected, wit::string> result4; - if ((int32_t) (*((uint8_t*) (ptr0 + 0)))==0) { - std::expected result2; - if ((int32_t) (*((uint8_t*) (ptr0 + sizeof(void*))))==0) { - - - } else { - auto len1 = *((size_t*) (ptr0 + (3*sizeof(void*)))); - - - result2=std::unexpected{wit::string((char const*)(*((uint8_t**) (ptr0 + (2*sizeof(void*))))), len1)}; - } - - result4.emplace(std::move(result2)); - } else { - auto len3 = *((size_t*) (ptr0 + (2*sizeof(void*)))); - - - result4=std::unexpected{wit::string((char const*)(*((uint8_t**) (ptr0 + sizeof(void*)))), len3)}; - } - auto result5 = result4; - return result5; -} -extern "C" __attribute__((__export_name__("test:results/test#string-error"))) -uint8_t * testX3AresultsX2FtestX23string_error(float arg0) -{ - std::vector _deallocate; - auto result0 = exports::test::results::test::StringError((float(arg0))); - for (auto i: _deallocate) { free(i); } - - _deallocate.clear(); - static uintptr_t ret_area[((3*sizeof(void*))+sizeof(uintptr_t)-1)/sizeof(uintptr_t)]; - uint8_t* ptr1 = (uint8_t*)(&ret_area); - if ((result0).has_value()) { - float payload2 = std::move(result0).value(); - *((int8_t*)(ptr1 + 0)) = (int32_t(0)); - *((float*)(ptr1 + sizeof(void*))) = (float(payload2)); - } else { - wit::string payload3 = std::move(result0).error(); - *((int8_t*)(ptr1 + 0)) = (int32_t(1)); - auto const&vec4 = payload3; - auto ptr4 = (uint8_t*)(vec4.data()); - auto len4 = (size_t)(vec4.size()); - payload3.leak(); - - *((size_t*)(ptr1 + (2*sizeof(void*)))) = len4; - *((uint8_t**)(ptr1 + sizeof(void*))) = ptr4; - } - return ptr1; -} -extern "C" __attribute__((__weak__, __export_name__("cabi_post_test:results/test#string-error"))) -void cabi_post_testX3AresultsX2FtestX23string_error(uint8_t * arg0) { - switch ((int32_t) (int32_t) (*((uint8_t*) (arg0 + 0)))) { - case 0: { - break; - } - case 1: { - if ((*((size_t*) (arg0 + (2*sizeof(void*))))) > 0) { - wit::string::drop_raw((void*) (*((uint8_t**) (arg0 + sizeof(void*))))); - } - break; - } - } -} -extern "C" __attribute__((__export_name__("test:results/test#enum-error"))) -uint8_t * testX3AresultsX2FtestX23enum_error(float arg0) -{ - std::vector _deallocate; - auto result0 = exports::test::results::test::EnumError((float(arg0))); - for (auto i: _deallocate) { free(i); } - - _deallocate.clear(); - static uint32_t ret_area[(8+sizeof(uint32_t)-1)/sizeof(uint32_t)]; - uint8_t* ptr1 = (uint8_t*)(&ret_area); - if ((result0).has_value()) { - float payload2 = std::move(result0).value(); - *((int8_t*)(ptr1 + 0)) = (int32_t(0)); - *((float*)(ptr1 + 4)) = (float(payload2)); - } else { - test::results::test::E payload3 = std::move(result0).error(); - *((int8_t*)(ptr1 + 0)) = (int32_t(1)); - *((int8_t*)(ptr1 + 4)) = int32_t(payload3); - } - return ptr1; -} -extern "C" __attribute__((__export_name__("test:results/test#record-error"))) -uint8_t * testX3AresultsX2FtestX23record_error(float arg0) -{ - std::vector _deallocate; - auto result0 = exports::test::results::test::RecordError((float(arg0))); - for (auto i: _deallocate) { free(i); } - - _deallocate.clear(); - static uint32_t ret_area[(12+sizeof(uint32_t)-1)/sizeof(uint32_t)]; - uint8_t* ptr1 = (uint8_t*)(&ret_area); - if ((result0).has_value()) { - float payload2 = std::move(result0).value(); - *((int8_t*)(ptr1 + 0)) = (int32_t(0)); - *((float*)(ptr1 + 4)) = (float(payload2)); - } else { - test::results::test::E2 payload3 = std::move(result0).error(); - *((int8_t*)(ptr1 + 0)) = (int32_t(1)); - *((int32_t*)(ptr1 + 4)) = (int32_t((payload3).line)); - *((int32_t*)(ptr1 + 8)) = (int32_t((payload3).column)); - } - return ptr1; -} -extern "C" __attribute__((__export_name__("test:results/test#variant-error"))) -uint8_t * testX3AresultsX2FtestX23variant_error(float arg0) -{ - std::vector _deallocate; - auto result0 = exports::test::results::test::VariantError((float(arg0))); - for (auto i: _deallocate) { free(i); } - - _deallocate.clear(); - static uint32_t ret_area[(16+sizeof(uint32_t)-1)/sizeof(uint32_t)]; - uint8_t* ptr1 = (uint8_t*)(&ret_area); - if ((result0).has_value()) { - float payload2 = std::move(result0).value(); - *((int8_t*)(ptr1 + 0)) = (int32_t(0)); - *((float*)(ptr1 + 4)) = (float(payload2)); - } else { - test::results::test::E3 payload3 = std::move(result0).error(); - *((int8_t*)(ptr1 + 0)) = (int32_t(1)); - switch ((int32_t) (payload3).variants.index()) { - case 0: { - const test::results::test::E &payload4 = std::get(payload3.variants).value; - *((int8_t*)(ptr1 + 4)) = (int32_t(0)); - *((int8_t*)(ptr1 + 8)) = int32_t(payload4); - break; - } - case 1: { - const test::results::test::E2 &payload5 = std::get(payload3.variants).value; - *((int8_t*)(ptr1 + 4)) = (int32_t(1)); - *((int32_t*)(ptr1 + 8)) = (int32_t((payload5).line)); - *((int32_t*)(ptr1 + 12)) = (int32_t((payload5).column)); - break; - } - } - } - return ptr1; -} -extern "C" __attribute__((__export_name__("test:results/test#empty-error"))) -uint8_t * testX3AresultsX2FtestX23empty_error(int32_t arg0) -{ - std::vector _deallocate; - auto result0 = exports::test::results::test::EmptyError((uint32_t(arg0))); - for (auto i: _deallocate) { free(i); } - - _deallocate.clear(); - static uint32_t ret_area[(8+sizeof(uint32_t)-1)/sizeof(uint32_t)]; - uint8_t* ptr1 = (uint8_t*)(&ret_area); - if ((result0).has_value()) { - uint32_t payload2 = std::move(result0).value(); - *((int8_t*)(ptr1 + 0)) = (int32_t(0)); - *((int32_t*)(ptr1 + 4)) = (int32_t(payload2)); - } else { - - *((int8_t*)(ptr1 + 0)) = (int32_t(1)); - } - return ptr1; -} -extern "C" __attribute__((__export_name__("test:results/test#double-error"))) -uint8_t * testX3AresultsX2FtestX23double_error(int32_t arg0) -{ - std::vector _deallocate; - auto result0 = exports::test::results::test::DoubleError((uint32_t(arg0))); - for (auto i: _deallocate) { free(i); } - - _deallocate.clear(); - static uintptr_t ret_area[((4*sizeof(void*))+sizeof(uintptr_t)-1)/sizeof(uintptr_t)]; - uint8_t* ptr1 = (uint8_t*)(&ret_area); - if ((result0).has_value()) { - std::expected payload2 = std::move(result0).value(); - *((int8_t*)(ptr1 + 0)) = (int32_t(0)); - if ((payload2).has_value()) { - - *((int8_t*)(ptr1 + sizeof(void*))) = (int32_t(0)); - } else { - wit::string payload4 = std::move(payload2).error(); - *((int8_t*)(ptr1 + sizeof(void*))) = (int32_t(1)); - auto const&vec5 = payload4; - auto ptr5 = (uint8_t*)(vec5.data()); - auto len5 = (size_t)(vec5.size()); - payload4.leak(); - - *((size_t*)(ptr1 + (3*sizeof(void*)))) = len5; - *((uint8_t**)(ptr1 + (2*sizeof(void*)))) = ptr5; - } - } else { - wit::string payload6 = std::move(result0).error(); - *((int8_t*)(ptr1 + 0)) = (int32_t(1)); - auto const&vec7 = payload6; - auto ptr7 = (uint8_t*)(vec7.data()); - auto len7 = (size_t)(vec7.size()); - payload6.leak(); - - *((size_t*)(ptr1 + (2*sizeof(void*)))) = len7; - *((uint8_t**)(ptr1 + sizeof(void*))) = ptr7; - } - return ptr1; -} -extern "C" __attribute__((__weak__, __export_name__("cabi_post_test:results/test#double-error"))) -void cabi_post_testX3AresultsX2FtestX23double_error(uint8_t * arg0) { - switch ((int32_t) (int32_t) (*((uint8_t*) (arg0 + 0)))) { - case 0: { - switch ((int32_t) (int32_t) (*((uint8_t*) (arg0 + sizeof(void*))))) { - case 0: { - break; - } - case 1: { - if ((*((size_t*) (arg0 + (3*sizeof(void*))))) > 0) { - wit::string::drop_raw((void*) (*((uint8_t**) (arg0 + (2*sizeof(void*)))))); - } - break; - } - } - break; - } - case 1: { - if ((*((size_t*) (arg0 + (2*sizeof(void*))))) > 0) { - wit::string::drop_raw((void*) (*((uint8_t**) (arg0 + sizeof(void*))))); - } - break; - } - } -} - -// Component Adapters From 91ca5de5236211349dfb79f9b77306a604027ce6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 3 Jun 2025 01:09:47 +0200 Subject: [PATCH 615/672] cargo fmt again --- crates/cpp/src/lib.rs | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index adc88638c..99a0fce6e 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -1152,12 +1152,7 @@ impl CppInterfaceGenerator<'_> { } } - fn generate_function( - &mut self, - func: &Function, - owner: &TypeOwner, - variant: AbiVariant, - ) { + fn generate_function(&mut self, func: &Function, owner: &TypeOwner, variant: AbiVariant) { fn class_namespace( cifg: &CppInterfaceGenerator, func: &Function, @@ -1207,8 +1202,8 @@ impl CppInterfaceGenerator<'_> { match is_special_method(func) { SpecialMethod::ResourceDrop => match lift_lower { LiftLower::LiftArgsLowerResults => { - let module_name = String::from("[export]") - + &self.wasm_import_module.clone().unwrap(); + let module_name = + String::from("[export]") + &self.wasm_import_module.clone().unwrap(); let wasm_sig = self.declare_import(&module_name, &func.name, &[WasmType::I32], &[]); uwriteln!( @@ -1218,8 +1213,7 @@ impl CppInterfaceGenerator<'_> { ); } LiftLower::LowerArgsLiftResults => { - let module_name = - self.wasm_import_module.clone().unwrap(); + let module_name = self.wasm_import_module.clone().unwrap(); let name = self.declare_import(&module_name, &func.name, &[WasmType::I32], &[]); uwriteln!( @@ -1236,8 +1230,8 @@ impl CppInterfaceGenerator<'_> { uwriteln!(self.gen.c_src.src, "{0}::Dtor(({0}*)arg0);", classname); } SpecialMethod::ResourceNew => { - let module_name = String::from("[export]") - + &self.wasm_import_module.clone().unwrap(); + let module_name = + String::from("[export]") + &self.wasm_import_module.clone().unwrap(); let wasm_sig = self.declare_import( &module_name, &func.name, @@ -1252,8 +1246,8 @@ impl CppInterfaceGenerator<'_> { ); } SpecialMethod::ResourceRep => { - let module_name = String::from("[export]") - + &self.wasm_import_module.clone().unwrap(); + let module_name = + String::from("[export]") + &self.wasm_import_module.clone().unwrap(); let wasm_sig = self.declare_import( &module_name, &func.name, @@ -1355,7 +1349,6 @@ impl CppInterfaceGenerator<'_> { self.gen.c_src.src.push_str("}\n"); } } - } // in C this is print_optional_ty From 5f0ad9b4b9ac5fdf5fd84543da939c74b2883a45 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 6 Jun 2025 00:47:26 +0200 Subject: [PATCH 616/672] cargo fmt and fixes --- Cargo.toml | 2 -- crates/core/src/abi.rs | 27 ++++++++++++++------ crates/core/src/lib.rs | 2 +- crates/cpp/src/lib.rs | 48 ++++++++++++++++-------------------- crates/rust/src/bindgen.rs | 11 +++++++-- crates/rust/src/interface.rs | 30 +++++++++++----------- crates/rust/src/lib.rs | 1 + 7 files changed, 66 insertions(+), 55 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 04ea2e686..7ed1a8b55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,7 +49,6 @@ wit-bindgen-moonbit = { path = 'crates/moonbit', version = '0.42.1' } wit-bindgen = { path = 'crates/guest-rust', version = '0.42.1', default-features = false } wit-bindgen-test = { path = 'crates/test', version = '0.42.1' } -wit-bindgen-cpp = { path = 'crates/cpp', version = '0.3.0' } wit-bindgen-bridge = { path = 'crates/bridge', version = '0.1.0' } [[bin]] @@ -68,7 +67,6 @@ 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 } env_logger = "0.11.7" diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index e9c474563..f82f75e82 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -868,7 +868,12 @@ pub fn deallocate_lists_in_types( indirect: bool, bindgen: &mut B, ) { - Generator::new(resolve, bindgen, false).deallocate_in_types(types, operands, indirect, Deallocate::Lists); + Generator::new(resolve, bindgen, false).deallocate_in_types( + types, + operands, + indirect, + Deallocate::Lists, + ); } /// Generate instructions in `bindgen` to deallocate all lists in `ptr` where @@ -2467,7 +2472,7 @@ fn push_flat_list_symmetric<'a>( resolve: &Resolve, mut list: impl Iterator, result: &mut FlatTypes<'_>, -// _symmetric: bool, + // _symmetric: bool, ) -> bool { list.all(|ty| push_flat_symmetric(resolve, ty, result)) } @@ -2486,7 +2491,11 @@ pub fn wasm_signature_symmetric( let mut storage = [WasmType::I32; MAX_FLAT_PARAMS + 1]; let mut params = FlatTypes::new(&mut storage); - let ok = push_flat_list_symmetric(resolve, func.params.iter().map(|(_, param)| param), &mut params); + let ok = push_flat_list_symmetric( + resolve, + func.params.iter().map(|(_, param)| param), + &mut params, + ); // assert_eq!(ok, !params.overflow); let indirect_params = !ok || params.to_vec().len() > MAX_FLAT_PARAMS; @@ -2513,8 +2522,10 @@ pub fn wasm_signature_symmetric( assert!(matches!(old[0], WasmType::I32)); old[0] = WasmType::Pointer; params = FlatTypes::new(&mut storage); - old.iter().for_each(|e| { params.push(*e); }); -// params.push(WasmType::Pointer); + old.iter().for_each(|e| { + params.push(*e); + }); + // params.push(WasmType::Pointer); } } @@ -2538,13 +2549,13 @@ pub fn wasm_signature_symmetric( _ => {} } - let mut storage = [WasmType::I32; MAX_FLAT_RESULTS+1]; + let mut storage = [WasmType::I32; MAX_FLAT_RESULTS + 1]; let mut results = FlatTypes::new(&mut storage); if let Some(ty) = &func.result { push_flat_symmetric(resolve, ty, &mut results); } - let retptr = results.to_vec().len()>MAX_FLAT_RESULTS; + let retptr = results.to_vec().len() > MAX_FLAT_RESULTS; // 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 @@ -2552,7 +2563,7 @@ pub fn wasm_signature_symmetric( // into. if retptr { results = FlatTypes::new(&mut storage); -// results.cur = 0; + // results.cur = 0; match variant { AbiVariant::GuestImport => { assert!(params.push(WasmType::Pointer)); diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 8b37592be..dc2fde01b 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -12,8 +12,8 @@ mod types; pub use types::{TypeInfo, Types}; mod path; pub use path::name_package_module; -pub mod symmetric; mod async_; +pub mod symmetric; pub use async_::AsyncFilterSet; #[derive(Default, Copy, Clone, PartialEq, Eq, Debug)] diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7688616d7..c47ca9a94 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -7,8 +7,8 @@ use std::{ process::{Command, Stdio}, str::FromStr, }; -use wit_bindgen_c::to_c_ident; use symbol_name::{make_external_component, make_external_symbol}; +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, @@ -19,8 +19,8 @@ use wit_bindgen_core::{ Files, InterfaceGenerator, Source, WorldGenerator, }; -mod wamr; mod symbol_name; +mod wamr; pub const RESOURCE_IMPORT_BASE_CLASS_NAME: &str = "ResourceImportBase"; pub const RESOURCE_EXPORT_BASE_CLASS_NAME: &str = "ResourceExportBase"; @@ -1248,12 +1248,11 @@ impl CppInterfaceGenerator<'_> { { if matches!(is_drop, SpecialMethod::Allocate) { res.result.push_str("Owned"); - } if let Some(ty) = &func.result { - res.result.push_str(&(self.type_name( - ty, - outer_namespace, - Flavor::Result(abi_variant), - )+ if matches!(is_drop, SpecialMethod::ResourceRep) { + } + if let Some(ty) = &func.result { + res.result.push_str( + &(self.type_name(ty, outer_namespace, Flavor::Result(abi_variant)) + + if matches!(is_drop, SpecialMethod::ResourceRep) { "*" } else { "" @@ -1301,20 +1300,20 @@ impl CppInterfaceGenerator<'_> { res.arguments .push((name.to_snake_case(), "uint8_t*".into())); } else { - let is_pointer = matches!( - (&is_drop, self.gen.opts.host_side()), - (SpecialMethod::Dtor, _) - | (SpecialMethod::ResourceNew, _) - | (SpecialMethod::ResourceDrop, true) - ) - { - "*" - } else { - "" - }; + let is_pointer = if matches!( + (&is_drop, self.gen.opts.host_side()), + (SpecialMethod::Dtor, _) + | (SpecialMethod::ResourceNew, _) + | (SpecialMethod::ResourceDrop, true) + ) { + "*" + } else { + "" + }; res.arguments.push(( name.to_snake_case(), - self.type_name(param, &res.namespace, Flavor::Argument(abi_variant)) + is_pointer, + self.type_name(param, &res.namespace, Flavor::Argument(abi_variant)) + + is_pointer, )); } } @@ -1476,12 +1475,7 @@ impl CppInterfaceGenerator<'_> { } } - fn generate_function( - &mut self, - func: &Function, - owner: &TypeOwner, - variant: AbiVariant, - ) { + fn generate_function(&mut self, func: &Function, owner: &TypeOwner, variant: AbiVariant) { fn class_namespace( cifg: &CppInterfaceGenerator, func: &Function, @@ -1786,7 +1780,7 @@ impl CppInterfaceGenerator<'_> { } None => make_external_component(&func.name), }; - let import_name = match module_name { + let import_name = match module_name { Some(ref module_name) => make_external_symbol( module_name, &func.name, diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 6a1e6ab3c..aa907a926 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -52,8 +52,15 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { } } - fn declare_import(&mut self, module_prefix: &str, name: &str, params: &[WasmType], results: &[WasmType]) -> String { - let rust_name = String::from(module_prefix) + &make_external_symbol(self.wasm_import_module, name, AbiVariant::GuestImport); + fn declare_import( + &mut self, + module_prefix: &str, + name: &str, + params: &[WasmType], + results: &[WasmType], + ) -> String { + let rust_name = String::from(module_prefix) + + &make_external_symbol(self.wasm_import_module, name, AbiVariant::GuestImport); self.src.push_str(&crate::declare_import( module_prefix, self.wasm_import_module, diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index f34afeb32..b6980d68c 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -220,9 +220,11 @@ impl<'i> InterfaceGenerator<'i> { let module = self.resolve.name_world_key(interface_name); let wasm_import_module = format!("[export]{module}"); let new_name = format!("[resource-new]{resource_name}"); - let external_new = make_external_symbol(&wasm_import_module, &new_name, AbiVariant::GuestImport); + let external_new = + make_external_symbol(&wasm_import_module, &new_name, AbiVariant::GuestImport); let rep_name = format!("[resource-rep]{resource_name}"); - let external_rep = make_external_symbol(&wasm_import_module, &rep_name, AbiVariant::GuestImport); + let external_rep = + make_external_symbol(&wasm_import_module, &rep_name, AbiVariant::GuestImport); let import_new = crate::declare_import( &wasm_import_module, &new_name, @@ -237,7 +239,11 @@ impl<'i> InterfaceGenerator<'i> { &[abi::WasmType::I32], &[abi::WasmType::Pointer], ); - let handle_type = if self.gen.opts.symmetric { "usize" } else { "u32" }; + let handle_type = if self.gen.opts.symmetric { + "usize" + } else { + "u32" + }; uwriteln!( self.src, r#" @@ -257,8 +263,8 @@ fn _resource_rep(handle: {handle_type}) -> *mut u8 unsafe {{ {external_rep}(handle as {handle_type}) }} }} - "#); - } + "# + ); for method in methods { self.src.push_str(method); } @@ -1295,12 +1301,8 @@ unsafe fn call_import(_params: Self::ParamsLower, _results: *mut u8) -> u32 {{ } else { AbiVariant::GuestExport }; - let sig = abi::wasm_signature_symmetric( - self.resolve, - variant, - func, - self.gen.opts.symmetric, - ); + let sig = + abi::wasm_signature_symmetric(self.resolve, variant, func, self.gen.opts.symmetric); let mut params = Vec::new(); for (i, param) in sig.params.iter().enumerate() { let name = format!("arg{}", i); @@ -2827,10 +2829,8 @@ impl<'a> {camel}Borrow<'a>{{ let wasm_resource = self.path_to_wasm_resource(); let drop_name = format!("[resource-drop]{name}"); - let export_name = make_external_symbol( - &wasm_import_module, - &drop_name, - AbiVariant::GuestImport,); + let export_name = + make_external_symbol(&wasm_import_module, &drop_name, AbiVariant::GuestImport); let intrinsic = crate::declare_import( &wasm_import_module, &drop_name, diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 12ab80a3e..b5e8f250a 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -1738,6 +1738,7 @@ fn wasm_type(ty: WasmType) -> &'static str { } fn declare_import( + module_prefix: &str, wasm_import_module: &str, wasm_import_name: &str, rust_name: &str, From 47f422c87abc1e05788c4ad739bfbc08da3a8b85 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 6 Jun 2025 22:58:03 +0200 Subject: [PATCH 617/672] update and fixes --- Cargo.lock | 81 +++++++++++++++----------------- crates/core/src/abi.rs | 2 +- crates/cpp/src/lib.rs | 94 +++++++++++++++++++------------------- crates/rust/src/bindgen.rs | 1 - crates/rust/src/lib.rs | 8 ++-- 5 files changed, 89 insertions(+), 97 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a51ea44bb..481c74940 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -819,7 +819,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.232.0", + "wasm-encoder 0.233.0", "wit-bindgen-core", "wit-component", "wit-parser", @@ -1002,9 +1002,8 @@ checksum = "ddbd7f2a9e3635abe5d4df93b12cadc8d6818079785ee4fab3719ae3c85a064e" [[package]] name = "wasm-compose" -version = "0.232.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d529e2655cd5722a1d21754386461502bf0b3d7a36657b3f16554a416ce4d401" +version = "0.233.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#5837f933b6efd11e79850a18de97cdcb8eb3b930" dependencies = [ "anyhow", "heck 0.4.1", @@ -1016,8 +1015,8 @@ dependencies = [ "serde_derive", "serde_yaml", "smallvec", - "wasm-encoder 0.232.0", - "wasmparser 0.232.0", + "wasm-encoder 0.233.0", + "wasmparser 0.233.0", "wat", ] @@ -1032,12 +1031,11 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.232.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a447e61e38d1226b57e4628edadff36d16760be24a343712ba236b5106c95156" +version = "0.233.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#5837f933b6efd11e79850a18de97cdcb8eb3b930" dependencies = [ "leb128fmt", - "wasmparser 0.232.0", + "wasmparser 0.233.0", ] [[package]] @@ -1058,14 +1056,13 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.232.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71aba7991cf9922a097b2dcc4c36ba5bc207339bdcd3a515530fc2555f01e8eb" +version = "0.233.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#5837f933b6efd11e79850a18de97cdcb8eb3b930" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.232.0", - "wasmparser 0.232.0", + "wasm-encoder 0.233.0", + "wasmparser 0.233.0", ] [[package]] @@ -1081,9 +1078,8 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.232.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917739b33bb1eb0e9a49bcd2637a351931be4578d0cc4d37b908d7a797784fbb" +version = "0.233.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#5837f933b6efd11e79850a18de97cdcb8eb3b930" dependencies = [ "bitflags", "hashbrown", @@ -1094,22 +1090,20 @@ dependencies = [ [[package]] name = "wast" -version = "232.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5a22bfb0c309f5cf4b0cfa4fae77801e52570e88bff1344c1b7673a9977954" +version = "233.0.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#5837f933b6efd11e79850a18de97cdcb8eb3b930" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.0", - "wasm-encoder 0.232.0", + "wasm-encoder 0.233.0", ] [[package]] name = "wat" -version = "1.232.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e2054d3da4289c8634a6ed0dd177e066518f45772c521b6959f5d6109f4c210" +version = "1.233.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#5837f933b6efd11e79850a18de97cdcb8eb3b930" dependencies = [ "wast", ] @@ -1223,8 +1217,8 @@ dependencies = [ "clap", "heck 0.5.0", "indexmap", - "wasm-encoder 0.232.0", - "wasm-metadata 0.232.0", + "wasm-encoder 0.233.0", + "wasm-metadata 0.233.0", "wit-bindgen-core", "wit-component", ] @@ -1236,7 +1230,8 @@ dependencies = [ "anyhow", "clap", "env_logger", - "wasm-encoder 0.232.0", + "wasm-encoder 0.233.0", + "wit-bindgen-bridge", "wit-bindgen-c", "wit-bindgen-core", "wit-bindgen-cpp", @@ -1267,8 +1262,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.232.0", - "wasm-metadata 0.232.0", + "wasm-encoder 0.233.0", + "wasm-metadata 0.233.0", "wit-bindgen-c", "wit-bindgen-core", "wit-component", @@ -1282,7 +1277,7 @@ dependencies = [ "clap", "heck 0.5.0", "indexmap", - "wasm-metadata 0.232.0", + "wasm-metadata 0.233.0", "wit-bindgen-core", "wit-component", "wit-parser", @@ -1332,7 +1327,7 @@ dependencies = [ "serde_json", "syn", "test-helpers", - "wasm-metadata 0.232.0", + "wasm-metadata 0.233.0", "wit-bindgen", "wit-bindgen-core", "wit-bindgen-rt", @@ -1370,8 +1365,8 @@ dependencies = [ "wac-types", "wasi-preview1-component-adapter-provider", "wasm-compose", - "wasm-encoder 0.232.0", - "wasmparser 0.232.0", + "wasm-encoder 0.233.0", + "wasmparser 0.233.0", "wat", "wit-bindgen-csharp", "wit-component", @@ -1380,9 +1375,8 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.232.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d1d7a3444f5039b4461a66dc085d749a832e518a86e8c7498d6fdd9df776ed0" +version = "0.233.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#5837f933b6efd11e79850a18de97cdcb8eb3b930" dependencies = [ "anyhow", "bitflags", @@ -1391,18 +1385,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.232.0", - "wasm-metadata 0.232.0", - "wasmparser 0.232.0", + "wasm-encoder 0.233.0", + "wasm-metadata 0.233.0", + "wasmparser 0.233.0", "wat", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.232.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00a0e031533e3f9082057b09b346f76d3af12a714feccbe8d32f926254319d86" +version = "0.233.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#5837f933b6efd11e79850a18de97cdcb8eb3b930" dependencies = [ "anyhow", "id-arena", @@ -1413,5 +1406,5 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.232.0", + "wasmparser 0.233.0", ] diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index f82f75e82..eeee724d3 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -774,7 +774,7 @@ pub fn lower_flat( value: B::Operand, ty: &Type, ) -> Vec { - let mut generator = Generator::new(resolve, bindgen); + let mut generator = Generator::new(resolve, bindgen, false); generator.stack.push(value); generator.realloc = Some(Realloc::Export("cabi_realloc")); generator.lower(ty); diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index c47ca9a94..fe0f8b034 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -11,7 +11,7 @@ use symbol_name::{make_external_component, make_external_symbol}; 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, + symmetric, uwrite, uwriteln, wit_parser::{ Alignment, ArchitectureSize, Docs, Function, FunctionKind, Handle, Int, InterfaceId, Resolve, SizeAlign, Stability, Type, TypeDefKind, TypeId, TypeOwner, WorldId, WorldKey, @@ -1829,49 +1829,49 @@ impl CppInterfaceGenerator<'_> { } } - 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 { - 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() - } + // 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 { + // 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( @@ -2555,9 +2555,9 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { self.src.push_str(s); } - fn typename_lift(&self, id: TypeId) -> String { - self.gen.type_path(id, true) - } + // fn typename_lift(&self, id: TypeId) -> String { + // self.gen.type_path(id, true) + // } fn let_results(&mut self, amt: usize, results: &mut Vec) { if amt > 0 { diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index aa907a926..c0c219247 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -62,7 +62,6 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { let rust_name = String::from(module_prefix) + &make_external_symbol(self.wasm_import_module, name, AbiVariant::GuestImport); self.src.push_str(&crate::declare_import( - module_prefix, self.wasm_import_module, name, &rust_name, diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index b5e8f250a..7c3d61bf9 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -480,7 +480,7 @@ impl RustWasm { let construct = if self.opts.symmetric { format!("{async_support}::future_support::new_future()") } else { - format!("unsafe {{ {async_support}::future_new::(T::VTABLE) }}") + format!("unsafe {{ {async_support}::future_new::(default, T::VTABLE) }}") }; self.src.push_str(&format!( "\ @@ -500,7 +500,7 @@ pub mod wit_future {{ /// The `default` function provided computes the default value to be sent in /// this future if no other value was otherwise sent. pub fn new(default: fn() -> T) -> ({async_support}::FutureWriter, {async_support}::FutureReader) {{ - unsafe {{ {async_support}::future_new::(default, T::VTABLE) }} + {construct} }} }} ", @@ -1738,7 +1738,7 @@ fn wasm_type(ty: WasmType) -> &'static str { } fn declare_import( - module_prefix: &str, + // module_prefix: &str, wasm_import_module: &str, wasm_import_name: &str, rust_name: &str, @@ -1762,7 +1762,7 @@ fn declare_import( #[cfg(target_arch = \"wasm32\")] #[link(wasm_import_module = \"{wasm_import_module}\")] unsafe extern \"C\" {{ - #[link_name = \"{wasm_import_name}\"] + #[cfg_attr(target_arch = \"wasm32\", link_name = \"{wasm_import_name}\")] fn {rust_name}{sig}; }} From 56ffc83c3cf57ac6aaf70bf09650ab6ada1b8eb6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 7 Jun 2025 12:19:22 +0200 Subject: [PATCH 618/672] mostly working runtime tests again --- crates/core/src/symmetric.rs | 5 ++++- crates/rust/src/bindgen.rs | 8 ++++---- crates/rust/src/interface.rs | 19 ++++++++++++++++--- crates/rust/src/lib.rs | 5 +---- 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/crates/core/src/symmetric.rs b/crates/core/src/symmetric.rs index 2eedd7a16..1a3f4a019 100644 --- a/crates/core/src/symmetric.rs +++ b/crates/core/src/symmetric.rs @@ -25,7 +25,10 @@ fn needs_dealloc2(resolve: &Resolve, tp: &Type) -> bool { TypeDefKind::Handle(_) => false, TypeDefKind::Flags(_) => false, TypeDefKind::Tuple(t) => t.types.iter().any(|f| needs_dealloc2(resolve, f)), - TypeDefKind::Variant(_) => todo!(), + TypeDefKind::Variant(v) => v + .cases + .iter() + .any(|c| c.ty.map_or(false, |t| needs_dealloc2(resolve, &t))), TypeDefKind::Option(tp) => needs_dealloc2(resolve, tp), TypeDefKind::Result(r) => { r.ok.as_ref() diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index c0c219247..17320fd36 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -842,10 +842,10 @@ impl Bindgen for FunctionBindgen<'_, '_> { "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}}", - )); - self.push_str("else {\n::core::ptr::null_mut()\n};\n"); + // self.push_str(&format!( + // "if ptr.is_null()\n{{\n{alloc}::handle_alloc_error({layout});\n}}\nptr\n}}", + // )); + // self.push_str("else {\n::core::ptr::null_mut()\n};\n"); if realloc.is_none() { // If an allocator isn't requested then we must clean up the // allocation ourselves since our callee isn't taking diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index b6980d68c..eb71f08ef 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -244,6 +244,11 @@ impl<'i> InterfaceGenerator<'i> { } else { "u32" }; + let casting = if self.gen.opts.symmetric { + "" + } else { + " as i32" + }; uwriteln!( self.src, r#" @@ -260,7 +265,7 @@ fn _resource_rep(handle: {handle_type}) -> *mut u8 where Self: Sized {{ {import_rep} - unsafe {{ {external_rep}(handle as {handle_type}) }} + unsafe {{ {external_rep}(handle{casting}) }} }} "# @@ -341,6 +346,7 @@ macro_rules! {macro_name} {{ uwriteln!( self.src, r#" #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] + #[allow(non_snake_case)] unsafe extern "C" fn {dtor_symbol}(arg0: usize) {{ $($path_to_types)*::_export_drop_{name}_cabi::<<$ty as $($path_to_types)*::Guest>::{camel}>(arg0) }} @@ -1242,6 +1248,7 @@ unsafe fn call_import(_params: Self::ParamsLower, _results: *mut u8) -> u32 {{ "\ #[cfg_attr(target_arch = \"wasm32\", export_name = \"{export_prefix}{export_name}\")] #[cfg_attr(not(target_arch = \"wasm32\"), no_mangle)] + #[allow(non_snake_case)] unsafe extern \"C\" fn {external_name}\ ", ); @@ -1280,6 +1287,7 @@ unsafe fn call_import(_params: Self::ParamsLower, _results: *mut u8) -> u32 {{ "\ #[cfg_attr(target_arch = \"wasm32\", export_name = \"{export_prefix}cabi_post_{export_name}\")] #[cfg_attr(not(target_arch = \"wasm32\"), no_mangle)] + #[allow(non_snake_case)] unsafe extern \"C\" fn {external_name}\ " ); @@ -2845,7 +2853,7 @@ impl<'a> {camel}Borrow<'a>{{ #[inline] unsafe fn drop(_handle: {handle_type}) {{ {intrinsic} - unsafe {{ {export_name}(_handle) }}; + unsafe {{ {export_name}(_handle{casting}) }}; }} }} "#, @@ -2853,7 +2861,12 @@ impl<'a> {camel}Borrow<'a>{{ "usize" } else { "u32" - } + }, + casting = if self.gen.opts.symmetric { + "" + } else { + " as i32" + }, ); } diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 7c3d61bf9..cbc82bf26 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -1759,15 +1759,12 @@ fn declare_import( } format!( " - #[cfg(target_arch = \"wasm32\")] #[link(wasm_import_module = \"{wasm_import_module}\")] unsafe extern \"C\" {{ + #[allow(non_snake_case)] #[cfg_attr(target_arch = \"wasm32\", link_name = \"{wasm_import_name}\")] fn {rust_name}{sig}; }} - - #[cfg(not(target_arch = \"wasm32\"))] - unsafe extern \"C\" fn {rust_name}{sig} {{ unreachable!() }} " ) } From 6efb808d7f5b581a385640a9d4a4289aa3465d07 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 7 Jun 2025 23:35:39 +0200 Subject: [PATCH 619/672] all standard tests pass again, symmetric test skeleton --- crates/cpp/src/lib.rs | 6 +++--- crates/test/src/lib.rs | 12 ++++++++++++ crates/test/src/rust.rs | 37 +++++++++++++++++++++---------------- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index fe0f8b034..a35e26bd9 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -3117,9 +3117,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { self.src, "auto {var} = {tname}::Owned({tname}::ResourceRep({op}));" ); - if !self.gen.gen.opts.symmetric { - uwriteln!(self.src, "{var}->into_handle();"); - } + // if !self.gen.gen.opts.symmetric { + // uwriteln!(self.src, "{var}->into_handle();"); + // } results.push(format!("std::move({var})")) } AbiVariant::GuestImportAsync => todo!(), diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index c41a35004..238041dd0 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -104,6 +104,10 @@ pub struct Opts { /// Passing `--lang rust` will only test Rust for example. #[clap(short, long, required = true, value_delimiter = ',')] languages: Vec, + + /// Generate code for symmetric ABI and compile to native + #[clap(short, long)] + symmetric: bool, } impl Opts { @@ -525,6 +529,10 @@ impl Runner<'_> { args.push(arg.to_string()); } + if self.is_symmetric() { + args.push(String::from("--symmetric")) + } + codegen_tests.push(( language.clone(), test, @@ -1081,6 +1089,10 @@ status: {}", std::process::exit(1); } } + + fn is_symmetric(&self) -> bool { + self.opts.symmetric + } } struct StepResult<'a> { diff --git a/crates/test/src/rust.rs b/crates/test/src/rust.rs index 9f68f7593..6f08736ea 100644 --- a/crates/test/src/rust.rs +++ b/crates/test/src/rust.rs @@ -132,20 +132,21 @@ path = 'lib.rs' super::write_if_different(&wit_bindgen.join("lib.rs"), "")?; println!("Building `wit-bindgen` from crates.io..."); - runner.run_command( - Command::new("cargo") - .current_dir(&wit_bindgen) - .arg("build") - .arg("-pwit-bindgen") - .arg("-pfutures") - .arg("--target") - .arg(&opts.rust_target), - )?; + let mut cmd = Command::new("cargo"); + cmd.current_dir(&wit_bindgen) + .arg("build") + .arg("-pwit-bindgen") + .arg("-pfutures"); + if !runner.is_symmetric() { + cmd.arg("--target").arg(&opts.rust_target); + } + runner.run_command(&mut cmd)?; - let target_out_dir = wit_bindgen - .join("target") - .join(&opts.rust_target) - .join("debug"); + let mut target_out_dir = wit_bindgen.join("target"); + if !runner.is_symmetric() { + target_out_dir = target_out_dir.join(&opts.rust_target); + } + target_out_dir = target_out_dir.join("debug"); let host_out_dir = wit_bindgen.join("target/debug"); let wit_bindgen_rlib = target_out_dir.join("libwit_bindgen.rlib"); let futures_rlib = target_out_dir.join("libfutures.rlib"); @@ -306,9 +307,13 @@ impl Runner<'_> { } fn produces_component(&self) -> bool { - match self.opts.rust.rust_target.as_str() { - "wasm32-unknown-unknown" | "wasm32-wasi" | "wasm32-wasip1" => false, - _ => true, + if self.is_symmetric() { + true + } else { + match self.opts.rust.rust_target.as_str() { + "wasm32-unknown-unknown" | "wasm32-wasi" | "wasm32-wasip1" => false, + _ => true, + } } } } From 449b47f82a700022a0360431bdd933f0bfa68e9a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 8 Jun 2025 12:46:11 +0200 Subject: [PATCH 620/672] adapt to symmetric --- crates/test/src/lib.rs | 6 +++- crates/test/src/rust.rs | 68 +++++++++++++++++++++++++++++++---------- 2 files changed, 57 insertions(+), 17 deletions(-) diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 238041dd0..d5814921e 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -815,7 +815,11 @@ impl Runner<'_> { let artifacts_dir = root_dir.join(format!("{}-{}", component.name, component.language)); let _ = fs::remove_dir_all(&artifacts_dir); let bindings_dir = artifacts_dir.join("bindings"); - let output = root_dir.join(format!("{}-{}.wasm", component.name, component.language)); + let output = root_dir.join(if self.is_symmetric() { + format!("{}-{}.so", component.name, component.language) + } else { + format!("{}-{}.wasm", component.name, component.language) + }); component .language .obj() diff --git a/crates/test/src/rust.rs b/crates/test/src/rust.rs index 6f08736ea..1c51a476f 100644 --- a/crates/test/src/rust.rs +++ b/crates/test/src/rust.rs @@ -30,6 +30,7 @@ pub struct State { wit_bindgen_rlib: PathBuf, futures_rlib: PathBuf, wit_bindgen_deps: Vec, + native_deps: Vec, } /// Rust-specific configuration of component files @@ -100,14 +101,28 @@ impl LanguageMethods for Rust { let dir = cwd.join(&runner.opts.artifacts).join("rust"); let wit_bindgen = dir.join("wit-bindgen"); - let wit_bindgen_dep = match &opts.rust_wit_bindgen_path { - Some(path) => format!("path = {:?}", cwd.join(path)), - None => { - let version = opts - .rust_wit_bindgen_version - .as_deref() - .unwrap_or(env!("CARGO_PKG_VERSION")); - format!("version = \"{version}\"") + let mut symmetric_runtime = String::new(); + let wit_bindgen_dep = if runner.is_symmetric() { + let bindgen_path = cwd.join("crates/symmetric_executor/dummy-bindgen"); + let executor_path = cwd.join("crates/symmetric_executor"); + symmetric_runtime.push_str(&format!( + "symmetric_executor = {{ path = {executor_path:?} }}\n" + )); + symmetric_runtime.push_str(&format!( + "symmetric_stream = {{ path = \"{}/symmetric_stream\" }}\n", + executor_path.display() + )); + format!("path = {bindgen_path:?}, package = \"mini-bindgen\"") + } else { + match &opts.rust_wit_bindgen_path { + Some(path) => format!("path = {:?}", cwd.join(path)), + None => { + let version = opts + .rust_wit_bindgen_version + .as_deref() + .unwrap_or(env!("CARGO_PKG_VERSION")); + format!("version = \"{version}\"") + } } }; @@ -123,6 +138,7 @@ name = "tmp" [dependencies] wit-bindgen = {{ {wit_bindgen_dep} }} futures = "0.3.31" +{symmetric_runtime} [lib] path = 'lib.rs' @@ -135,10 +151,16 @@ path = 'lib.rs' let mut cmd = Command::new("cargo"); cmd.current_dir(&wit_bindgen) .arg("build") - .arg("-pwit-bindgen") + .arg(if runner.is_symmetric() { + "-pmini-bindgen" + } else { + "-pwit-bindgen" + }) .arg("-pfutures"); if !runner.is_symmetric() { cmd.arg("--target").arg(&opts.rust_target); + } else { + cmd.arg("-psymmetric_executor").arg("-psymmetric_stream"); } runner.run_command(&mut cmd)?; @@ -148,15 +170,26 @@ path = 'lib.rs' } target_out_dir = target_out_dir.join("debug"); let host_out_dir = wit_bindgen.join("target/debug"); - let wit_bindgen_rlib = target_out_dir.join("libwit_bindgen.rlib"); + let wit_bindgen_rlib = target_out_dir.join(if runner.is_symmetric() { + "libmini_bindgen.rlib" + } else { + "libwit_bindgen.rlib" + }); let futures_rlib = target_out_dir.join("libfutures.rlib"); assert!(wit_bindgen_rlib.exists()); assert!(futures_rlib.exists()); + let wit_bindgen_deps = vec![target_out_dir.join("deps"), host_out_dir.join("deps")]; + let mut native_deps = Vec::new(); + if runner.is_symmetric() { + native_deps.push(target_out_dir); + } + runner.rust_state = Some(State { wit_bindgen_rlib, futures_rlib, - wit_bindgen_deps: vec![target_out_dir.join("deps"), host_out_dir.join("deps")], + wit_bindgen_deps, + native_deps, }); Ok(()) } @@ -295,14 +328,17 @@ impl Runner<'_> { .arg(&format!( "--extern=futures={}", state.futures_rlib.display() - )) - .arg("--target") - .arg(&opts.rust_target) - .arg("-Dwarnings") - .arg("-Cdebuginfo=1"); + )); + if !self.is_symmetric() { + cmd.arg("--target").arg(&opts.rust_target); + } + cmd.arg("-Dwarnings").arg("-Cdebuginfo=1"); for dep in state.wit_bindgen_deps.iter() { cmd.arg(&format!("-Ldependency={}", dep.display())); } + for dep in state.native_deps.iter() { + cmd.arg(&format!("-Lnative={}", dep.display())); + } cmd } From 9530b73b74f415e11ccf0ea15d0224bfceea28fb Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 10 Jun 2025 01:29:50 +0200 Subject: [PATCH 621/672] link_name, test links --- TODO.md | 2 ++ crates/rust/src/bindgen.rs | 3 ++ crates/rust/src/interface.rs | 6 +++- crates/rust/src/lib.rs | 4 +++ crates/test/src/c.rs | 4 +-- crates/test/src/cpp.rs | 2 +- crates/test/src/csharp.rs | 2 +- crates/test/src/custom.rs | 2 +- crates/test/src/lib.rs | 68 ++++++++++++++++++++++++++++-------- crates/test/src/moonbit.rs | 2 +- crates/test/src/rust.rs | 4 ++- crates/test/src/wat.rs | 2 +- 12 files changed, 77 insertions(+), 24 deletions(-) diff --git a/TODO.md b/TODO.md index 48667fc4d..56eb26d56 100644 --- a/TODO.md +++ b/TODO.md @@ -63,3 +63,5 @@ * Nested lists * Host: Strings inside records +* Strings test: return-unicode should get out parameter + diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 17320fd36..8ffad35b1 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -61,6 +61,9 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { ) -> String { let rust_name = String::from(module_prefix) + &make_external_symbol(self.wasm_import_module, name, AbiVariant::GuestImport); + if let Some(library) = &self.r#gen.r#gen.opts.link_name { + self.src.push_str(&format!("\n#[link(name = \"{}\")]", library)); + } self.src.push_str(&crate::declare_import( self.wasm_import_module, name, diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index eb71f08ef..02246c1c0 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -1221,6 +1221,7 @@ unsafe fn call_import(_params: Self::ParamsLower, _results: *mut u8) -> u32 {{ Identifier::StreamOrFuturePayload => unreachable!(), }; let export_prefix = self.r#gen.opts.export_prefix.as_deref().unwrap_or(""); + let mut library_name = String::new(); 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( @@ -1231,6 +1232,9 @@ unsafe fn call_import(_params: Self::ParamsLower, _results: *mut u8) -> u32 {{ if let Some(export_prefix) = self.r#gen.opts.export_prefix.as_ref() { external_name.insert_str(0, export_prefix); } + if let Some(library) = &self.r#gen.opts.link_name { + library_name = format!("\n#[link(name = \"{}\")]", library); + } (export_name, external_name) } else { let export_name = func.legacy_core_export_name(wasm_module_export_name.as_deref()); @@ -1248,7 +1252,7 @@ unsafe fn call_import(_params: Self::ParamsLower, _results: *mut u8) -> u32 {{ "\ #[cfg_attr(target_arch = \"wasm32\", export_name = \"{export_prefix}{export_name}\")] #[cfg_attr(not(target_arch = \"wasm32\"), no_mangle)] - #[allow(non_snake_case)] + #[allow(non_snake_case)]{library_name} unsafe extern \"C\" fn {external_name}\ ", ); diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index cbc82bf26..bd5caddd7 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -281,6 +281,10 @@ pub struct Opts { #[cfg_attr(feature = "clap", arg(long, default_value_t = bool::default()))] pub symmetric: bool, + /// Library to link to for imports + #[cfg_attr(feature = "clap", arg(long))] + pub link_name: Option, + /// 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, diff --git a/crates/test/src/c.rs b/crates/test/src/c.rs index 8ffb09cf5..c775615db 100644 --- a/crates/test/src/c.rs +++ b/crates/test/src/c.rs @@ -74,7 +74,7 @@ impl LanguageMethods for C { ] } - fn prepare(&self, runner: &mut Runner<'_>) -> Result<()> { + fn prepare(&self, runner: &mut Runner<'_>, _: &str) -> Result<()> { prepare(runner, clang(runner)) } @@ -109,7 +109,7 @@ impl LanguageMethods for Cpp { C.should_fail_verify(name, config, args) } - fn prepare(&self, runner: &mut Runner<'_>) -> Result<()> { + fn prepare(&self, runner: &mut Runner<'_>, _: &str) -> Result<()> { prepare(runner, clangpp(runner)) } diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index 917d4da41..c120cac20 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -48,7 +48,7 @@ impl LanguageMethods for Cpp17 { false } - fn prepare(&self, runner: &mut crate::Runner<'_>) -> anyhow::Result<()> { + fn prepare(&self, runner: &mut crate::Runner<'_>, _: &str) -> anyhow::Result<()> { let compiler = clangpp(runner); let cwd = std::env::current_dir()?; let dir = cwd.join(&runner.opts.artifacts).join("cpp"); diff --git a/crates/test/src/csharp.rs b/crates/test/src/csharp.rs index 0bfeebbbc..4d3d36db2 100644 --- a/crates/test/src/csharp.rs +++ b/crates/test/src/csharp.rs @@ -43,7 +43,7 @@ impl LanguageMethods for Csharp { config.async_ } - fn prepare(&self, runner: &mut Runner<'_>) -> Result<()> { + fn prepare(&self, runner: &mut Runner<'_>, _: &str) -> Result<()> { runner.run_command(dotnet().arg("--version"))?; Ok(()) diff --git a/crates/test/src/custom.rs b/crates/test/src/custom.rs index c260d8640..01ed96ef8 100644 --- a/crates/test/src/custom.rs +++ b/crates/test/src/custom.rs @@ -117,7 +117,7 @@ impl LanguageMethods for Language { ) } - fn prepare(&self, runner: &mut Runner<'_>) -> Result<()> { + fn prepare(&self, runner: &mut Runner<'_>, _: &str) -> Result<()> { let dir = env::current_dir()? .join(&runner.opts.artifacts) .join(&self.extension); diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index d5814921e..a0858b800 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -433,17 +433,34 @@ impl Runner<'_> { }; assert!(bindgen.args.is_empty()); bindgen.args = config.args.into(); + let mut has_link_name = false; if language == Language::Cpp17 { bindgen.args.retain(|elem| { if elem == "--language=Cpp" { language = Language::Cpp; false } else { + if elem.starts_with("--link_name") { + has_link_name = true; + } true } }); } + if self.is_symmetric() && matches!(kind, Kind::Runner) && !has_link_name { + match &language { + Language::Rust => { + bindgen.args.push(String::from("--link-name")); + bindgen.args.push(String::from("test-rust")); + } + _ => { + println!("Symmetric: --link_name missing from language {language:?}"); + // todo!(); + } + } + } + Ok(Component { name: path.file_stem().unwrap().to_str().unwrap().to_string(), path: path.to_path_buf(), @@ -461,12 +478,12 @@ impl Runner<'_> { let all_languages = self.all_languages(); let mut prepared = HashSet::new(); - let mut prepare = |lang: &Language| -> Result<()> { + let mut prepare = |lang: &Language, name: &str| -> Result<()> { if !self.include_language(lang) || !prepared.insert(lang.clone()) { return Ok(()); } lang.obj() - .prepare(self) + .prepare(self, name) .with_context(|| format!("failed to prepare language {lang}")) }; @@ -474,12 +491,12 @@ impl Runner<'_> { match &test.kind { TestKind::Runtime(c) => { for component in c { - prepare(&component.language)? + prepare(&component.language, &test.name)? } } TestKind::Codegen(_) => { for lang in all_languages.iter() { - prepare(lang)?; + prepare(lang, &test.name)?; } } } @@ -667,10 +684,23 @@ impl Runner<'_> { println!("Compiling {} components:", components.len()); + // if self.is_symmetric() { + // for (_, comp) in tests.iter_mut() { + // match comp.kind { + // Kind::Runner => { + // comp.bindgen.args.push(String::from("--")); + // // TODO: proper language handling + // comp.bindgen.args.push(String::from("test-rust")); + // } + // Kind::Test => (), + // } + // } + // } + // In parallel compile all sources to their binary component // form. let compile_results = components - .par_iter() + .iter() .map(|(test, component)| { let path = self .compile_component(test, component) @@ -816,7 +846,10 @@ impl Runner<'_> { let _ = fs::remove_dir_all(&artifacts_dir); let bindings_dir = artifacts_dir.join("bindings"); let output = root_dir.join(if self.is_symmetric() { - format!("{}-{}.so", component.name, component.language) + match &component.kind { + Kind::Runner => format!("{}-{}_exe", component.name, component.language), + Kind::Test => format!("lib{}-{}.so", component.name, component.language), + } } else { format!("{}-{}.wasm", component.name, component.language) }); @@ -832,15 +865,17 @@ impl Runner<'_> { }; component.language.obj().compile(self, &result)?; - // Double-check the output is indeed a component and it's indeed valid. - let wasm = fs::read(&output) - .with_context(|| format!("failed to read output wasm file {output:?}"))?; - if !wasmparser::Parser::is_component(&wasm) { - bail!("output file {output:?} is not a component"); + if !self.is_symmetric() { + // Double-check the output is indeed a component and it's indeed valid. + let wasm = fs::read(&output) + .with_context(|| format!("failed to read output wasm file {output:?}"))?; + if !wasmparser::Parser::is_component(&wasm) { + bail!("output file {output:?} is not a component"); + } + wasmparser::Validator::new_with_features(wasmparser::WasmFeatures::all()) + .validate_all(&wasm) + .with_context(|| format!("compiler produced invalid wasm file {output:?}"))?; } - wasmparser::Validator::new_with_features(wasmparser::WasmFeatures::all()) - .validate_all(&wasm) - .with_context(|| format!("compiler produced invalid wasm file {output:?}"))?; Ok(output) } @@ -1154,7 +1189,7 @@ trait LanguageMethods { /// Performs any one-time preparation necessary for this language, such as /// downloading or caching dependencies. - fn prepare(&self, runner: &mut Runner<'_>) -> Result<()>; + fn prepare(&self, runner: &mut Runner<'_>, name: &str) -> Result<()>; /// Add some files to the generated directory _before_ calling bindgen fn generate_bindings_prepare( @@ -1182,6 +1217,9 @@ trait LanguageMethods { .arg(format!("%{}", bindgen.world)) .arg("--out-dir") .arg(dir); + if runner.is_symmetric() { + cmd.arg("--symmetric"); + } match bindgen.wit_config.default_bindgen_args { Some(true) | None => { diff --git a/crates/test/src/moonbit.rs b/crates/test/src/moonbit.rs index 5b3dd0fa7..61d1d7846 100644 --- a/crates/test/src/moonbit.rs +++ b/crates/test/src/moonbit.rs @@ -26,7 +26,7 @@ impl LanguageMethods for MoonBit { &["--derive-show", "--derive-eq", "--derive-error"] } - fn prepare(&self, runner: &mut crate::Runner<'_>) -> anyhow::Result<()> { + fn prepare(&self, runner: &mut crate::Runner<'_>, _: &str) -> anyhow::Result<()> { println!("Testing if MoonBit toolchain exists..."); if runner .run_command(Command::new("moon").arg("version")) diff --git a/crates/test/src/rust.rs b/crates/test/src/rust.rs index 1c51a476f..29d5ec295 100644 --- a/crates/test/src/rust.rs +++ b/crates/test/src/rust.rs @@ -95,7 +95,7 @@ impl LanguageMethods for Rust { &["--stubs"] } - fn prepare(&self, runner: &mut Runner<'_>) -> Result<()> { + fn prepare(&self, runner: &mut Runner<'_>, test_name: &str) -> Result<()> { let cwd = env::current_dir()?; let opts = &runner.opts.rust; let dir = cwd.join(&runner.opts.artifacts).join("rust"); @@ -183,6 +183,8 @@ path = 'lib.rs' let mut native_deps = Vec::new(); if runner.is_symmetric() { native_deps.push(target_out_dir); + let root_dir = runner.opts.artifacts.join(test_name); + native_deps.push(root_dir); } runner.rust_state = Some(State { diff --git a/crates/test/src/wat.rs b/crates/test/src/wat.rs index 6209000c2..a6cec77bb 100644 --- a/crates/test/src/wat.rs +++ b/crates/test/src/wat.rs @@ -25,7 +25,7 @@ impl LanguageMethods for Wat { Some(";;@") } - fn prepare(&self, _runner: &mut Runner<'_>) -> Result<()> { + fn prepare(&self, _runner: &mut Runner<'_>, _: &str) -> Result<()> { Ok(()) } From f76a8f3da05d620b545a1a59cb0f33ae2a7fe2b5 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 12 Jun 2025 21:36:56 +0200 Subject: [PATCH 622/672] more proper symmetric abi signature --- Cargo.lock | 16 ++--- crates/core/src/abi.rs | 124 +------------------------------------ crates/rust/src/bindgen.rs | 3 +- 3 files changed, 11 insertions(+), 132 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 869a951f4..691eb73b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1003,7 +1003,7 @@ checksum = "ddbd7f2a9e3635abe5d4df93b12cadc8d6818079785ee4fab3719ae3c85a064e" [[package]] name = "wasm-compose" version = "0.234.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d4183937f90061a16b9a377aa8538b696e7dedfb" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" dependencies = [ "anyhow", "heck 0.4.1", @@ -1032,7 +1032,7 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.234.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d4183937f90061a16b9a377aa8538b696e7dedfb" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" dependencies = [ "leb128fmt", "wasmparser 0.234.0", @@ -1057,7 +1057,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.234.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d4183937f90061a16b9a377aa8538b696e7dedfb" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" dependencies = [ "anyhow", "indexmap", @@ -1079,7 +1079,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.234.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d4183937f90061a16b9a377aa8538b696e7dedfb" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" dependencies = [ "bitflags", "hashbrown", @@ -1091,7 +1091,7 @@ dependencies = [ [[package]] name = "wast" version = "234.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d4183937f90061a16b9a377aa8538b696e7dedfb" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" dependencies = [ "bumpalo", "leb128fmt", @@ -1103,7 +1103,7 @@ dependencies = [ [[package]] name = "wat" version = "1.234.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d4183937f90061a16b9a377aa8538b696e7dedfb" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" dependencies = [ "wast", ] @@ -1376,7 +1376,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.234.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d4183937f90061a16b9a377aa8538b696e7dedfb" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" dependencies = [ "anyhow", "bitflags", @@ -1395,7 +1395,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.234.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#d4183937f90061a16b9a377aa8538b696e7dedfb" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" dependencies = [ "anyhow", "id-arena", diff --git a/crates/core/src/abi.rs b/crates/core/src/abi.rs index eccd553d2..6ce6fdef6 100644 --- a/crates/core/src/abi.rs +++ b/crates/core/src/abi.rs @@ -2452,135 +2452,13 @@ fn cast(from: WasmType, to: WasmType) -> Bitcast { } } -fn push_flat_symmetric(resolve: &Resolve, ty: &Type, vec: &mut FlatTypes<'_>) -> bool { - 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 -fn push_flat_list_symmetric<'a>( - resolve: &Resolve, - mut list: impl Iterator, - result: &mut FlatTypes<'_>, - // _symmetric: bool, -) -> bool { - list.all(|ty| push_flat_symmetric(resolve, ty, result)) -} - 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 storage = [WasmType::I32; MAX_FLAT_PARAMS + 1]; - let mut params = FlatTypes::new(&mut storage); - let ok = push_flat_list_symmetric( - resolve, - func.params.iter().map(|(_, param)| param), - &mut params, - ); - // assert_eq!(ok, !params.overflow); - - let indirect_params = !ok || params.to_vec().len() > MAX_FLAT_PARAMS; - if indirect_params { - params = FlatTypes::new(&mut storage); - params.push(WasmType::Pointer); - } else { - if matches!( - (&func.kind, variant), - ( - crate::FunctionKind::Method(_) | crate::FunctionKind::AsyncMethod(_), - AbiVariant::GuestExport - | AbiVariant::GuestExportAsync - | AbiVariant::GuestExportAsyncStackful - ) - ) { - // Guest exported methods always receive resource rep as first argument - // - // TODO: Ideally you would distinguish between imported and exported - // resource Handles and then use either I32 or Pointer in abi::push_flat(). - // But this contextual information isn't available, yet. - // See https://github.com/bytecodealliance/wasm-tools/pull/1438 for more details. - let mut old = params.to_vec(); - assert!(matches!(old[0], WasmType::I32)); - old[0] = WasmType::Pointer; - params = FlatTypes::new(&mut storage); - old.iter().for_each(|e| { - params.push(*e); - }); - // params.push(WasmType::Pointer); - } - } - - match variant { - AbiVariant::GuestExportAsync => { - return WasmSignature { - params: params.to_vec(), - indirect_params, - results: vec![WasmType::Pointer], - retptr: false, - }; - } - AbiVariant::GuestExportAsyncStackful => { - return WasmSignature { - params: params.to_vec(), - indirect_params, - results: Vec::new(), - retptr: false, - }; - } - _ => {} - } - - let mut storage = [WasmType::I32; MAX_FLAT_RESULTS + 1]; - let mut results = FlatTypes::new(&mut storage); - if let Some(ty) = &func.result { - push_flat_symmetric(resolve, ty, &mut results); - } - - let retptr = results.to_vec().len() > MAX_FLAT_RESULTS; - - // 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 retptr { - results = FlatTypes::new(&mut storage); - // results.cur = 0; - match variant { - AbiVariant::GuestImport => { - assert!(params.push(WasmType::Pointer)); - } - AbiVariant::GuestExport => { - assert!(results.push(WasmType::Pointer)); - } - _ => unreachable!(), - } - } - - WasmSignature { - params: params.to_vec(), - indirect_params, - results: results.to_vec(), - retptr, - } + resolve.wasm_signature_symmetric(variant, func, symmetric) } fn flat_types(resolve: &Resolve, ty: &Type) -> Option> { diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 8ffad35b1..cc1b5bda4 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -62,7 +62,8 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { let rust_name = String::from(module_prefix) + &make_external_symbol(self.wasm_import_module, name, AbiVariant::GuestImport); if let Some(library) = &self.r#gen.r#gen.opts.link_name { - self.src.push_str(&format!("\n#[link(name = \"{}\")]", library)); + self.src + .push_str(&format!("\n#[link(name = \"{}\")]", library)); } self.src.push_str(&crate::declare_import( self.wasm_import_module, From 4aa589eeb747f98d69f711eccf6885c80bf77480 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 12 Jun 2025 22:17:11 +0200 Subject: [PATCH 623/672] fix many warnings and compilation of some tests --- crates/rust/src/bindgen.rs | 4 +- crates/symmetric_executor/Cargo.lock | 268 ++++++++++++++++++ .../dummy-bindgen/src/lib.rs | 2 +- .../src/async_support/future_support.rs | 39 +-- .../src/async_support/stream_support.rs | 12 +- .../symmetric_executor/rust-client/src/lib.rs | 51 +++- crates/symmetric_executor/src/lib.rs | 1 + 7 files changed, 325 insertions(+), 52 deletions(-) diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index cc1b5bda4..37f14ffce 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -776,7 +776,6 @@ impl Bindgen for FunctionBindgen<'_, '_> { } Instruction::StringLift => { - let vec = self.r#gen.path_to_vec(); let tmp = self.tmp(); let len = format!("len{}", tmp); uwriteln!(self.src, "let {len} = {};", operands[1]); @@ -788,6 +787,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { ); results.push(format!("string{tmp}")); } else { + let vec = self.r#gen.path_to_vec(); if self.r#gen.r#gen.opts.symmetric { // symmetric must not access zero page memory uwriteln!( @@ -843,7 +843,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { assert!(self.r#gen.needs_deallocate); //} self.push_str(&format!( - "if !ptr.is_null() {{ _deallocate.push((ptr, {layout})); }}\n" + "if !{result}.is_null() {{ _deallocate.push(({result}, {layout})); }}\n" )); } // self.push_str(&format!( diff --git a/crates/symmetric_executor/Cargo.lock b/crates/symmetric_executor/Cargo.lock index d4123b331..d5a4b6ffd 100644 --- a/crates/symmetric_executor/Cargo.lock +++ b/crates/symmetric_executor/Cargo.lock @@ -2,16 +2,40 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + [[package]] name = "dummy-rt" version = "0.1.0" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "futures" version = "0.3.31" @@ -101,18 +125,83 @@ dependencies = [ "slab", ] +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +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.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[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.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "mini-bindgen" +version = "0.1.0" +dependencies = [ + "dummy-rt", + "wit-bindgen", + "wit-bindgen-symmetric-rt", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + [[package]] name = "pin-project-lite" version = "0.2.15" @@ -125,6 +214,16 @@ 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" @@ -143,6 +242,50 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "slab" version = "0.4.9" @@ -187,6 +330,96 @@ 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.234.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.234.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.234.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" +dependencies = [ + "bitflags", + "hashbrown", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.42.1" +dependencies = [ + "wit-bindgen-rt", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.42.1" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.42.1" +dependencies = [ + "bitflags", + "futures", + "once_cell", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.42.1" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.42.1" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + [[package]] name = "wit-bindgen-symmetric-rt" version = "0.36.0" @@ -194,3 +427,38 @@ dependencies = [ "dummy-rt", "futures", ] + +[[package]] +name = "wit-component" +version = "0.234.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.234.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] diff --git a/crates/symmetric_executor/dummy-bindgen/src/lib.rs b/crates/symmetric_executor/dummy-bindgen/src/lib.rs index 3e4ea18cf..8e239dbe6 100644 --- a/crates/symmetric_executor/dummy-bindgen/src/lib.rs +++ b/crates/symmetric_executor/dummy-bindgen/src/lib.rs @@ -3,7 +3,7 @@ #[cfg(feature = "symmetric")] pub mod rt { pub use dummy_rt::rt::maybe_link_cabi_realloc; - pub use wit_bindgen_symmetric_rt::{async_support, run, EventGenerator, EventSubscription}; + pub use wit_bindgen_symmetric_rt::{async_support, run, Cleanup, EventGenerator, EventSubscription}; } #[cfg(feature = "canonical")] 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 index f68e1de3c..c87dd92bf 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/future_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs @@ -1,6 +1,5 @@ -use core::ptr::{self, NonNull}; use std::{ - alloc::{self, Layout}, + alloc::Layout, future::{Future, IntoFuture}, mem::MaybeUninit, pin::Pin, @@ -11,7 +10,7 @@ use futures::FutureExt; use crate::symmetric_stream::{Address, Buffer}; -use super::{wait_on, Stream}; +use super::{super::Cleanup, wait_on, Stream}; #[doc(hidden)] pub struct FutureVtable { @@ -20,40 +19,6 @@ pub struct FutureVtable { pub lift: unsafe fn(dst: *mut u8) -> T, } -// stolen from guest-rust/rt/src/lib.rs -pub struct Cleanup { - ptr: NonNull, - layout: Layout, -} - -// Usage of the returned pointer is always unsafe and must abide by these -// conventions, but this structure itself has no inherent reason to not be -// send/sync. -unsafe impl Send for Cleanup {} -unsafe impl Sync for Cleanup {} - -impl Cleanup { - pub fn new(layout: Layout) -> (*mut u8, Option) { - if layout.size() == 0 { - return (ptr::null_mut(), None); - } - let ptr = unsafe { alloc::alloc(layout) }; - let ptr = match NonNull::new(ptr) { - Some(ptr) => ptr, - None => alloc::handle_alloc_error(layout), - }; - (ptr.as_ptr(), Some(Cleanup { ptr, layout })) - } -} - -impl Drop for Cleanup { - fn drop(&mut self) { - unsafe { - alloc::dealloc(self.ptr.as_ptr(), self.layout); - } - } -} - pub struct FutureWriter { handle: Stream, vtable: &'static FutureVtable, 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 index 51d914784..cd9dc9c8b 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -47,7 +47,7 @@ pub enum StreamResult { pub struct StreamWrite<'a, T: 'static> { _phantom: PhantomData<&'a T>, writer: &'a mut StreamWriter, - future: Option + 'static + Send>>>, + _future: Option + 'static + Send>>>, values: Vec, } @@ -80,7 +80,7 @@ impl Future for StreamWrite<'_, T> { pub struct StreamWriter { handle: Stream, future: Option + 'static + Send>>>, - vtable: &'static StreamVtable, + _vtable: &'static StreamVtable, } impl StreamWriter { @@ -89,14 +89,14 @@ impl StreamWriter { Self { handle, future: None, - vtable, + _vtable: vtable, } } pub fn write(&mut self, values: Vec) -> StreamWrite<'_, T> { StreamWrite { writer: self, - future: None, + _future: None, _phantom: PhantomData, values, } @@ -186,7 +186,7 @@ impl Drop for StreamWriter { pub struct StreamReader { handle: Stream, future: Option>> + 'static + Send>>>, - vtable: &'static StreamVtable, + _vtable: &'static StreamVtable, } impl fmt::Debug for StreamReader { @@ -203,7 +203,7 @@ impl StreamReader { Self { handle, future: None, - vtable, + _vtable: vtable, } } diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index 3e2258ecf..8e7419212 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -1,8 +1,10 @@ +use core::ptr::{self, NonNull}; 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; +use std::alloc::{self, Layout}; pub mod async_support; mod module; @@ -31,17 +33,54 @@ pub fn register( // } pub unsafe fn subscribe_event_send_ptr(event_send: *mut EventGenerator2) -> EventSubscription { - let gen: EventGenerator = unsafe { EventGenerator::from_handle(event_send as usize) }; + let gener: EventGenerator = unsafe { EventGenerator::from_handle(event_send as usize) }; // (unsafe {Arc::from_raw(event_send.cast()) }); - let subscription = gen.subscribe(); + let subscription = gener.subscribe(); // avoid consuming the generator - std::mem::forget(gen); + std::mem::forget(gener); subscription } pub unsafe fn activate_event_send_ptr(event_send: *mut EventGenerator2) { - let gen: EventGenerator = unsafe { EventGenerator::from_handle(event_send as usize) }; - gen.activate(); + let gener: EventGenerator = unsafe { EventGenerator::from_handle(event_send as usize) }; + gener.activate(); // avoid consuming the generator - std::mem::forget(gen); + std::mem::forget(gener); +} + +// stolen from guest-rust/rt/src/lib.rs +pub struct Cleanup { + ptr: NonNull, + layout: Layout, +} + +// Usage of the returned pointer is always unsafe and must abide by these +// conventions, but this structure itself has no inherent reason to not be +// send/sync. +unsafe impl Send for Cleanup {} +unsafe impl Sync for Cleanup {} + +impl Cleanup { + pub fn new(layout: Layout) -> (*mut u8, Option) { + if layout.size() == 0 { + return (ptr::null_mut(), None); + } + let ptr = unsafe { alloc::alloc(layout) }; + let ptr = match NonNull::new(ptr) { + Some(ptr) => ptr, + None => alloc::handle_alloc_error(layout), + }; + (ptr.as_ptr(), Some(Cleanup { ptr, layout })) + } + pub fn forget(self) { + core::mem::forget(self); + } +} + +impl Drop for Cleanup { + fn drop(&mut self) { + unsafe { + alloc::dealloc(self.ptr.as_ptr(), self.layout); + } + } } diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 1b5f9518b..b6280b723 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -25,6 +25,7 @@ struct Ignore; struct OpaqueData; impl symmetric_executor::GuestCallbackFunction for Ignore {} impl symmetric_executor::GuestCallbackData for OpaqueData {} +#[allow(dead_code)] struct CallbackRegistrationInternal(usize); impl symmetric_executor::GuestEventSubscription for EventSubscriptionInternal { From 0ba43abf64f99d592b14354b385d084a0dd7c9ca Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Jun 2025 10:13:09 +0200 Subject: [PATCH 624/672] simplify stream of strings example --- tests/runtime-async/async/stream-string/runner.rs | 9 +-------- tests/runtime-async/async/stream-string/test.rs | 4 ---- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/tests/runtime-async/async/stream-string/runner.rs b/tests/runtime-async/async/stream-string/runner.rs index f7b770bc5..f0364efc9 100644 --- a/tests/runtime-async/async/stream-string/runner.rs +++ b/tests/runtime-async/async/stream-string/runner.rs @@ -1,18 +1,11 @@ -//@ args = '--async=-none' - include!(env!("BINDINGS")); use wit_bindgen::rt::async_support; use crate::a::b::the_test::f; -use futures_util::StreamExt; +use futures::StreamExt; fn main() { - // I didn't find a different way to tell rust that aX3AbX2Fthe_testX00f - // is defined by the test crate - #[cfg(not(target_arch = "wasm32"))] - test::force_link(); - async_support::block_on(async { let mut stream = f(); let result = stream.next().await; diff --git a/tests/runtime-async/async/stream-string/test.rs b/tests/runtime-async/async/stream-string/test.rs index aa1281849..ec362c1f2 100644 --- a/tests/runtime-async/async/stream-string/test.rs +++ b/tests/runtime-async/async/stream-string/test.rs @@ -21,7 +21,3 @@ impl Guest for Component { rd } } - -#[inline(never)] -#[cfg(not(target_arch = "wasm32"))] -pub fn force_link() {} From be25681301f44c87fe331ff69f1456dd3011df4e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Jun 2025 10:14:10 +0200 Subject: [PATCH 625/672] minimize the differences between symmetric and canonical --- .../rust-client/src/async_support.rs | 2 +- .../src/async_support/stream_support.rs | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 590a3f5ad..2b6340bca 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -12,7 +12,7 @@ use crate::module::symmetric::runtime::symmetric_executor::{ }; pub use future_support::{FutureReader, FutureVtable, FutureWriter}; -pub use stream_support::{results, Stream, StreamReader, StreamVtable, StreamWriter}; +pub use stream_support::{results, Stream, StreamReader, StreamVtable, StreamWriter, stream_new}; pub mod future_support; // later make it non-pub 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 index cd9dc9c8b..4564ed858 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -199,16 +199,16 @@ impl fmt::Debug for StreamReader { impl StreamReader { #[doc(hidden)] - pub fn new(handle: Stream, vtable: &'static StreamVtable) -> Self { + pub unsafe fn new(handle: *mut u8, vtable: &'static StreamVtable) -> Self { Self { - handle, + handle: unsafe { Stream::from_handle(handle as usize) }, future: None, _vtable: vtable, } } pub unsafe fn from_handle(handle: *mut u8, vtable: &'static StreamVtable) -> Self { - Self::new(unsafe { Stream::from_handle(handle as usize) }, vtable) + Self::new(handle, vtable) } /// Cancel the current pending read operation. @@ -278,6 +278,7 @@ impl Drop for StreamReader { } } +/// deprecate this, replace with stream_new pub fn new_stream( vtable: &'static StreamVtable, ) -> (StreamWriter, StreamReader) { @@ -285,6 +286,12 @@ pub fn new_stream( let handle2 = handle.clone(); ( StreamWriter::new(handle, vtable), - StreamReader::new(handle2, vtable), + unsafe { StreamReader::new(handle2.take_handle() as *mut u8, vtable) }, ) } + +pub fn stream_new( + vtable: &'static StreamVtable, +) -> (StreamWriter, StreamReader) { + new_stream(vtable) +} From 00a0341ce6d9ba89bab87711e5250070d8464b4d Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Jun 2025 10:14:55 +0200 Subject: [PATCH 626/672] use the new symmetric rt compatibility crate --- crates/rust/src/lib.rs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index bd5caddd7..9b6b60a51 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -405,11 +405,7 @@ impl RustWasm { } fn async_support_path(&self) -> String { - if self.opts.symmetric { - "wit_bindgen_symmetric_rt::async_support".into() - } else { - format!("{}::async_support", self.runtime_path()) - } + format!("{}::async_support", self.runtime_path()) } fn name_interface( @@ -513,17 +509,14 @@ 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!( - " + let vtable_def = format!( + " const VTABLE: &'static {async_support}::StreamVtable; " - ) - }; + ); let construct = if self.opts.symmetric { - format!("{async_support}::stream_support::new_stream()") + // no unsafe needed + format!("{async_support}::stream_new::(T::VTABLE)") } else { format!("unsafe {{ {async_support}::stream_new::(T::VTABLE) }}") }; From f660bbea6a045690a1a2f904ac0853ddf8854ecc Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Jun 2025 10:15:30 +0200 Subject: [PATCH 627/672] symmetric vtable support --- crates/rust/src/interface.rs | 47 ++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index a28ff4de6..e7cfe18bd 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -645,12 +645,12 @@ macro_rules! {macro_name} {{ format!("unsafe fn dealloc_lists(ptr: *mut u8) {{ {dealloc_lists} }}"); let mut lift_arg = "lift"; let mut lower_arg = "lower"; - let mut dealloc_lists_arg = "dealloc_lists"; + let mut dealloc_lists_arg = "dealloc_lists,"; if let PayloadFor::Stream = payload_for { lift_arg = "lift: Some(lift)"; lower_arg = "lower: Some(lower)"; - dealloc_lists_arg = "dealloc_lists: Some(dealloc_lists)"; + dealloc_lists_arg = "dealloc_lists: Some(dealloc_lists),"; let is_list_canonical = match payload_type { Some(ty) => self.is_list_canonical(ty), @@ -660,19 +660,32 @@ macro_rules! {macro_name} {{ if is_list_canonical { lift_arg = "lift: None"; lower_arg = "lower: None"; - dealloc_lists_arg = "dealloc_lists: None"; + dealloc_lists_arg = "dealloc_lists: None,"; lift_fn = String::new(); lower_fn = String::new(); dealloc_lists_fn = String::new(); } } - - let code = format!( + let mut vtable_part1 = r#" + cancel_write, + cancel_read, + drop_writable, + drop_readable,"#; + let mut vtable_part2 = r#" + new, + start_read, + start_write,"#; + + let mut code = format!( r#" #[doc(hidden)] #[allow(unused_unsafe)] pub mod vtable{ordinal} {{ - +"# + ); + if !self.gen.opts.symmetric { + code.push_str(&format!( + r#" #[cfg(not(target_arch = "wasm32"))] unsafe extern "C" fn cancel_write(_: u32) -> u32 {{ unreachable!() }} #[cfg(not(target_arch = "wasm32"))] @@ -706,25 +719,27 @@ pub mod vtable{ordinal} {{ #[link_name = "[async-lower][{import_prefix}-write-{index}]{func_name}"] fn start_write(_: u32, _: *const u8{start_extra}) -> u32; }} - +"# + )); + } else { + dealloc_lists_arg = ""; + vtable_part1 = ""; + vtable_part2 = ""; + } + code.push_str(&format!(r#" {lift_fn} {lower_fn} {dealloc_lists_fn} pub static VTABLE: {async_support}::{camel}Vtable<{name}> = {async_support}::{camel}Vtable::<{name}> {{ - cancel_write, - cancel_read, - drop_writable, - drop_readable, - {dealloc_lists_arg}, + {vtable_part1} + {dealloc_lists_arg} layout: unsafe {{ ::std::alloc::Layout::from_size_align_unchecked({size}, {align}) }}, {lift_arg}, {lower_arg}, - new, - start_read, - start_write, + {vtable_part2} }}; impl super::{camel}Payload for {name} {{ @@ -732,7 +747,7 @@ pub mod vtable{ordinal} {{ }} }} "#, - ); + )); let map = match payload_for { PayloadFor::Future => &mut self.r#gen.future_payloads, From 2a4e6764c87710bc43621fbbc8e31f051b64a3e3 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Jun 2025 10:16:00 +0200 Subject: [PATCH 628/672] fix lifting casts --- crates/rust/src/bindgen.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index 37f14ffce..1c059bb38 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -479,15 +479,16 @@ impl Bindgen for FunctionBindgen<'_, '_> { .future_payloads .get_index_of(&name) .unwrap(); - if self.r#gen.r#gen.opts.symmetric { - results.push(format!("{async_support}::FutureReader::from_handle({op})")) + let path = self.r#gen.path_to_root(); + let recast = if self.r#gen.r#gen.opts.symmetric { + "" } else { - let path = self.r#gen.path_to_root(); - results.push(format!( - "{async_support}::FutureReader::new\ - ({op} as u32, &{path}wit_future::vtable{ordinal}::VTABLE)" - )) - } + " as u32" + }; + results.push(format!( + "{async_support}::FutureReader::new\ + ({op}{recast}, &{path}wit_future::vtable{ordinal}::VTABLE)" + )); } Instruction::StreamLower { .. } => { @@ -515,15 +516,16 @@ impl Bindgen for FunctionBindgen<'_, '_> { .stream_payloads .get_index_of(&name) .unwrap(); - if self.r#gen.r#gen.opts.symmetric { - results.push(format!("{async_support}::StreamReader::from_handle({op})")) + let path = self.r#gen.path_to_root(); + let recast = if self.r#gen.r#gen.opts.symmetric { + "" } else { - let path = self.r#gen.path_to_root(); - results.push(format!( - "{async_support}::StreamReader::new\ - ({op} as u32, &{path}wit_stream::vtable{ordinal}::VTABLE)" - )) - } + " as u32" + }; + results.push(format!( + "{async_support}::StreamReader::new\ + ({op}{recast}, &{path}wit_stream::vtable{ordinal}::VTABLE)" + )) } Instruction::ErrorContextLower { .. } => { From 5a59e6467fd057ae01622de031c5aca02f8935ff Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Jun 2025 10:18:10 +0200 Subject: [PATCH 629/672] upgrade wit-bindgen dependency --- Cargo.lock | 72 +++++++++++++++++++++++++++--------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 691eb73b7..71bfb0b0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -819,7 +819,7 @@ name = "test-helpers" version = "0.0.0" dependencies = [ "codegen-macro", - "wasm-encoder 0.234.0", + "wasm-encoder 0.235.0", "wit-bindgen-core", "wit-component", "wit-parser", @@ -1002,8 +1002,8 @@ checksum = "ddbd7f2a9e3635abe5d4df93b12cadc8d6818079785ee4fab3719ae3c85a064e" [[package]] name = "wasm-compose" -version = "0.234.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" +version = "0.235.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" dependencies = [ "anyhow", "heck 0.4.1", @@ -1015,8 +1015,8 @@ dependencies = [ "serde_derive", "serde_yaml", "smallvec", - "wasm-encoder 0.234.0", - "wasmparser 0.234.0", + "wasm-encoder 0.235.0", + "wasmparser 0.235.0", "wat", ] @@ -1031,11 +1031,11 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.234.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" +version = "0.235.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" dependencies = [ "leb128fmt", - "wasmparser 0.234.0", + "wasmparser 0.235.0", ] [[package]] @@ -1056,13 +1056,13 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.234.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" +version = "0.235.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.234.0", - "wasmparser 0.234.0", + "wasm-encoder 0.235.0", + "wasmparser 0.235.0", ] [[package]] @@ -1078,8 +1078,8 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.234.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" +version = "0.235.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" dependencies = [ "bitflags", "hashbrown", @@ -1090,20 +1090,20 @@ dependencies = [ [[package]] name = "wast" -version = "234.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" +version = "235.0.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.0", - "wasm-encoder 0.234.0", + "wasm-encoder 0.235.0", ] [[package]] name = "wat" -version = "1.234.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" +version = "1.235.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" dependencies = [ "wast", ] @@ -1217,8 +1217,8 @@ dependencies = [ "clap", "heck 0.5.0", "indexmap", - "wasm-encoder 0.234.0", - "wasm-metadata 0.234.0", + "wasm-encoder 0.235.0", + "wasm-metadata 0.235.0", "wit-bindgen-core", "wit-component", ] @@ -1230,7 +1230,7 @@ dependencies = [ "anyhow", "clap", "env_logger", - "wasm-encoder 0.234.0", + "wasm-encoder 0.235.0", "wit-bindgen-bridge", "wit-bindgen-c", "wit-bindgen-core", @@ -1262,8 +1262,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.234.0", - "wasm-metadata 0.234.0", + "wasm-encoder 0.235.0", + "wasm-metadata 0.235.0", "wit-bindgen-c", "wit-bindgen-core", "wit-component", @@ -1277,7 +1277,7 @@ dependencies = [ "clap", "heck 0.5.0", "indexmap", - "wasm-metadata 0.234.0", + "wasm-metadata 0.235.0", "wit-bindgen-core", "wit-component", "wit-parser", @@ -1327,7 +1327,7 @@ dependencies = [ "serde_json", "syn", "test-helpers", - "wasm-metadata 0.234.0", + "wasm-metadata 0.235.0", "wit-bindgen", "wit-bindgen-core", "wit-bindgen-rt", @@ -1365,8 +1365,8 @@ dependencies = [ "wac-types", "wasi-preview1-component-adapter-provider", "wasm-compose", - "wasm-encoder 0.234.0", - "wasmparser 0.234.0", + "wasm-encoder 0.235.0", + "wasmparser 0.235.0", "wat", "wit-bindgen-csharp", "wit-component", @@ -1375,8 +1375,8 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.234.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" +version = "0.235.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" dependencies = [ "anyhow", "bitflags", @@ -1385,17 +1385,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.234.0", - "wasm-metadata 0.234.0", - "wasmparser 0.234.0", + "wasm-encoder 0.235.0", + "wasm-metadata 0.235.0", + "wasmparser 0.235.0", "wat", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.234.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#b975a3ac3e4a94b538bfe8cbd3edcad243aba36a" +version = "0.235.0" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" dependencies = [ "anyhow", "id-arena", @@ -1406,5 +1406,5 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.234.0", + "wasmparser 0.235.0", ] From bb141c6901a4b4c3c533e1ce592bde591246eab5 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Jun 2025 11:43:28 +0200 Subject: [PATCH 630/672] reduce differenced between symmetric and canonical --- crates/symmetric_executor/dummy-bindgen/Cargo.toml | 3 ++- crates/symmetric_executor/dummy-bindgen/src/lib.rs | 9 ++++++++- .../symmetric_executor/rust-client/src/async_support.rs | 6 +++++- .../rust-client/src/async_support/stream_support.rs | 7 +++---- .../rust-client/src/async_support/subtask.rs | 6 ++++++ 5 files changed, 24 insertions(+), 7 deletions(-) create mode 100644 crates/symmetric_executor/rust-client/src/async_support/subtask.rs diff --git a/crates/symmetric_executor/dummy-bindgen/Cargo.toml b/crates/symmetric_executor/dummy-bindgen/Cargo.toml index b07850c0a..97551013c 100644 --- a/crates/symmetric_executor/dummy-bindgen/Cargo.toml +++ b/crates/symmetric_executor/dummy-bindgen/Cargo.toml @@ -10,6 +10,7 @@ original = { path = "../../guest-rust", optional = true, package = "wit-bindgen" [features] # no default gives you the original wit-bindgen crate with rt -default = [ "symmetric" ] +default = [ "symmetric", "async" ] symmetric = [ "dep:dummy-rt", "dep:wit-bindgen-symmetric-rt" ] canonical = [ "dep:original" ] +async = [] diff --git a/crates/symmetric_executor/dummy-bindgen/src/lib.rs b/crates/symmetric_executor/dummy-bindgen/src/lib.rs index 8e239dbe6..019b5d86b 100644 --- a/crates/symmetric_executor/dummy-bindgen/src/lib.rs +++ b/crates/symmetric_executor/dummy-bindgen/src/lib.rs @@ -3,8 +3,15 @@ #[cfg(feature = "symmetric")] pub mod rt { pub use dummy_rt::rt::maybe_link_cabi_realloc; - pub use wit_bindgen_symmetric_rt::{async_support, run, Cleanup, EventGenerator, EventSubscription}; + pub use wit_bindgen_symmetric_rt::{ + async_support, run, Cleanup, EventGenerator, EventSubscription, + }; } #[cfg(feature = "canonical")] pub use original::rt; + +#[cfg(feature = "async")] +pub use wit_bindgen_symmetric_rt::async_support::{ + block_on, spawn, FutureReader, FutureWriter, StreamReader, StreamResult, StreamWriter, +}; diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 2b6340bca..a45f74835 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -12,11 +12,15 @@ use crate::module::symmetric::runtime::symmetric_executor::{ }; pub use future_support::{FutureReader, FutureVtable, FutureWriter}; -pub use stream_support::{results, Stream, StreamReader, StreamVtable, StreamWriter, stream_new}; +pub use stream_support::{ + results, stream_new, Stream, StreamReader, StreamResult, StreamVtable, StreamWriter, +}; +pub use subtask::Subtask; pub mod future_support; // later make it non-pub pub mod stream_support; +mod subtask; // See https://github.com/rust-lang/rust/issues/13231 for the limitation // / Send constraint on futures for spawn, loosen later 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 index 4564ed858..d00b5a299 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -284,10 +284,9 @@ pub fn new_stream( ) -> (StreamWriter, StreamReader) { let handle = Stream::new(); let handle2 = handle.clone(); - ( - StreamWriter::new(handle, vtable), - unsafe { StreamReader::new(handle2.take_handle() as *mut u8, vtable) }, - ) + (StreamWriter::new(handle, vtable), unsafe { + StreamReader::new(handle2.take_handle() as *mut u8, vtable) + }) } pub fn stream_new( diff --git a/crates/symmetric_executor/rust-client/src/async_support/subtask.rs b/crates/symmetric_executor/rust-client/src/async_support/subtask.rs new file mode 100644 index 000000000..f147bac52 --- /dev/null +++ b/crates/symmetric_executor/rust-client/src/async_support/subtask.rs @@ -0,0 +1,6 @@ +use std::alloc::Layout; + +// dummy to just make the generated code compile, for now +pub unsafe trait Subtask { + type ABI_LAYOUT: Layout; +} From 5c084b0cd0aad90e4736fe269c989ff3abd50f3a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Jun 2025 12:10:51 +0200 Subject: [PATCH 631/672] stream_simple compiles, no test though --- crates/rust/src/interface.rs | 8 +++++--- .../src/async_support/stream_support.rs | 8 +++++++- .../rust-client/src/async_support/subtask.rs | 18 +++++++++++++++++- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index e7cfe18bd..ea1a8a3f2 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -893,9 +893,11 @@ pub mod vtable{ordinal} {{ ) { let param_tys = func.params.iter().map(|(_, ty)| *ty).collect::>(); let async_support = self.r#gen.async_support_path(); - let sig = self - .resolve - .wasm_signature(AbiVariant::GuestImportAsync, func); + let sig = self.resolve.wasm_signature_symmetric( + AbiVariant::GuestImportAsync, + func, + self.r#gen.opts.symmetric, + ); uwriteln!( self.src, " 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 index d00b5a299..8cd56d65a 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -37,10 +37,16 @@ pub mod results { pub struct AbiBuffer(PhantomData); +impl AbiBuffer { + pub fn remaining(&self) -> usize { + todo!() + } +} + #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum StreamResult { Complete(usize), - // Closed, + Dropped, // Cancelled, } diff --git a/crates/symmetric_executor/rust-client/src/async_support/subtask.rs b/crates/symmetric_executor/rust-client/src/async_support/subtask.rs index f147bac52..c9b70cc52 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/subtask.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/subtask.rs @@ -1,6 +1,22 @@ use std::alloc::Layout; +use std::future::Future; // dummy to just make the generated code compile, for now pub unsafe trait Subtask { - type ABI_LAYOUT: Layout; + const ABI_LAYOUT: Layout; + const RESULTS_OFFSET: usize; + type Params; + type Results; + type ParamsLower: Copy; + unsafe fn call_import(params: Self::ParamsLower, results: *mut u8) -> u32; + unsafe fn params_lower(params: Self::Params, dst: *mut u8) -> Self::ParamsLower; + unsafe fn params_dealloc_lists(lower: Self::ParamsLower); + unsafe fn params_dealloc_lists_and_own(lower: Self::ParamsLower); + unsafe fn results_lift(src: *mut u8) -> Self::Results; + fn call(_params: Self::Params) -> impl Future + where + Self: Sized, + { + async { todo!() } + } } From cfe03bcb21b80a93a14451b8c21da50b1733dfd5 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Jun 2025 13:07:29 +0200 Subject: [PATCH 632/672] simple future compiles --- crates/rust/src/lib.rs | 17 ++--- .../rust-client/src/async_support.rs | 21 +++++- .../src/async_support/future_support.rs | 67 +++++++++++-------- 3 files changed, 65 insertions(+), 40 deletions(-) diff --git a/crates/rust/src/lib.rs b/crates/rust/src/lib.rs index 9b6b60a51..dc2d2e328 100644 --- a/crates/rust/src/lib.rs +++ b/crates/rust/src/lib.rs @@ -468,20 +468,13 @@ 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!( - " + let vtable_def = format!( + " const VTABLE: &'static {async_support}::FutureVtable; " - ) - }; - let construct = if self.opts.symmetric { - format!("{async_support}::future_support::new_future()") - } else { - format!("unsafe {{ {async_support}::future_new::(default, T::VTABLE) }}") - }; + ); + let construct = + format!("unsafe {{ {async_support}::future_new::(default, T::VTABLE) }}"); self.src.push_str(&format!( "\ pub mod wit_future {{ diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index a45f74835..5d7821426 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -11,7 +11,7 @@ use crate::module::symmetric::runtime::symmetric_executor::{ self, CallbackState, EventGenerator, EventSubscription, }; -pub use future_support::{FutureReader, FutureVtable, FutureWriter}; +pub use future_support::{future_new, FutureReader, FutureVtable, FutureWriter}; pub use stream_support::{ results, stream_new, Stream, StreamReader, StreamResult, StreamVtable, StreamWriter, }; @@ -173,3 +173,22 @@ pub fn block_on(future: impl Future + 'static) -> T { symmetric_executor::run(); return unsafe { result.to_owned().write().unwrap().assume_init_read() }; } + +pub struct TaskCancelOnDrop; + +impl TaskCancelOnDrop { + pub fn new() -> Self { + todo!(); + // Self + } + pub fn forget(self) {} +} + +pub fn start_task(future: impl Future) -> i32 { + unsafe { spawn_unchecked(future) }; + todo!() +} + +pub unsafe fn callback(_event0: u32, _event1: u32, _event2: u32) -> u32 { + todo!(); +} 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 index c87dd92bf..f70258a8d 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/future_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs @@ -29,8 +29,8 @@ impl FutureWriter { Self { handle, vtable } } - pub fn write(self, data: T) -> CancelableWrite { - CancelableWrite { + pub fn write(self, data: T) -> FutureWrite { + FutureWrite { writer: self, future: None, data: Some(data), @@ -39,16 +39,16 @@ impl FutureWriter { } /// Represents a write operation which may be canceled prior to completion. -pub struct CancelableWrite { +pub struct FutureWrite { writer: FutureWriter, - future: Option + 'static + Send>>>, + future: Option> + 'static + Send>>>, data: Option, } -impl Future for CancelableWrite { - type Output = (); +impl Future for FutureWrite { + type Output = Result<(), ()>; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { let me = self.get_mut(); if me.future.is_none() { @@ -65,16 +65,18 @@ impl Future for CancelableWrite { unsafe { (lower)(data, addr) }; buffer.set_size(1); handle.finish_writing(Some(buffer)); - }) as Pin + Send>>); + Ok(()) + }) + as Pin + Send>>); } me.future.as_mut().unwrap().poll_unpin(cx) } } /// Represents a read operation which may be canceled prior to completion. -pub struct CancelableRead { +pub struct FutureRead { reader: FutureReader, - future: Option> + 'static + Send>>>, + future: Option + 'static + Send>>>, } pub struct FutureReader { @@ -83,19 +85,19 @@ pub struct FutureReader { } impl FutureReader { - pub fn new(handle: Stream, vtable: &'static FutureVtable) -> Self { - Self { handle, vtable } + pub fn new(handle: *mut u8, vtable: &'static FutureVtable) -> Self { + Self { handle: unsafe { Stream::from_handle(handle as usize) }, vtable } } - pub fn read(self) -> CancelableRead { - CancelableRead { + pub fn read(self) -> FutureRead { + FutureRead { reader: self, future: None, } } pub unsafe fn from_handle(handle: *mut u8, vtable: &'static FutureVtable) -> Self { - Self::new(unsafe { Stream::from_handle(handle as usize) }, vtable) + Self::new(handle, vtable) } pub fn take_handle(&self) -> *mut () { @@ -103,10 +105,10 @@ impl FutureReader { } } -impl Future for CancelableRead { - type Output = Option; +impl Future for FutureRead { + type Output = T; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { let me = self.get_mut(); if me.future.is_none() { @@ -125,16 +127,14 @@ impl Future for CancelableRead { if let Some(buffer2) = buffer2 { let count = buffer2.get_size(); if count > 0 { - Some(unsafe { - (vtable.lift)(buffer2.get_address().take_handle() as *mut u8) - }) + unsafe { (vtable.lift)(buffer2.get_address().take_handle() as *mut u8) } } else { // make sure it lives long enough drop(cleanup); - None + todo!() } } else { - None + todo!() } }) as Pin + Send>>); } @@ -149,7 +149,7 @@ impl Future for CancelableRead { } } -impl CancelableRead { +impl FutureRead { pub fn cancel(mut self) -> FutureReader { self.cancel_mut() } @@ -160,8 +160,8 @@ impl CancelableRead { } impl IntoFuture for FutureReader { - type Output = Option; - type IntoFuture = CancelableRead; + type Output = T; + type IntoFuture = FutureRead; /// 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) @@ -178,6 +178,19 @@ pub fn new_future( let handle2 = handle.clone(); ( FutureWriter::new(handle, vtable), - FutureReader::new(handle2, vtable), + FutureReader::new(handle2.take_handle() as *mut u8, vtable), ) } + +pub unsafe fn future_new( + _default: fn() -> T, + vtable: &'static FutureVtable, +) -> (FutureWriter, FutureReader) { + new_future(vtable) + // let handle = Stream::new(); + // let handle2 = handle.clone(); + // ( + // FutureWriter::new(handle, vtable), + // FutureReader::new(handle2, vtable), + // ) +} From 1d581e7ce5b20c4c51f428908db80e32a0fc86ac Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 14 Jun 2025 13:33:49 +0200 Subject: [PATCH 633/672] future-string works --- tests/runtime-async/async/future-string/runner.rs | 4 +--- tests/runtime-async/async/future-string/test.rs | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/runtime-async/async/future-string/runner.rs b/tests/runtime-async/async/future-string/runner.rs index 79cf33604..99f1ccba1 100644 --- a/tests/runtime-async/async/future-string/runner.rs +++ b/tests/runtime-async/async/future-string/runner.rs @@ -1,5 +1,3 @@ -//@ args = '--async=-none' - include!(env!("BINDINGS")); use wit_bindgen::rt::async_support; @@ -9,6 +7,6 @@ use crate::a::b::the_test::f; fn main() { async_support::block_on(async { let result = f().await; - assert_eq!(result, Some(String::from("Hello"))); + assert_eq!(result, String::from("Hello")); }); } diff --git a/tests/runtime-async/async/future-string/test.rs b/tests/runtime-async/async/future-string/test.rs index ba91bf505..bdb01261f 100644 --- a/tests/runtime-async/async/future-string/test.rs +++ b/tests/runtime-async/async/future-string/test.rs @@ -10,9 +10,9 @@ use wit_bindgen::rt::async_support::{self, FutureReader}; impl Guest for Component { fn f() -> FutureReader { - let (wr, rd) = wit_future::new(); + let (wr, rd) = wit_future::new(String::default); async_support::spawn(async move { - wr.write(String::from("Hello")).await; + wr.write(String::from("Hello")).await.unwrap(); }); rd } From 510b79e5af8851c91ec7571ec2c536c27226dc26 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 15 Jun 2025 00:02:03 +0200 Subject: [PATCH 634/672] re-use abi buffer and waitable --- crates/guest-rust/rt/Cargo.toml | 2 + .../rt/src/async_support/abi_buffer.rs | 1 + .../rt/src/async_support/waitable.rs | 16 ++++- .../symmetric_executor/rust-client/Cargo.toml | 2 + .../rust-client/src/async_support.rs | 5 +- .../src/async_support/future_support.rs | 5 +- .../src/async_support/stream_support.rs | 69 +++++++++++++++++++ 7 files changed, 95 insertions(+), 5 deletions(-) diff --git a/crates/guest-rust/rt/Cargo.toml b/crates/guest-rust/rt/Cargo.toml index 83294ec60..21f4a69ed 100644 --- a/crates/guest-rust/rt/Cargo.toml +++ b/crates/guest-rust/rt/Cargo.toml @@ -17,3 +17,5 @@ once_cell = { version = "1.19.0", optional = true } [features] async = ["dep:futures", "dep:once_cell"] +# only for compatibility never used inside this crate +symmetric = [] diff --git a/crates/guest-rust/rt/src/async_support/abi_buffer.rs b/crates/guest-rust/rt/src/async_support/abi_buffer.rs index 1ceb30e25..ec8b7a04b 100644 --- a/crates/guest-rust/rt/src/async_support/abi_buffer.rs +++ b/crates/guest-rust/rt/src/async_support/abi_buffer.rs @@ -125,6 +125,7 @@ impl AbiBuffer { /// This signals that `amt` items are no longer going to be yielded from /// `abi_ptr_and_len`. Additionally this will perform any deallocation /// necessary for the starting `amt` items in this list. + #[cfg(not(feature = "symmetric"))] pub(crate) fn advance(&mut self, amt: usize) { assert!(amt + self.cursor <= self.rust_storage.len()); let Some(dealloc_lists) = self.vtable.dealloc_lists else { diff --git a/crates/guest-rust/rt/src/async_support/waitable.rs b/crates/guest-rust/rt/src/async_support/waitable.rs index 65fcdde9b..d52493e85 100644 --- a/crates/guest-rust/rt/src/async_support/waitable.rs +++ b/crates/guest-rust/rt/src/async_support/waitable.rs @@ -1,13 +1,12 @@ //! Generic support for "any waitable" and performing asynchronous operations on //! that waitable. -use super::cabi; -use std::ffi::c_void; +#[cfg(not(feature = "symmetric"))] +use {super::cabi, std::ffi::c_void, std::ptr}; use std::future::Future; use std::marker; use std::mem; use std::pin::Pin; -use std::ptr; use std::task::{Context, Poll, Waker}; /// Generic future-based operation on any "waitable" in the component model. @@ -163,6 +162,7 @@ where /// /// * Fill in `completion_status` with the result of a completion event. /// * Call `cx.waker().wake()`. + #[cfg(not(feature = "symmetric"))] pub fn register_waker(self: Pin<&mut Self>, waitable: u32, cx: &mut Context) { let (_, mut completion_status) = self.pin_project(); debug_assert!(completion_status.as_mut().code_mut().is_none()); @@ -199,11 +199,21 @@ where } } + #[cfg(feature = "symmetric")] + pub fn register_waker(self: Pin<&mut Self>, _waitable: u32, _cx: &mut Context) { + todo!() + } + #[cfg(feature = "symmetric")] + pub fn unregister_waker(self: Pin<&mut Self>, _waitable: u32) { + todo!() + } + /// Deregisters the corresponding `register_waker` within the current task /// for the `waitable` passed here. /// /// This relinquishes control of the original `completion_status` pointer /// passed to `register_waker` after this call has completed. + #[cfg(not(feature = "symmetric"))] pub fn unregister_waker(self: Pin<&mut Self>, waitable: u32) { // SAFETY: the contract of `wasip3_task_set` is that the returned // pointer is valid for the lifetime of our entire task, so it's valid diff --git a/crates/symmetric_executor/rust-client/Cargo.toml b/crates/symmetric_executor/rust-client/Cargo.toml index 61981dda6..746a15401 100644 --- a/crates/symmetric_executor/rust-client/Cargo.toml +++ b/crates/symmetric_executor/rust-client/Cargo.toml @@ -16,3 +16,5 @@ path = "../dummy-rt" [features] # always off feature never = [] +default = ["symmetric"] +symmetric = [] diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 5d7821426..0ebd0fff9 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -17,10 +17,13 @@ pub use stream_support::{ }; pub use subtask::Subtask; +#[path = "../../../guest-rust/rt/src/async_support/abi_buffer.rs"] +pub mod abi_buffer; pub mod future_support; -// later make it non-pub pub mod stream_support; mod subtask; +#[path = "../../../guest-rust/rt/src/async_support/waitable.rs"] +mod waitable; // See https://github.com/rust-lang/rust/issues/13231 for the limitation // / Send constraint on futures for spawn, loosen later 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 index f70258a8d..a105727cf 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/future_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/future_support.rs @@ -86,7 +86,10 @@ pub struct FutureReader { impl FutureReader { pub fn new(handle: *mut u8, vtable: &'static FutureVtable) -> Self { - Self { handle: unsafe { Stream::from_handle(handle as usize) }, vtable } + Self { + handle: unsafe { Stream::from_handle(handle as usize) }, + vtable, + } } pub fn read(self) -> FutureRead { 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 index 8cd56d65a..e49694695 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -235,6 +235,14 @@ impl StreamReader { pub fn into_handle(self) -> *mut () { self.handle.take_handle() as *mut () } + + pub fn read(&mut self, buf: Vec) -> StreamRead<'_, T> { + StreamRead { + // marker: PhantomData, + reader: self, + buf, + } + } } impl futures::stream::Stream for StreamReader { @@ -284,6 +292,67 @@ impl Drop for StreamReader { } } +pub struct StreamRead<'a, T: 'static> { + // marker: PhantomData<(&'a mut StreamReader, T)>, + buf: Vec, + reader: &'a mut StreamReader, +} + +impl Future for StreamRead<'_, T> { + type Output = (StreamResult, Vec); + + fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { + // TODO: Check whether WaitableOperation helps here + //self.pin_project().poll_complete(cx) + + todo!() + + // let me2 = self.get_mut(); + // let me = &mut me2.reader; + + // if me.future.is_none() { + // let mut buffer2 = Vec::new(); + // std::mem::swap(&mut buffer2, &mut me2.buf); + // 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::())) + // .collect::>(); + // let address = unsafe { Address::from_handle(buffer0.as_mut_ptr() as usize) }; + // let buffer = Buffer::new(address, buffer0.capacity() 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>(buffer0) }) + // } else { + // None + // } + // }) as Pin + 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<'a, T> StreamRead<'a, T> { +// fn pin_project(self: Pin<&mut Self>) -> Pin<&mut WaitableOperation>> { +// // SAFETY: we've chosen that when `Self` is pinned that it translates to +// // always pinning the inner field, so that's codified here. +// unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().op) } +// } +// } + /// deprecate this, replace with stream_new pub fn new_stream( vtable: &'static StreamVtable, From 98b0b9c9f69faf9b4271ec240c57a87136ef4dd8 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 15 Jun 2025 00:22:20 +0200 Subject: [PATCH 635/672] shared abi buffer --- .../rt/src/async_support/waitable.rs | 4 ++-- .../src/async_support/stream_support.rs | 24 +++++++++---------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/crates/guest-rust/rt/src/async_support/waitable.rs b/crates/guest-rust/rt/src/async_support/waitable.rs index d52493e85..d64aeec6a 100644 --- a/crates/guest-rust/rt/src/async_support/waitable.rs +++ b/crates/guest-rust/rt/src/async_support/waitable.rs @@ -1,13 +1,13 @@ //! Generic support for "any waitable" and performing asynchronous operations on //! that waitable. -#[cfg(not(feature = "symmetric"))] -use {super::cabi, std::ffi::c_void, std::ptr}; use std::future::Future; use std::marker; use std::mem; use std::pin::Pin; use std::task::{Context, Poll, Waker}; +#[cfg(not(feature = "symmetric"))] +use {super::cabi, std::ffi::c_void, std::ptr}; /// Generic future-based operation on any "waitable" in the component model. /// 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 index e49694695..9ab9865fc 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -1,6 +1,6 @@ pub use crate::module::symmetric::runtime::symmetric_stream::StreamObj as Stream; use crate::{ - async_support::wait_on, + async_support::{abi_buffer::AbiBuffer, wait_on}, symmetric_stream::{Address, Buffer}, }; use { @@ -35,14 +35,6 @@ pub mod results { pub const CANCELED: isize = 0; } -pub struct AbiBuffer(PhantomData); - -impl AbiBuffer { - pub fn remaining(&self) -> usize { - todo!() - } -} - #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum StreamResult { Complete(usize), @@ -67,13 +59,17 @@ impl Future for StreamWrite<'_, T> { let values: Vec<_> = me.values.drain(..).collect(); if values.is_empty() { // delayed flush - Poll::Ready((StreamResult::Complete(1), AbiBuffer(PhantomData))) + Poll::Ready(( + StreamResult::Complete(1), + AbiBuffer::new(Vec::new(), me.writer._vtable), + )) } else { Pin::new(&mut me.writer).start_send(values).unwrap(); match Pin::new(&mut me.writer).poll_ready(cx) { - Poll::Ready(_) => { - Poll::Ready((StreamResult::Complete(1), AbiBuffer(PhantomData))) - } + Poll::Ready(_) => Poll::Ready(( + StreamResult::Complete(1), + AbiBuffer::new(Vec::new(), me.writer._vtable), + )), Poll::Pending => Poll::Pending, } } @@ -164,6 +160,7 @@ impl Sink> for StreamWriter { let slice = unsafe { std::slice::from_raw_parts_mut(addr.cast::>(), item_len) }; for (a, b) in slice.iter_mut().zip(item.drain(..)) { + // TODO: lower a.write(b); } buffer.set_size(item_len as u64); @@ -267,6 +264,7 @@ impl futures::stream::Stream for StreamReader { if let Some(buffer2) = buffer2 { let count = buffer2.get_size(); buffer0.truncate(count as usize); + // TODO: lift Some(unsafe { mem::transmute::>, Vec>(buffer0) }) } else { None From 8872b609ecd6c9065c6acb4538194ce9c262dbb4 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Mon, 16 Jun 2025 23:07:08 +0200 Subject: [PATCH 636/672] execute symmetric tests --- crates/test/src/lib.rs | 53 ++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index a0858b800..2245f74e6 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -6,6 +6,7 @@ use std::collections::{HashMap, HashSet}; use std::fmt; use std::fs; use std::io::Write; +use std::os::unix::fs::symlink; use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; use std::sync::Arc; @@ -684,19 +685,6 @@ impl Runner<'_> { println!("Compiling {} components:", components.len()); - // if self.is_symmetric() { - // for (_, comp) in tests.iter_mut() { - // match comp.kind { - // Kind::Runner => { - // comp.bindgen.args.push(String::from("--")); - // // TODO: proper language handling - // comp.bindgen.args.push(String::from("test-rust")); - // } - // Kind::Test => (), - // } - // } - // } - // In parallel compile all sources to their binary component // form. let compile_results = components @@ -896,7 +884,9 @@ impl Runner<'_> { // done for async tests at this time to ensure that there's a version of // composition that's done which is at the same version as wasmparser // and friends. - let composed = if case.config.wac.is_none() && test_components.len() == 1 { + let composed = if self.is_symmetric() { + Vec::new() + } else if case.config.wac.is_none() && test_components.len() == 1 { self.compose_wasm_with_wasm_compose(runner_wasm, test_components)? } else { self.compose_wasm_with_wac(case, runner, runner_wasm, test_components)? @@ -911,11 +901,40 @@ impl Runner<'_> { filename.push_str("-"); filename.push_str(test.path.file_name().unwrap().to_str().unwrap()); } - filename.push_str(".wasm"); + if !self.is_symmetric() { + filename.push_str(".wasm"); + } let composed_wasm = dst.join(filename); - write_if_different(&composed_wasm, &composed)?; + if !self.is_symmetric() { + write_if_different(&composed_wasm, &composed)?; - self.run_command(self.test_runner.command().arg(&composed_wasm))?; + self.run_command(self.test_runner.command().arg(&composed_wasm))?; + } else { + if std::fs::exists(composed_wasm.clone())? { + std::fs::remove_dir_all(composed_wasm.clone())?; + } + std::fs::create_dir(composed_wasm.clone())?; + + let mut new_file = composed_wasm.clone(); + new_file.push(&(runner_wasm.file_name().unwrap())); + symlink(runner_wasm, new_file)?; + for (_c, p) in test_components.iter() { + let mut new_file = composed_wasm.clone(); + new_file.push(&(p.file_name().unwrap())); + symlink(p, new_file)?; + } + let cwd = runner_wasm.parent().unwrap().parent().unwrap(); + let dir = cwd.join("rust"); + let wit_bindgen = dir.join("wit-bindgen"); + let so_dir = wit_bindgen.join("target").join("debug").join("deps"); + symlink(so_dir.join("libsymmetric_executor.so"), composed_wasm.join("libsymmetric_executor.so"))?; + symlink(so_dir.join("libsymmetric_stream.so"), composed_wasm.join("libsymmetric_stream.so"))?; + + let mut cmd = Command::new(runner_wasm); + cmd.env("LD_LIBRARY_PATH", "."); + cmd.current_dir(composed_wasm); + self.run_command(&mut cmd)?; + } Ok(()) } From af2588ca9666e4dc10a8694ed16a6b6c445d59ab Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 17 Jun 2025 01:04:59 +0200 Subject: [PATCH 637/672] c++ symmetric linking but not running --- crates/cpp/src/lib.rs | 7 ++++++- crates/test/src/cpp.rs | 28 +++++++++++++++++++--------- crates/test/src/lib.rs | 10 ++++++++-- tests/runtime/numbers/runner.cpp | 1 + 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index a35e26bd9..5b49d2941 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -494,6 +494,11 @@ impl WorldGenerator for Cpp { self.world_id = Some(world); // self.sizes.fill(resolve); if !self.opts.host_side() { + let export_name = if self.opts.symmetric { + "" + } else { + ", __export_name__(\"cabi_realloc\")" + }; uwriteln!( self.c_src_head, r#"#include "{}_cpp.h" @@ -501,7 +506,7 @@ impl WorldGenerator for Cpp { extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size); - __attribute__((__weak__, __export_name__("cabi_realloc"))) + __attribute__((__weak__{export_name})) 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; diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index c120cac20..9ba1ef5c6 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -20,9 +20,13 @@ struct LangConfig { } fn clangpp(runner: &Runner<'_>) -> PathBuf { - match &runner.opts.c.wasi_sdk_path { - Some(path) => path.join("bin/wasm32-wasip2-clang++"), - None => "wasm32-wasip2-clang++".into(), + if runner.is_symmetric() { + "clang++".into() + } else { + match &runner.opts.c.wasi_sdk_path { + Some(path) => path.join("bin/wasm32-wasip2-clang++"), + None => "wasm32-wasip2-clang++".into(), + } } } @@ -141,13 +145,14 @@ impl LanguageMethods for Cpp17 { // Now compile the runner's source code to with the above object and the // component-type object into a final component. let mut cmd = Command::new(compiler); - cmd.arg(&compile.component.path) - .arg(&bindings_object) - .arg(compile.bindings_dir.join(format!( + cmd.arg(&compile.component.path).arg(&bindings_object); + if !runner.is_symmetric() { + cmd.arg(compile.bindings_dir.join(format!( "{}_component_type.o", compile.component.bindgen.world - ))) - .arg("-I") + ))); + } + cmd.arg("-I") .arg(&compile.bindings_dir) .arg("-I") .arg(helper_dir.to_str().unwrap().to_string()) @@ -169,9 +174,14 @@ impl LanguageMethods for Cpp17 { match compile.component.kind { Kind::Runner => {} Kind::Test => { - cmd.arg("-mexec-model=reactor"); + if !runner.is_symmetric() { + cmd.arg("-mexec-model=reactor"); + } } } + if runner.is_symmetric() { + cmd.arg("-fPIC").arg("-shared"); + } runner.run_command(&mut cmd)?; Ok(()) } diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 2245f74e6..5393bbe1a 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -927,8 +927,14 @@ impl Runner<'_> { let dir = cwd.join("rust"); let wit_bindgen = dir.join("wit-bindgen"); let so_dir = wit_bindgen.join("target").join("debug").join("deps"); - symlink(so_dir.join("libsymmetric_executor.so"), composed_wasm.join("libsymmetric_executor.so"))?; - symlink(so_dir.join("libsymmetric_stream.so"), composed_wasm.join("libsymmetric_stream.so"))?; + symlink( + so_dir.join("libsymmetric_executor.so"), + composed_wasm.join("libsymmetric_executor.so"), + )?; + symlink( + so_dir.join("libsymmetric_stream.so"), + composed_wasm.join("libsymmetric_stream.so"), + )?; let mut cmd = Command::new(runner_wasm); cmd.env("LD_LIBRARY_PATH", "."); diff --git a/tests/runtime/numbers/runner.cpp b/tests/runtime/numbers/runner.cpp index 85aaf38a6..89e661ade 100644 --- a/tests/runtime/numbers/runner.cpp +++ b/tests/runtime/numbers/runner.cpp @@ -1,5 +1,6 @@ #include #include +#include #include int main() From db6a3e6a31d308fa1e69f772f44381accc62666b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 17 Jun 2025 01:48:40 +0200 Subject: [PATCH 638/672] working pure c++ symmetric test --- crates/test/src/cpp.rs | 38 +++++++++++++++++++++++++++++++++----- crates/test/src/lib.rs | 2 ++ 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index 9ba1ef5c6..44254b52f 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -10,6 +10,10 @@ use std::process::Command; pub struct Cpp17; +pub struct State { + native_deps: Vec, +} + /// C/C++-specific configuration of component files #[derive(Default, Deserialize)] #[serde(deny_unknown_fields)] @@ -52,7 +56,7 @@ impl LanguageMethods for Cpp17 { false } - fn prepare(&self, runner: &mut crate::Runner<'_>, _: &str) -> anyhow::Result<()> { + fn prepare(&self, runner: &mut crate::Runner<'_>, test_name: &str) -> anyhow::Result<()> { let compiler = clangpp(runner); let cwd = std::env::current_dir()?; let dir = cwd.join(&runner.opts.artifacts).join("cpp"); @@ -69,6 +73,20 @@ impl LanguageMethods for Cpp17 { ); })?; + let mut native_deps = Vec::new(); + if runner.is_symmetric() { + let cwd = std::env::current_dir()?; + let dir = cwd.join(&runner.opts.artifacts).join("rust"); + let wit_bindgen = dir.join("wit-bindgen"); + let mut target_out_dir = wit_bindgen.join("target"); + target_out_dir.push("debug"); + + native_deps.push(target_out_dir); + let root_dir = runner.opts.artifacts.join(test_name); + native_deps.push(root_dir); + } + + runner.cpp_state = Some(State { native_deps }); Ok(()) } @@ -157,9 +175,11 @@ impl LanguageMethods for Cpp17 { .arg("-I") .arg(helper_dir.to_str().unwrap().to_string()) .arg("-I") - .arg(helper_dir2.to_str().unwrap().to_string()) - .arg("-fno-exceptions") - .arg("-Wall") + .arg(helper_dir2.to_str().unwrap().to_string()); + if !runner.is_symmetric() { + cmd.arg("-fno-exceptions"); + } + cmd.arg("-Wall") .arg("-Wextra") .arg("-Werror") .arg("-Wc++-compat") @@ -180,7 +200,15 @@ impl LanguageMethods for Cpp17 { } } if runner.is_symmetric() { - cmd.arg("-fPIC").arg("-shared"); + cmd.arg("-fPIC"); + if !matches!(compile.component.kind, Kind::Runner) { + cmd.arg("-shared"); + } else { + for i in runner.cpp_state.as_ref().unwrap().native_deps.iter() { + cmd.arg(format!("-L{}", i.as_os_str().to_str().unwrap())); + } + cmd.arg("-ltest-cpp17"); + } } runner.run_command(&mut cmd)?; Ok(()) diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 5393bbe1a..067b8f989 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -116,6 +116,7 @@ impl Opts { Runner { opts: self, rust_state: None, + cpp_state: None, wit_bindgen, test_runner: runner::TestRunner::new(&self.runner)?, } @@ -227,6 +228,7 @@ struct Verify<'a> { struct Runner<'a> { opts: &'a Opts, rust_state: Option, + cpp_state: Option, wit_bindgen: &'a Path, test_runner: runner::TestRunner, } From 54116977738b108621182e4e4ef8a63c2b236d3c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 17 Jun 2025 22:54:30 +0200 Subject: [PATCH 639/672] add bitflags (needed by flags) --- crates/symmetric_executor/Cargo.lock | 1 + crates/symmetric_executor/rust-client/Cargo.toml | 2 ++ crates/symmetric_executor/rust-client/src/lib.rs | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/crates/symmetric_executor/Cargo.lock b/crates/symmetric_executor/Cargo.lock index d5a4b6ffd..203befb4d 100644 --- a/crates/symmetric_executor/Cargo.lock +++ b/crates/symmetric_executor/Cargo.lock @@ -424,6 +424,7 @@ dependencies = [ name = "wit-bindgen-symmetric-rt" version = "0.36.0" dependencies = [ + "bitflags", "dummy-rt", "futures", ] diff --git a/crates/symmetric_executor/rust-client/Cargo.toml b/crates/symmetric_executor/rust-client/Cargo.toml index 746a15401..654daa122 100644 --- a/crates/symmetric_executor/rust-client/Cargo.toml +++ b/crates/symmetric_executor/rust-client/Cargo.toml @@ -6,6 +6,7 @@ version = "0.36.0" edition = "2021" [dependencies] +bitflags = "2.9.1" futures = "0.3.31" #wit-bindgen = { path = "../../guest-rust" } @@ -18,3 +19,4 @@ path = "../dummy-rt" never = [] default = ["symmetric"] symmetric = [] +bitflags = [] diff --git a/crates/symmetric_executor/rust-client/src/lib.rs b/crates/symmetric_executor/rust-client/src/lib.rs index 8e7419212..48017ebfd 100644 --- a/crates/symmetric_executor/rust-client/src/lib.rs +++ b/crates/symmetric_executor/rust-client/src/lib.rs @@ -9,6 +9,11 @@ use std::alloc::{self, Layout}; pub mod async_support; mod module; +// Re-export `bitflags` so that we can reference it from macros. +#[cfg(feature = "bitflags")] +#[doc(hidden)] +pub use bitflags; + pub struct EventSubscription2; pub struct EventGenerator2; From ad0215021120d9c1087c98d925936b22bde6386a Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 17 Jun 2025 22:56:28 +0200 Subject: [PATCH 640/672] fix several symmetric tests --- crates/rust/src/interface.rs | 53 +++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index ea1a8a3f2..135d4b561 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -225,18 +225,23 @@ impl<'i> InterfaceGenerator<'i> { let rep_name = format!("[resource-rep]{resource_name}"); let external_rep = make_external_symbol(&wasm_import_module, &rep_name, AbiVariant::GuestImport); + let handle_type = if self.gen.opts.symmetric { + abi::WasmType::Pointer + } else { + abi::WasmType::I32 + }; let import_new = crate::declare_import( &wasm_import_module, &new_name, &external_new, &[abi::WasmType::Pointer], - &[abi::WasmType::I32], + &[handle_type], ); let import_rep = crate::declare_import( &wasm_import_module, &rep_name, &external_rep, - &[abi::WasmType::I32], + &[handle_type], &[abi::WasmType::Pointer], ); let handle_type = if self.gen.opts.symmetric { @@ -245,13 +250,34 @@ impl<'i> InterfaceGenerator<'i> { "u32" }; let casting = if self.gen.opts.symmetric { - "" + " as *mut u8" } else { " as i32" }; - uwriteln!( - self.src, - r#" + if self.gen.opts.symmetric { + uwriteln!( + self.src, + r#" +#[doc(hidden)] +unsafe fn _resource_new(val: *mut u8) -> {handle_type} + where Self: Sized +{{ + val as {handle_type} +}} + +#[doc(hidden)] +fn _resource_rep(handle: {handle_type}) -> *mut u8 + where Self: Sized +{{ + handle as *mut u8 +}} + + "# + ); + } else { + uwriteln!( + self.src, + r#" #[doc(hidden)] unsafe fn _resource_new(val: *mut u8) -> {handle_type} where Self: Sized @@ -269,7 +295,8 @@ fn _resource_rep(handle: {handle_type}) -> *mut u8 }} "# - ); + ); + } for method in methods { self.src.push_str(method); } @@ -343,12 +370,13 @@ macro_rules! {macro_name} {{ &(String::from("[resource-drop]") + &name), AbiVariant::GuestImport, ); + let resource_lowercase = name.to_lower_camel_case(); uwriteln!( self.src, r#" #[cfg_attr(not(target_arch = "wasm32"), no_mangle)] #[allow(non_snake_case)] unsafe extern "C" fn {dtor_symbol}(arg0: usize) {{ - $($path_to_types)*::_export_drop_{name}_cabi::<<$ty as $($path_to_types)*::Guest>::{camel}>(arg0) + $($path_to_types)*::_export_drop_{resource_lowercase}_cabi::<<$ty as $($path_to_types)*::Guest>::{camel}>(arg0) }} "# ); @@ -782,6 +810,7 @@ pub mod vtable{ordinal} {{ && symmetric::has_non_canonical_list_rust(self.resolve, &func.params) { self.needs_deallocate = true; + let _ = self.path_to_std_alloc_module(); uwriteln!( self.src, "let mut _deallocate: Vec<(*mut u8, _rt::alloc::Layout)> = Vec::new();" @@ -2864,7 +2893,11 @@ impl<'a> {camel}Borrow<'a>{{ &wasm_import_module, &drop_name, &export_name, - &[abi::WasmType::I32], + &[if self.gen.opts.symmetric { + abi::WasmType::Pointer + } else { + abi::WasmType::I32 + }], &[], ); uwriteln!( @@ -2884,7 +2917,7 @@ impl<'a> {camel}Borrow<'a>{{ "u32" }, casting = if self.gen.opts.symmetric { - "" + " as *mut u8" } else { " as i32" }, From c88a07925538f9c35e94805f38361e21cebea0ad Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Tue, 17 Jun 2025 23:15:04 +0200 Subject: [PATCH 641/672] use fixed wasm-tools --- Cargo.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 71bfb0b0d..b4da7d04a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1003,7 +1003,7 @@ checksum = "ddbd7f2a9e3635abe5d4df93b12cadc8d6818079785ee4fab3719ae3c85a064e" [[package]] name = "wasm-compose" version = "0.235.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#cab9bcb81e7fc2cf96b31e972557ca1a691872f2" dependencies = [ "anyhow", "heck 0.4.1", @@ -1032,7 +1032,7 @@ dependencies = [ [[package]] name = "wasm-encoder" version = "0.235.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#cab9bcb81e7fc2cf96b31e972557ca1a691872f2" dependencies = [ "leb128fmt", "wasmparser 0.235.0", @@ -1057,7 +1057,7 @@ dependencies = [ [[package]] name = "wasm-metadata" version = "0.235.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#cab9bcb81e7fc2cf96b31e972557ca1a691872f2" dependencies = [ "anyhow", "indexmap", @@ -1079,7 +1079,7 @@ dependencies = [ [[package]] name = "wasmparser" version = "0.235.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#cab9bcb81e7fc2cf96b31e972557ca1a691872f2" dependencies = [ "bitflags", "hashbrown", @@ -1091,7 +1091,7 @@ dependencies = [ [[package]] name = "wast" version = "235.0.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#cab9bcb81e7fc2cf96b31e972557ca1a691872f2" dependencies = [ "bumpalo", "leb128fmt", @@ -1103,7 +1103,7 @@ dependencies = [ [[package]] name = "wat" version = "1.235.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#cab9bcb81e7fc2cf96b31e972557ca1a691872f2" dependencies = [ "wast", ] @@ -1376,7 +1376,7 @@ dependencies = [ [[package]] name = "wit-component" version = "0.235.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#cab9bcb81e7fc2cf96b31e972557ca1a691872f2" dependencies = [ "anyhow", "bitflags", @@ -1395,7 +1395,7 @@ dependencies = [ [[package]] name = "wit-parser" version = "0.235.0" -source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#31350999b658538ac0819bb8a255e7d171c2b321" +source = "git+https://github.com/cpetig/wasm-tools?branch=symmetric#cab9bcb81e7fc2cf96b31e972557ca1a691872f2" dependencies = [ "anyhow", "id-arena", From 26036d69f241c1e2933180ebe60e5754bc8dcf1c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 18 Jun 2025 01:05:24 +0200 Subject: [PATCH 642/672] link name fixes --- crates/rust/src/bindgen.rs | 2 +- crates/rust/src/interface.rs | 10 +++++----- crates/test/src/lib.rs | 5 +---- tests/runtime/resource_floats/intermediate.rs | 2 ++ tests/runtime/resource_floats/runner.rs | 2 ++ 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/rust/src/bindgen.rs b/crates/rust/src/bindgen.rs index d91c70cb4..455397ec0 100644 --- a/crates/rust/src/bindgen.rs +++ b/crates/rust/src/bindgen.rs @@ -445,7 +445,7 @@ impl Bindgen for FunctionBindgen<'_, '_> { &{tmp} }}", cast = if self.r#gen.r#gen.opts.symmetric { - "" + " as usize" } else { " as u32" } diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 135d4b561..1ad278e5c 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -1267,7 +1267,7 @@ unsafe fn call_import(_params: Self::ParamsLower, _results: *mut u8) -> u32 {{ Identifier::StreamOrFuturePayload => unreachable!(), }; let export_prefix = self.r#gen.opts.export_prefix.as_deref().unwrap_or(""); - let mut library_name = String::new(); + // let mut library_name = String::new(); 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( @@ -1278,9 +1278,9 @@ unsafe fn call_import(_params: Self::ParamsLower, _results: *mut u8) -> u32 {{ if let Some(export_prefix) = self.r#gen.opts.export_prefix.as_ref() { external_name.insert_str(0, export_prefix); } - if let Some(library) = &self.r#gen.opts.link_name { - library_name = format!("\n#[link(name = \"{}\")]", library); - } + // if let Some(library) = &self.r#gen.opts.link_name { + // library_name = format!("\n#[link(name = \"{}\")]", library); + // } (export_name, external_name) } else { let export_name = func.legacy_core_export_name(wasm_module_export_name.as_deref()); @@ -1298,7 +1298,7 @@ unsafe fn call_import(_params: Self::ParamsLower, _results: *mut u8) -> u32 {{ "\ #[cfg_attr(target_arch = \"wasm32\", export_name = \"{export_prefix}{export_name}\")] #[cfg_attr(not(target_arch = \"wasm32\"), no_mangle)] - #[allow(non_snake_case)]{library_name} + #[allow(non_snake_case)] unsafe extern \"C\" fn {external_name}\ ", ); diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 067b8f989..e0aa47a98 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -436,21 +436,18 @@ impl Runner<'_> { }; assert!(bindgen.args.is_empty()); bindgen.args = config.args.into(); - let mut has_link_name = false; if language == Language::Cpp17 { bindgen.args.retain(|elem| { if elem == "--language=Cpp" { language = Language::Cpp; false } else { - if elem.starts_with("--link_name") { - has_link_name = true; - } true } }); } + let has_link_name = bindgen.args.iter().any(|elem| elem.starts_with("--link-name")); if self.is_symmetric() && matches!(kind, Kind::Runner) && !has_link_name { match &language { Language::Rust => { diff --git a/tests/runtime/resource_floats/intermediate.rs b/tests/runtime/resource_floats/intermediate.rs index 31bb862b0..c18fae6ef 100644 --- a/tests/runtime/resource_floats/intermediate.rs +++ b/tests/runtime/resource_floats/intermediate.rs @@ -1,3 +1,5 @@ +//@ args = '--link-name leaf-rust' + include!(env!("BINDINGS")); use exports::exports::{Float as FloatExport, GuestFloat}; diff --git a/tests/runtime/resource_floats/runner.rs b/tests/runtime/resource_floats/runner.rs index a41586aa5..a6b57f97e 100644 --- a/tests/runtime/resource_floats/runner.rs +++ b/tests/runtime/resource_floats/runner.rs @@ -1,3 +1,5 @@ +//@ args = '--link-name intermediate-rust' + include!(env!("BINDINGS")); use exports::Float as Float2; From 5585b084f167ad14c8200569da30d330e0daa4be Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 18 Jun 2025 22:18:06 +0200 Subject: [PATCH 643/672] c++ future test --- crates/test/src/cpp.rs | 34 ++++++++++++------- crates/test/src/lib.rs | 5 ++- .../async/future-string/runner.cpp | 14 ++++++++ .../async/future-string/test.cpp | 7 ++++ 4 files changed, 47 insertions(+), 13 deletions(-) create mode 100644 tests/runtime-async/async/future-string/runner.cpp create mode 100644 tests/runtime-async/async/future-string/test.cpp diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index 44254b52f..ccb71dcc3 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -129,10 +129,15 @@ impl LanguageMethods for Cpp17 { helper_dir.push("cpp"); helper_dir.push("helper-types"); // for expected - let mut helper_dir2 = cwd; + let mut helper_dir2 = cwd.clone(); helper_dir2.push("crates"); helper_dir2.push("cpp"); helper_dir2.push("test_headers"); + // for async_support.h + let mut helper_dir3 = cwd.clone(); + helper_dir3.push("crates"); + helper_dir3.push("symmetric_executor"); + helper_dir3.push("cpp-client"); // Compile the C-based bindings to an object file. let bindings_object = compile.output.with_extension("bindings.o"); @@ -147,17 +152,20 @@ impl LanguageMethods for Cpp17 { .arg("-I") .arg(helper_dir.to_str().unwrap().to_string()) .arg("-I") - .arg(helper_dir2.to_str().unwrap().to_string()) - .arg("-fno-exceptions") - .arg("-Wall") - .arg("-Wextra") - .arg("-Werror") - .arg("-Wno-unused-parameter") - .arg("-std=c++17") - .arg("-c") - .arg("-g") - .arg("-o") - .arg(&bindings_object); + .arg(helper_dir2.to_str().unwrap().to_string()); + if runner.is_symmetric() { + cmd.arg("-I").arg(helper_dir3.to_str().unwrap().to_string()); + } + cmd.arg("-fno-exceptions") + .arg("-Wall") + .arg("-Wextra") + .arg("-Werror") + .arg("-Wno-unused-parameter") + .arg("-std=c++17") + .arg("-c") + .arg("-g") + .arg("-o") + .arg(&bindings_object); runner.run_command(&mut cmd)?; // Now compile the runner's source code to with the above object and the @@ -178,6 +186,8 @@ impl LanguageMethods for Cpp17 { .arg(helper_dir2.to_str().unwrap().to_string()); if !runner.is_symmetric() { cmd.arg("-fno-exceptions"); + } else { + cmd.arg("-I").arg(helper_dir3.to_str().unwrap().to_string()); } cmd.arg("-Wall") .arg("-Wextra") diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index e0aa47a98..286570f09 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -447,7 +447,10 @@ impl Runner<'_> { }); } - let has_link_name = bindgen.args.iter().any(|elem| elem.starts_with("--link-name")); + let has_link_name = bindgen + .args + .iter() + .any(|elem| elem.starts_with("--link-name")); if self.is_symmetric() && matches!(kind, Kind::Runner) && !has_link_name { match &language { Language::Rust => { diff --git a/tests/runtime-async/async/future-string/runner.cpp b/tests/runtime-async/async/future-string/runner.cpp new file mode 100644 index 000000000..12cfe52bb --- /dev/null +++ b/tests/runtime-async/async/future-string/runner.cpp @@ -0,0 +1,14 @@ +#include + +void assert_str(wit::string const& str, const char* expected) { + size_t expected_len = strlen(expected); + assert(str.size() == expected_len); + assert(memcmp(str.data(), expected, expected_len) == 0); +} + +int main() { + std::future f = a::b::the_test::F(); + wit::string s = f.get(); + assert_str(s, "Hello"); + return 0; +} diff --git a/tests/runtime-async/async/future-string/test.cpp b/tests/runtime-async/async/future-string/test.cpp new file mode 100644 index 000000000..6f36de99a --- /dev/null +++ b/tests/runtime-async/async/future-string/test.cpp @@ -0,0 +1,7 @@ +# include + +std::future exports::a::b::the_test::F() { + std::promise result; + result.set_value(wit::string::from_view("Hello")); + return result.get_future(); +} From 9460735c6ca422216aac38e285e7ba7364df1e53 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 18 Jun 2025 22:35:38 +0200 Subject: [PATCH 644/672] regenerate rust binding --- crates/cpp/tests/symmetric_future/Cargo.lock | 18 +- .../tests/symmetric_future/future/Cargo.toml | 6 +- .../future/src/future_world.rs | 359 +++++++++--------- .../tests/symmetric_future/future/src/lib.rs | 8 +- 4 files changed, 202 insertions(+), 189 deletions(-) diff --git a/crates/cpp/tests/symmetric_future/Cargo.lock b/crates/cpp/tests/symmetric_future/Cargo.lock index 68fd3f217..ea26f3672 100644 --- a/crates/cpp/tests/symmetric_future/Cargo.lock +++ b/crates/cpp/tests/symmetric_future/Cargo.lock @@ -8,6 +8,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + [[package]] name = "dummy-rt" version = "0.1.0" @@ -16,11 +22,10 @@ version = "0.1.0" name = "future" version = "0.1.0" dependencies = [ - "dummy-rt", "futures", + "mini-bindgen", "source", "symmetric_stream", - "wit-bindgen-symmetric-rt", ] [[package]] @@ -134,6 +139,14 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "mini-bindgen" +version = "0.1.0" +dependencies = [ + "dummy-rt", + "wit-bindgen-symmetric-rt", +] + [[package]] name = "pin-project-lite" version = "0.2.16" @@ -221,6 +234,7 @@ checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" name = "wit-bindgen-symmetric-rt" version = "0.36.0" dependencies = [ + "bitflags", "dummy-rt", "futures", ] diff --git a/crates/cpp/tests/symmetric_future/future/Cargo.toml b/crates/cpp/tests/symmetric_future/future/Cargo.toml index e011077b4..d218ec8a2 100644 --- a/crates/cpp/tests/symmetric_future/future/Cargo.toml +++ b/crates/cpp/tests/symmetric_future/future/Cargo.toml @@ -6,12 +6,8 @@ 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" +wit-bindgen = { path = "../../../../symmetric_executor/dummy-bindgen", package = "mini-bindgen" } [lib] crate-type = ["cdylib"] diff --git a/crates/cpp/tests/symmetric_future/future/src/future_world.rs b/crates/cpp/tests/symmetric_future/future/src/future_world.rs index b4a38afdc..cd03ac284 100644 --- a/crates/cpp/tests/symmetric_future/future/src/future_world.rs +++ b/crates/cpp/tests/symmetric_future/future/src/future_world.rs @@ -1,199 +1,201 @@ -// Generated by `wit-bindgen` 0.40.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.42.1. 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 { - 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), - ::VTABLE, - ) - } - } + pub mod test { + + #[allow(dead_code, async_fn_in_trait, 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)] + #[allow(async_fn_in_trait)] + pub fn create() -> wit_bindgen::rt::async_support::FutureReader{ + unsafe { + + #[link(wasm_import_module = "test:test/future-source")] + unsafe extern "C" { + #[allow(non_snake_case)] + #[cfg_attr(target_arch = "wasm32", link_name = "create")] + fn testX3AtestX2Ffuture_sourceX00create() -> *mut u8; + } + let ret = testX3AtestX2Ffuture_sourceX00create(); + wit_bindgen::rt::async_support::FutureReader::new(ret, &super::super::super::wit_future::vtable0::VTABLE) } + } + } + + } } #[allow(dead_code, clippy::all)] pub mod exports { + pub mod test { 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() -> 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; - } - #[doc(hidden)] - - macro_rules! __export_test_test_future_test_cabi{ + + #[allow(dead_code, async_fn_in_trait, 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, unused_unsafe)] + pub unsafe fn _export_create_cabi() -> *mut u8 { unsafe {#[cfg(target_arch="wasm32")] + _rt::run_ctors_once();let result0 = { + T::create() + }; + (result0).take_handle() as *mut u8 + } } + pub trait Guest { + #[allow(async_fn_in_trait)] + fn create() -> wit_bindgen::rt::async_support::FutureReader; + } + #[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>() + #[allow(non_snake_case)] + unsafe extern "C" fn testX3AtestX2Ffuture_testX00create() -> *mut u8 { + unsafe { $($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: T) -> i32 { - t.as_i32() - } + #[doc(hidden)] + pub(crate) use __export_test_test_future_test_cabi; - 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; + } +} +} +mod _rt { + #![allow(dead_code, clippy::all)] + + pub fn as_i32(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")] + pub fn run_ctors_once() { + wit_bindgen::rt::run_ctors_once(); + } } pub mod wit_future { - #![allow(dead_code, unused_variables, clippy::all)] - - #[doc(hidden)] - pub trait FuturePayload: Unpin + Sized + 'static { - const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::FutureVtable; - } - #[doc(hidden)] - #[allow(unused_unsafe)] - pub mod vtable0 { - unsafe fn lift(ptr: *mut u8) -> u32 { - unsafe { - let l0 = *ptr.add(0).cast::(); - - l0 as u32 - } - } - unsafe fn lower(value: u32, ptr: *mut u8) { - unsafe { - *ptr.add(0).cast::() = super::super::_rt::as_i32(value); - } - } - pub static VTABLE: wit_bindgen_symmetric_rt::async_support::FutureVtable = - wit_bindgen_symmetric_rt::async_support::FutureVtable:: { - layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(4, 4) }, - lift, - lower, - }; - - impl super::FuturePayload for u32 { - const VTABLE: &'static wit_bindgen_symmetric_rt::async_support::FutureVtable = - &VTABLE; - } - } - /// Creates a new Component Model `future` with the specified payload type. - pub fn new() -> ( - wit_bindgen_symmetric_rt::async_support::FutureWriter, - wit_bindgen_symmetric_rt::async_support::FutureReader, - ) { - wit_bindgen_symmetric_rt::async_support::future_support::new_future(T::VTABLE) - } + #![allow(dead_code, unused_variables, clippy::all)] + + #[doc(hidden)] + pub trait FuturePayload: Unpin + Sized + 'static { + const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable; + } + #[doc(hidden)] + #[allow(unused_unsafe)] + pub mod vtable0 { + + unsafe fn lift(ptr: *mut u8) -> u32 { unsafe { let l0 = *ptr.add(0).cast::(); + + l0 as u32 } } + unsafe fn lower(value: u32, ptr: *mut u8) { unsafe { *ptr.add(0).cast::() = super::super::_rt::as_i32(value); + } } + unsafe fn dealloc_lists(ptr: *mut u8) { unsafe { } } + + pub static VTABLE: wit_bindgen::rt::async_support::FutureVtable = wit_bindgen::rt::async_support::FutureVtable:: { + + + layout: unsafe { + ::std::alloc::Layout::from_size_align_unchecked(4, 4) + }, + lift, + lower, + + }; + + impl super::FuturePayload for u32 { + const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable = &VTABLE; + } +} +/// Creates a new Component Model `future` with the specified payload type. +/// +/// The `default` function provided computes the default value to be sent in +/// this future if no other value was otherwise sent. +pub fn new(default: fn() -> T) -> (wit_bindgen::rt::async_support::FutureWriter, wit_bindgen::rt::async_support::FutureReader) { + unsafe { wit_bindgen::rt::async_support::future_new::(default, T::VTABLE) } +} } /// Generates `#[unsafe(no_mangle)]` functions to export the specified type as @@ -225,7 +227,7 @@ macro_rules! __export_future_world_impl { 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")] +#[unsafe(link_section = "component-type:wit-bindgen:0.42.1:test:test:future-world:encoded world")] #[doc(hidden)] #[allow(clippy::octal_escapes)] pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 264] = *b"\ @@ -234,10 +236,11 @@ 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"; +t\x070.235.0\x10wit-bindgen-rust\x060.42.1"; #[inline(never)] #[doc(hidden)] pub fn __link_custom_section_describing_imports() { - wit_bindgen::rt::maybe_link_cabi_realloc(); + 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 index 18ed6147e..ea39b0c90 100644 --- a/crates/cpp/tests/symmetric_future/future/src/lib.rs +++ b/crates/cpp/tests/symmetric_future/future/src/lib.rs @@ -1,7 +1,7 @@ mod future_world; use future_world::test::test::future_source; -use wit_bindgen_symmetric_rt::async_support; +use wit_bindgen::rt::async_support; future_world::export!(MyStruct with_types_in future_world); @@ -9,11 +9,11 @@ struct MyStruct; impl future_world::exports::test::test::future_test::Guest for MyStruct { fn create() -> async_support::FutureReader { - let (write, read) = future_world::wit_future::new(); + let (write, read) = future_world::wit_future::new(u32::default); let input = future_source::create(); async_support::spawn(async move { - let input = input.await.unwrap(); - write.write(input * 2).await; + let input = input.await; + let _ = write.write(input * 2).await; }); read } From 6adf63b9ce4afee8982136a4a3f6cfa798b9b7d2 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 18 Jun 2025 22:38:38 +0200 Subject: [PATCH 645/672] link name --- crates/cpp/tests/symmetric_future/future/src/future_world.rs | 1 + crates/cpp/tests/symmetric_future/generate.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/cpp/tests/symmetric_future/future/src/future_world.rs b/crates/cpp/tests/symmetric_future/future/src/future_world.rs index cd03ac284..bb57b8f66 100644 --- a/crates/cpp/tests/symmetric_future/future/src/future_world.rs +++ b/crates/cpp/tests/symmetric_future/future/src/future_world.rs @@ -17,6 +17,7 @@ pub mod test { pub fn create() -> wit_bindgen::rt::async_support::FutureReader{ unsafe { + #[link(name = "source")] #[link(wasm_import_module = "test:test/future-source")] unsafe extern "C" { #[allow(non_snake_case)] diff --git a/crates/cpp/tests/symmetric_future/generate.sh b/crates/cpp/tests/symmetric_future/generate.sh index b89af3940..240d2090f 100755 --- a/crates/cpp/tests/symmetric_future/generate.sh +++ b/crates/cpp/tests/symmetric_future/generate.sh @@ -1,4 +1,4 @@ #!/bin/sh -(cd future/src ; ../../../../../../target/debug/wit-bindgen rust ../../wit/future.wit --async none --symmetric) +(cd future/src ; ../../../../../../target/debug/wit-bindgen rust ../../wit/future.wit --symmetric --link-name source) (cd future_cpp; ../../../../../target/debug/wit-bindgen cpp ../wit/future.wit --symmetric) cargo fmt From 9068a976681cd6595f3d257e92525de1d1197986 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Wed, 18 Jun 2025 22:46:46 +0200 Subject: [PATCH 646/672] regenerated bindings --- .../future/src/future_world.rs | 381 +++++++++--------- .../future_cpp/future_world.cpp | 4 +- .../future_cpp/future_world_cpp.h | 2 +- 3 files changed, 188 insertions(+), 199 deletions(-) diff --git a/crates/cpp/tests/symmetric_future/future/src/future_world.rs b/crates/cpp/tests/symmetric_future/future/src/future_world.rs index bb57b8f66..96e95baaa 100644 --- a/crates/cpp/tests/symmetric_future/future/src/future_world.rs +++ b/crates/cpp/tests/symmetric_future/future/src/future_world.rs @@ -1,204 +1,190 @@ // Generated by `wit-bindgen` 0.42.1. DO NOT EDIT! // Options used: +#[rustfmt::skip] #[allow(dead_code, clippy::all)] pub mod test { - pub mod test { - - #[allow(dead_code, async_fn_in_trait, 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)] - #[allow(async_fn_in_trait)] - pub fn create() -> wit_bindgen::rt::async_support::FutureReader{ - unsafe { - - #[link(name = "source")] - #[link(wasm_import_module = "test:test/future-source")] - unsafe extern "C" { - #[allow(non_snake_case)] - #[cfg_attr(target_arch = "wasm32", link_name = "create")] - fn testX3AtestX2Ffuture_sourceX00create() -> *mut u8; - } - let ret = testX3AtestX2Ffuture_sourceX00create(); - wit_bindgen::rt::async_support::FutureReader::new(ret, &super::super::super::wit_future::vtable0::VTABLE) + pub mod test { + #[allow(dead_code, async_fn_in_trait, 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)] + #[allow(async_fn_in_trait)] + pub fn create() -> wit_bindgen::rt::async_support::FutureReader { + unsafe { + #[link(name = "source")] + #[link(wasm_import_module = "test:test/future-source")] + unsafe extern "C" { + #[allow(non_snake_case)] + #[cfg_attr(target_arch = "wasm32", link_name = "create")] + fn testX3AtestX2Ffuture_sourceX00create() -> *mut u8; + } + let ret = testX3AtestX2Ffuture_sourceX00create(); + wit_bindgen::rt::async_support::FutureReader::new( + ret, + &super::super::super::wit_future::vtable0::VTABLE, + ) + } + } } - } - } - - } } +#[rustfmt::skip] #[allow(dead_code, clippy::all)] pub mod exports { - pub mod test { pub mod test { - - #[allow(dead_code, async_fn_in_trait, 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, unused_unsafe)] - pub unsafe fn _export_create_cabi() -> *mut u8 { unsafe {#[cfg(target_arch="wasm32")] - _rt::run_ctors_once();let result0 = { - T::create() - }; - (result0).take_handle() as *mut u8 - } } - pub trait Guest { - #[allow(async_fn_in_trait)] - fn create() -> wit_bindgen::rt::async_support::FutureReader; - } - #[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)] - #[allow(non_snake_case)] - unsafe extern "C" fn testX3AtestX2Ffuture_testX00create() -> *mut u8 { - unsafe { $($path_to_types)*::_export_create_cabi::<$ty>() } - } - };); - } - #[doc(hidden)] - pub(crate) use __export_test_test_future_test_cabi; - - } - - } -} + pub mod test { + #[allow(dead_code, async_fn_in_trait, 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, unused_unsafe)] + pub unsafe fn _export_create_cabi() -> *mut u8 { + unsafe { + #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); + let result0 = { T::create() }; + (result0).take_handle() as *mut u8 + } + } + pub trait Guest { + #[allow(async_fn_in_trait)] + fn create() -> wit_bindgen::rt::async_support::FutureReader; + } + #[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)] + #[allow(non_snake_case)] unsafe extern "C" fn + testX3AtestX2Ffuture_testX00create() -> * mut u8 { unsafe { + $($path_to_types)*:: _export_create_cabi::<$ty > () } } }; + }; + } + #[doc(hidden)] + pub(crate) use __export_test_test_future_test_cabi; + } + } + } } +#[rustfmt::skip] mod _rt { - #![allow(dead_code, clippy::all)] - - pub fn as_i32(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")] - pub fn run_ctors_once() { - wit_bindgen::rt::run_ctors_once(); - } + #![allow(dead_code, clippy::all)] + pub fn as_i32(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")] + pub fn run_ctors_once() { + wit_bindgen::rt::run_ctors_once(); + } } pub mod wit_future { - #![allow(dead_code, unused_variables, clippy::all)] - - #[doc(hidden)] - pub trait FuturePayload: Unpin + Sized + 'static { - const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable; - } - #[doc(hidden)] - #[allow(unused_unsafe)] - pub mod vtable0 { - - unsafe fn lift(ptr: *mut u8) -> u32 { unsafe { let l0 = *ptr.add(0).cast::(); - - l0 as u32 } } - unsafe fn lower(value: u32, ptr: *mut u8) { unsafe { *ptr.add(0).cast::() = super::super::_rt::as_i32(value); - } } - unsafe fn dealloc_lists(ptr: *mut u8) { unsafe { } } - - pub static VTABLE: wit_bindgen::rt::async_support::FutureVtable = wit_bindgen::rt::async_support::FutureVtable:: { - - - layout: unsafe { - ::std::alloc::Layout::from_size_align_unchecked(4, 4) - }, - lift, - lower, - - }; - - impl super::FuturePayload for u32 { - const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable = &VTABLE; - } -} -/// Creates a new Component Model `future` with the specified payload type. -/// -/// The `default` function provided computes the default value to be sent in -/// this future if no other value was otherwise sent. -pub fn new(default: fn() -> T) -> (wit_bindgen::rt::async_support::FutureWriter, wit_bindgen::rt::async_support::FutureReader) { - unsafe { wit_bindgen::rt::async_support::future_new::(default, T::VTABLE) } -} + #![allow(dead_code, unused_variables, clippy::all)] + #[doc(hidden)] + pub trait FuturePayload: Unpin + Sized + 'static { + const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable; + } + #[doc(hidden)] + #[allow(unused_unsafe)] + pub mod vtable0 { + unsafe fn lift(ptr: *mut u8) -> u32 { + unsafe { + let l0 = *ptr.add(0).cast::(); + l0 as u32 + } + } + unsafe fn lower(value: u32, ptr: *mut u8) { + unsafe { + *ptr.add(0).cast::() = super::super::_rt::as_i32(value); + } + } + unsafe fn dealloc_lists(ptr: *mut u8) { + unsafe {} + } + pub static VTABLE: wit_bindgen::rt::async_support::FutureVtable = wit_bindgen::rt::async_support::FutureVtable::< + u32, + > { + layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(4, 4) }, + lift, + lower, + }; + impl super::FuturePayload for u32 { + const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable = &VTABLE; + } + } + /// Creates a new Component Model `future` with the specified payload type. + /// + /// The `default` function provided computes the default value to be sent in + /// this future if no other value was otherwise sent. + pub fn new( + default: fn() -> T, + ) -> ( + wit_bindgen::rt::async_support::FutureWriter, + wit_bindgen::rt::async_support::FutureReader, + ) { + unsafe { wit_bindgen::rt::async_support::future_new::(default, T::VTABLE) } + } } - /// Generates `#[unsafe(no_mangle)]` functions to export the specified type as /// the root implementation of all generated traits. /// @@ -217,18 +203,23 @@ pub fn new(default: fn() -> T) -> (wit_bindgen::rt::async_supp /// ``` #[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); - ) + ($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; - +#[rustfmt::skip] #[cfg(target_arch = "wasm32")] -#[unsafe(link_section = "component-type:wit-bindgen:0.42.1:test:test:future-world:encoded world")] +#[unsafe( + link_section = "component-type:wit-bindgen:0.42.1:test:test:future-world:encoded world" +)] #[doc(hidden)] #[allow(clippy::octal_escapes)] pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 264] = *b"\ @@ -238,10 +229,8 @@ uture-source\x05\0\x01B\x03\x01e\x01y\x01@\0\0\0\x04\0\x06create\x01\x01\x04\0\x 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.235.0\x10wit-bindgen-rust\x060.42.1"; - #[inline(never)] #[doc(hidden)] pub fn __link_custom_section_describing_imports() { - wit_bindgen::rt::maybe_link_cabi_realloc(); + wit_bindgen::rt::maybe_link_cabi_realloc(); } - diff --git a/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp b/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp index bce9a0088..ded808cdd 100644 --- a/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp +++ b/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp @@ -1,4 +1,4 @@ -// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.42.1. DO NOT EDIT! // Ensure that the *_component_type.o object is linked in #ifdef __wasm32__ @@ -12,7 +12,7 @@ void __component_type_object_force_link_future_world_public_use_in_this_compilat extern "C" void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size); -__attribute__((__weak__, __export_name__("cabi_realloc"))) +__attribute__((__weak__)) 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; diff --git a/crates/cpp/tests/symmetric_future/future_cpp/future_world_cpp.h b/crates/cpp/tests/symmetric_future/future_cpp/future_world_cpp.h index b0e1b8517..14c211dc7 100644 --- a/crates/cpp/tests/symmetric_future/future_cpp/future_world_cpp.h +++ b/crates/cpp/tests/symmetric_future/future_cpp/future_world_cpp.h @@ -1,4 +1,4 @@ -// Generated by `wit-bindgen` 0.3.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.42.1. DO NOT EDIT! #ifndef __CPP_GUEST_BINDINGS_FUTURE_WORLD_H #define __CPP_GUEST_BINDINGS_FUTURE_WORLD_H #define WIT_SYMMETRIC From 840faaf3b71e79af76f27b07a375cf675d527a31 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 19 Jun 2025 02:36:57 +0200 Subject: [PATCH 647/672] linking c++ future test --- crates/cpp/src/lib.rs | 69 +++++++++++++++++-- crates/cpp/tests/symmetric_future/generate.sh | 2 +- .../cpp-client/async_support.h | 8 +-- crates/test/src/cpp.rs | 15 ++-- 4 files changed, 81 insertions(+), 13 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 5b49d2941..c0ce48e9c 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -124,6 +124,8 @@ struct Cpp { // needed for symmetric disambiguation interface_prefixes: HashMap<(Direction, WorldKey), String>, import_prefix: Option, + + temp: usize, } #[derive(Default, Debug, Clone, Copy)] @@ -478,6 +480,11 @@ impl Cpp { self.includes.push(String::from("\"") + &filename + "\""); } } + + fn tmp(&mut self) -> usize { + self.temp += 1; + self.temp + } } #[derive(Default)] @@ -2675,6 +2682,56 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { TypeDefKind::Unknown => todo!(), } } + + fn lower_lift(&mut self, payload: Option<&Type>) -> String { + let typestr = self + .gen + .optional_type_name(payload, &self.namespace, Flavor::InStruct); + let tmpnr = self.r#gen.r#gen.tmp(); + uwriteln!(self.r#gen.r#gen.c_src_head, "struct Lift{tmpnr} {{"); + let mut bindgen = FunctionBindgen::new(self.gen, Vec::new()); + let lift = if let Some(ty) = payload { + let res = wit_bindgen_core::abi::lift_from_memory( + bindgen.r#gen.resolve, + &mut bindgen, + "ptr".into(), + ty, + ); + format!("{} return {res};", String::from(bindgen.src)) + } else { + String::new() + }; + let mut bindgen = FunctionBindgen::new(self.gen, Vec::new()); + let lower = if let Some(ty) = payload { + wit_bindgen_core::abi::lower_to_memory( + bindgen.r#gen.resolve, + &mut bindgen, + "ptr".into(), + "value".into(), + ty, + ); + String::from(bindgen.src) + } else { + String::new() + }; + let lowered_size = if let Some(ty) = payload { + self.r#gen + .sizes + .size(ty) + .format_term(POINTER_SIZE_EXPRESSION, true) + } else { + String::from("1") + }; + + uwriteln!( + self.r#gen.r#gen.c_src_head, + "static {typestr} lift(uint8_t const* ptr) {{ {lift} }} + static void lower({typestr} && value, uint8_t *ptr) {{ {lower} }} + static constexpr size_t SIZE = {lowered_size};" + ); + uwriteln!(self.r#gen.r#gen.c_src_head, "}};"); + format!("Lift{tmpnr}") + } } fn move_if_necessary(arg: &str) -> String { @@ -3796,8 +3853,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { } abi::Instruction::LengthStore { offset } => self.store("size_t", *offset, operands), abi::Instruction::FutureLower { payload, .. } => { + let lower_lift = self.lower_lift(payload.as_ref()); results.push(format!( - "lower_future<{}>(std::move({}))", + "lower_future<{}, {lower_lift}>(std::move({}))", self.gen.optional_type_name( payload.as_ref(), &self.namespace, @@ -3807,8 +3865,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { )); } abi::Instruction::FutureLift { payload, .. } => { + let lower_lift = self.lower_lift(payload.as_ref()); results.push(format!( - "lift_future<{}>({})", + "lift_future<{}, {lower_lift}>({})", self.gen.optional_type_name( payload.as_ref(), &self.namespace, @@ -3818,8 +3877,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { )); } abi::Instruction::StreamLower { payload, .. } => { + let lower_lift = self.lower_lift(payload.as_ref()); results.push(format!( - "lower_stream<{}>(std::move({}))", + "lower_stream<{}, {lower_lift}>(std::move({}))", self.gen.optional_type_name( payload.as_ref(), &self.namespace, @@ -3829,8 +3889,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { )); } abi::Instruction::StreamLift { payload, .. } => { + let lower_lift = self.lower_lift(payload.as_ref()); results.push(format!( - "lift_stream<{}>({})", + "lift_stream<{}, {lower_lift}>({})", self.gen.optional_type_name( payload.as_ref(), &self.namespace, diff --git a/crates/cpp/tests/symmetric_future/generate.sh b/crates/cpp/tests/symmetric_future/generate.sh index 240d2090f..5d7ac9a5a 100755 --- a/crates/cpp/tests/symmetric_future/generate.sh +++ b/crates/cpp/tests/symmetric_future/generate.sh @@ -1,4 +1,4 @@ #!/bin/sh -(cd future/src ; ../../../../../../target/debug/wit-bindgen rust ../../wit/future.wit --symmetric --link-name source) +(cd future/src ; ../../../../../../target/debug/wit-bindgen rust ../../wit/future.wit --symmetric --link-name source --format) (cd future_cpp; ../../../../../target/debug/wit-bindgen cpp ../wit/future.wit --symmetric) cargo fmt diff --git a/crates/symmetric_executor/cpp-client/async_support.h b/crates/symmetric_executor/cpp-client/async_support.h index 97c2ab4a6..9f1eb417e 100644 --- a/crates/symmetric_executor/cpp-client/async_support.h +++ b/crates/symmetric_executor/cpp-client/async_support.h @@ -3,13 +3,13 @@ #include "module_cpp.h" #include "stream_support.h" -static symmetric::runtime::symmetric_executor::CallbackState fulfil_promise_void(void* data) { +static inline symmetric::runtime::symmetric_executor::CallbackState fulfil_promise_void(void* data) { std::unique_ptr> ptr((std::promise*)data); ptr->set_value(); return symmetric::runtime::symmetric_executor::CallbackState::kReady; } -static std::future lift_event(void* event) { +static inline std::future lift_event(void* event) { std::promise result; std::future result1 = result.get_future(); if (!event) { @@ -24,7 +24,7 @@ static std::future lift_event(void* event) { return result1; } -static symmetric::runtime::symmetric_executor::CallbackState wait_on_future(std::future* fut) { +static inline symmetric::runtime::symmetric_executor::CallbackState wait_on_future(std::future* fut) { fut->get(); delete fut; return symmetric::runtime::symmetric_executor::CallbackState::kReady; @@ -90,7 +90,7 @@ std::future lift_future(uint8_t* stream) { std::future result= promise.get_future(); auto stream2 = symmetric::runtime::symmetric_stream::StreamObj(wit::ResourceImportBase(stream)); auto event = stream2.ReadReadySubscribe(); - std::unique_ptr> data = std::make_unique>(fulfil_promise_data{std::move(stream2), std::move(promise)}); + std::unique_ptr> data = std::make_unique>(fulfil_promise_data{std::move(stream2), std::move(promise), {0}}); symmetric::runtime::symmetric_stream::Buffer buf = symmetric::runtime::symmetric_stream::Buffer( symmetric::runtime::symmetric_stream::Address(wit::ResourceImportBase((wit::ResourceImportBase::handle_t)&data->value)), 1 diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index ccb71dcc3..df93cf4e9 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -154,7 +154,9 @@ impl LanguageMethods for Cpp17 { .arg("-I") .arg(helper_dir2.to_str().unwrap().to_string()); if runner.is_symmetric() { - cmd.arg("-I").arg(helper_dir3.to_str().unwrap().to_string()); + cmd.arg("-I") + .arg(helper_dir3.to_str().unwrap().to_string()) + .arg("-fPIC"); } cmd.arg("-fno-exceptions") .arg("-Wall") @@ -211,14 +213,19 @@ impl LanguageMethods for Cpp17 { } if runner.is_symmetric() { cmd.arg("-fPIC"); + for i in runner.cpp_state.as_ref().unwrap().native_deps.iter() { + cmd.arg(format!("-L{}", i.as_os_str().to_str().unwrap())); + } if !matches!(compile.component.kind, Kind::Runner) { cmd.arg("-shared"); } else { - for i in runner.cpp_state.as_ref().unwrap().native_deps.iter() { - cmd.arg(format!("-L{}", i.as_os_str().to_str().unwrap())); - } cmd.arg("-ltest-cpp17"); } + cmd.arg("-L") + .arg(helper_dir3.to_str().unwrap().to_string()) + .arg("-lruntime") + .arg("-lsymmetric_stream") + .arg("-lsymmetric_executor"); } runner.run_command(&mut cmd)?; Ok(()) From ea7de9a5481e67139e9c1d4cbbdaf9c1920cab0c Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 19 Jun 2025 12:15:34 +0200 Subject: [PATCH 648/672] optionally output the commands (WIT_BINDGEN_TRACE), hide C++ symbol chaos --- crates/cpp/src/lib.rs | 7 +++++++ crates/test/src/cpp.rs | 9 ++++++++- crates/test/src/lib.rs | 3 +++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index c0ce48e9c..7ecdc6769 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -850,6 +850,13 @@ impl WorldGenerator for Cpp { .unwrap() .as_slice(), ); + if self.opts.symmetric { + // this keeps the symbols down for shared objects, could be more specific + files.push( + &format!("{}.verscr", world.name), + b"{\n global:\n *X00*;\n local: *;\n};\n", + ); + } Ok(()) } diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index df93cf4e9..08e90720b 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -212,7 +212,14 @@ impl LanguageMethods for Cpp17 { } } if runner.is_symmetric() { - cmd.arg("-fPIC"); + cmd.arg("-fPIC").arg(format!( + "-Wl,--version-script={}", + compile + .bindings_dir + .join(format!("{}.verscr", compile.component.bindgen.world)) + .to_str() + .unwrap() // .to_string(), + )); for i in runner.cpp_state.as_ref().unwrap().native_deps.iter() { cmd.arg(format!("-L{}", i.as_os_str().to_str().unwrap())); } diff --git a/crates/test/src/lib.rs b/crates/test/src/lib.rs index 286570f09..e3541e419 100644 --- a/crates/test/src/lib.rs +++ b/crates/test/src/lib.rs @@ -1047,6 +1047,9 @@ impl Runner<'_> { .output() .with_context(|| format!("failed to spawn {cmd:?}"))?; if output.status.success() { + if std::env::var("WIT_BINDGEN_TRACE").is_ok() { + eprintln!("$ {cmd:?}"); + } return Ok(()); } From 688ed17a4b85bc98b0e68ae29a6a541a0908a006 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 19 Jun 2025 12:42:51 +0200 Subject: [PATCH 649/672] implement short cut at registration --- .../symmetric_executor/rust-client/Cargo.toml | 3 --- crates/symmetric_executor/src/lib.rs | 18 ++++++++++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/crates/symmetric_executor/rust-client/Cargo.toml b/crates/symmetric_executor/rust-client/Cargo.toml index 654daa122..100cd4d61 100644 --- a/crates/symmetric_executor/rust-client/Cargo.toml +++ b/crates/symmetric_executor/rust-client/Cargo.toml @@ -1,5 +1,3 @@ -#[workspace] - [package] name = "wit-bindgen-symmetric-rt" version = "0.36.0" @@ -8,7 +6,6 @@ edition = "2021" [dependencies] bitflags = "2.9.1" futures = "0.3.31" -#wit-bindgen = { path = "../../guest-rust" } [dependencies.wit-bindgen] package = "dummy-rt" diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index b6280b723..b6c1aa1df 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -300,6 +300,20 @@ impl symmetric_executor::Guest for Guest { let cb: CallbackType = unsafe { transmute(callback.take_handle()) }; let data = data.take_handle() as *mut OpaqueData; + // try to take a short cut + let trigger: EventSubscriptionInternal = trigger.into_inner(); + if trigger.inner.ready() { + if DEBUGGING { + println!("register ready event {:x} {:x}", cb as usize, data as usize); + } + if matches!((cb)(data), CallbackState::Ready) { + println!("registration unnecessary"); + return symmetric_executor::CallbackRegistration::new( + CallbackRegistrationInternal(0), + ); + } + } + let subscr = QueuedEvent::new(trigger, CallbackEntry(cb, data)); let id = subscr.id; match EXECUTOR.try_lock() { @@ -363,9 +377,9 @@ struct QueuedEvent { static ID_SOURCE: AtomicUsize = AtomicUsize::new(0); impl QueuedEvent { - fn new(event: symmetric_executor::EventSubscription, callback: CallbackEntry) -> Self { + fn new(trigger: EventSubscriptionInternal, callback: CallbackEntry) -> Self { let id = ID_SOURCE.fetch_add(1, Ordering::Relaxed); - let trigger: EventSubscriptionInternal = event.into_inner(); + // let trigger: EventSubscriptionInternal = event.into_inner(); let inner = trigger.inner; let event_fd = match &inner { EventType::Triggered { From 029effdcb9a742cfb44ae102cc6e3807a064d511 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 19 Jun 2025 16:26:47 +0200 Subject: [PATCH 650/672] simplify executor code --- crates/symmetric_executor/src/lib.rs | 382 +++++++++++++++++---------- 1 file changed, 243 insertions(+), 139 deletions(-) diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index b6c1aa1df..f52ddee1a 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -1,5 +1,4 @@ use std::{ - ffi::c_int, mem::transmute, sync::{ atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}, @@ -25,6 +24,158 @@ struct Ignore; struct OpaqueData; impl symmetric_executor::GuestCallbackFunction for Ignore {} impl symmetric_executor::GuestCallbackData for OpaqueData {} + +// Hide the specifics of eventfd +mod event_fd { + pub type EventFd = core::ffi::c_int; + + pub fn activate(fd: EventFd) { + let file_signal: u64 = 1; + if super::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() + ); + } + } + pub fn consume(fd: EventFd) { + let mut dummy: u64 = 0; + + let readresult = unsafe { + libc::read( + fd, + 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() + ); + } +} + +use event_fd::EventFd; + +struct WaitSet { + wait: libc::timeval, + // non null if timeval is finite (timeout) + tvptr: *mut libc::timeval, + maxfd: EventFd, + rfds: core::mem::MaybeUninit, +} + +struct WaitSetIterator<'a> { + ws: &'a WaitSet, + fd: EventFd, +} + +impl<'a> Iterator for WaitSetIterator<'a> { + type Item = EventFd; + + fn next(&mut self) -> Option { + let rfd_ptr = self.ws.rfds.as_ptr(); + while self.fd < self.ws.maxfd { + let fd = self.fd; + self.fd += 1; + if unsafe { libc::FD_ISSET(fd, rfd_ptr) } { + return Some(fd); + } + } + None + } +} + +impl WaitSet { + fn new(change_event: Option) -> Self { + let wait = libc::timeval { + tv_sec: i64::MAX, + tv_usec: 999999, + }; + let tvptr = core::ptr::null_mut(); + let maxfd = change_event.map_or(0, |fd| fd + 1); + //let now = SystemTime::now(); + let mut rfds = core::mem::MaybeUninit::::uninit(); + let rfd_ptr = rfds.as_mut_ptr(); + unsafe { libc::FD_ZERO(rfd_ptr) }; + if let Some(fd) = change_event { + unsafe { + libc::FD_SET(fd, rfd_ptr); + } + } + Self { + wait, + tvptr, + maxfd, + rfds, + } + } + + fn register(&mut self, fd: EventFd) { + let rfd_ptr = self.rfds.as_mut_ptr(); + unsafe { libc::FD_SET(fd, rfd_ptr) }; + if fd >= self.maxfd { + self.maxfd = fd + 1; + } + } + + fn timeout(&mut self, diff: Duration) { + let secs = diff.as_secs() as i64; + let usecs = diff.subsec_micros() as i64; + if secs < self.wait.tv_sec || (secs == self.wait.tv_sec && usecs < self.wait.tv_usec) { + self.wait.tv_sec = secs; + self.wait.tv_usec = usecs; + } + self.tvptr = core::ptr::from_mut(&mut self.wait); + } + + fn debug(&self) { + let rfd_ptr = self.rfds.as_ptr(); + if self.tvptr.is_null() { + println!("select({}, {:x}, null)", self.maxfd, unsafe { + *rfd_ptr.cast::() + },); + } else { + println!( + "select({}, {:x}, {}.{})", + self.maxfd, + unsafe { *rfd_ptr.cast::() }, + self.wait.tv_sec, + self.wait.tv_usec + ); + } + } + + // see select for the return value + fn wait(&mut self) -> i32 { + let rfd_ptr = self.rfds.as_mut_ptr(); + unsafe { + libc::select( + self.maxfd, + rfd_ptr, + std::ptr::null_mut(), + std::ptr::null_mut(), + self.tvptr, + ) + } + } + + fn iter_active(&self) -> WaitSetIterator<'_> { + WaitSetIterator { ws: self, fd: 0 } + } +} + #[allow(dead_code)] struct CallbackRegistrationInternal(usize); @@ -87,24 +238,8 @@ impl symmetric_executor::GuestEventGenerator for EventGenerator { 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() - ); - } + event_fd::activate(*fd); }); } else if DEBUGGING { println!("activate failure"); @@ -133,6 +268,53 @@ impl Executor { fd }) } + + /// run the executor until it would block, returns number of handled events + fn tick(ex: &mut std::sync::MutexGuard<'_, Self>, ws: &mut WaitSet) -> (usize, usize) { + let mut count_events = 0; + let mut count_waiting = 0; + let now = SystemTime::now(); + 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 + ); + } + count_events += 1; + task.callback + .take_if(|CallbackEntry(f, data)| matches!((f)(*data), CallbackState::Ready)); + } else { + count_waiting += 1; + match &task.inner { + EventType::Triggered { + last_counter: _, + event: _, + } => { + ws.register(task.event_fd); + } + EventType::SystemTime(system_time) => { + if *system_time > now { + let diff = system_time.duration_since(now).unwrap_or_default(); + ws.timeout(diff); + } 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()); + (count_events, count_waiting) + } } static EXECUTOR: Mutex = Mutex::new(Executor { @@ -153,70 +335,23 @@ impl symmetric_executor::Guest for Guest { fn run() { let change_event = EXECUTOR.lock().unwrap().change_event(); 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::::uninit(); - let rfd_ptr = rfds.as_mut_ptr(); - unsafe { libc::FD_ZERO(rfd_ptr) }; - unsafe { - libc::FD_SET(change_event, rfd_ptr); - } - { + let mut ws = WaitSet::new(Some(change_event)); + // 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::::uninit(); + // let rfd_ptr = rfds.as_mut_ptr(); + // unsafe { libc::FD_ZERO(rfd_ptr) }; + // unsafe { + // libc::FD_SET(change_event, rfd_ptr); + // } + let count_waiting = { 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 (_, count_waiting) = Executor::tick(&mut ex, &mut ws); { let mut new_tasks = NEW_TASKS.lock().unwrap(); if !new_tasks.is_empty() { @@ -228,10 +363,12 @@ impl symmetric_executor::Guest for Guest { 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) }) + count_waiting + }; + if count_waiting == 0 + // 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 { @@ -245,47 +382,14 @@ impl symmetric_executor::Guest for Guest { // 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::() - },); - } else { - println!( - "select({maxfd}, {:x}, {}.{})", - unsafe { *rfd_ptr.cast::() }, - wait.tv_sec, - wait.tv_usec - ); - } + ws.debug(); } - let selectresult = unsafe { - libc::select( - maxfd, - rfd_ptr, - std::ptr::null_mut(), - std::ptr::null_mut(), - tvptr, - ) - }; + let selectresult = ws.wait(); // 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() - ); - } + for i in ws.iter_active() { + event_fd::consume(i); } } } @@ -319,21 +423,22 @@ impl symmetric_executor::Guest for Guest { match EXECUTOR.try_lock() { Ok(mut lock) => { lock.active_tasks.push(subscr); - let file_signal: u64 = 1; - let fd = lock.change_event(); - 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() - ); - } + // let file_signal: u64 = 1; + // let fd = ; + event_fd::activate(lock.change_event()); + // 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) { @@ -348,7 +453,6 @@ impl symmetric_executor::Guest for Guest { } } -type EventFd = c_int; type Count = u32; struct EventInner { @@ -374,7 +478,7 @@ struct QueuedEvent { callback: Option, } -static ID_SOURCE: AtomicUsize = AtomicUsize::new(0); +static ID_SOURCE: AtomicUsize = AtomicUsize::new(1); impl QueuedEvent { fn new(trigger: EventSubscriptionInternal, callback: CallbackEntry) -> Self { From 2d0edd33d497db04d60da6c2888af8cb4ba730a3 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 19 Jun 2025 16:47:03 +0200 Subject: [PATCH 651/672] cleanup + direct progress on subscription --- crates/cpp/tests/symmetric_stream/Cargo.lock | 7 +++ crates/symmetric_executor/src/lib.rs | 56 ++++++-------------- 2 files changed, 22 insertions(+), 41 deletions(-) diff --git a/crates/cpp/tests/symmetric_stream/Cargo.lock b/crates/cpp/tests/symmetric_stream/Cargo.lock index f8e00cd67..6a70f3485 100644 --- a/crates/cpp/tests/symmetric_stream/Cargo.lock +++ b/crates/cpp/tests/symmetric_stream/Cargo.lock @@ -8,6 +8,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + [[package]] name = "dummy-rt" version = "0.1.0" @@ -221,6 +227,7 @@ checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" name = "wit-bindgen-symmetric-rt" version = "0.36.0" dependencies = [ + "bitflags", "dummy-rt", "futures", ] diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index f52ddee1a..99a80b7a7 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -105,7 +105,6 @@ impl WaitSet { }; let tvptr = core::ptr::null_mut(); let maxfd = change_event.map_or(0, |fd| fd + 1); - //let now = SystemTime::now(); let mut rfds = core::mem::MaybeUninit::::uninit(); let rfd_ptr = rfds.as_mut_ptr(); unsafe { libc::FD_ZERO(rfd_ptr) }; @@ -336,22 +335,9 @@ impl symmetric_executor::Guest for Guest { let change_event = EXECUTOR.lock().unwrap().change_event(); loop { let mut ws = WaitSet::new(Some(change_event)); - // 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::::uninit(); - // let rfd_ptr = rfds.as_mut_ptr(); - // unsafe { libc::FD_ZERO(rfd_ptr) }; - // unsafe { - // libc::FD_SET(change_event, rfd_ptr); - // } - let count_waiting = { + let (count_events, count_waiting) = { let mut ex = EXECUTOR.lock().unwrap(); - let (_, count_waiting) = Executor::tick(&mut ex, &mut ws); + let (count_events, count_waiting) = Executor::tick(&mut ex, &mut ws); { let mut new_tasks = NEW_TASKS.lock().unwrap(); if !new_tasks.is_empty() { @@ -363,17 +349,13 @@ impl symmetric_executor::Guest for Guest { if ex.active_tasks.is_empty() { break; } - count_waiting + (count_events, count_waiting) }; - if count_waiting == 0 - // 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 count_events != 0 { + // we processed events, perhaps more became ready if DEBUGGING { println!( - "Relooping with {} tasks", + "Relooping with {} tasks after {count_events} events, {count_waiting} waiting", EXECUTOR.lock().unwrap().active_tasks.len() ); } @@ -400,7 +382,6 @@ impl symmetric_executor::Guest for Guest { callback: symmetric_executor::CallbackFunction, data: symmetric_executor::CallbackData, ) -> symmetric_executor::CallbackRegistration { - // let trigger: EventSubscriptionInternal = trigger.into_inner(); let cb: CallbackType = unsafe { transmute(callback.take_handle()) }; let data = data.take_handle() as *mut OpaqueData; @@ -423,22 +404,16 @@ impl symmetric_executor::Guest for Guest { match EXECUTOR.try_lock() { Ok(mut lock) => { lock.active_tasks.push(subscr); - // let file_signal: u64 = 1; - // let fd = ; + let mut ws = WaitSet::new(None); + // process as long as there is immediate progress + loop { + let (count_events, _count_waiting) = Executor::tick(&mut lock, &mut ws); + if count_events == 0 { + break; + } + } + // wake other threads last event_fd::activate(lock.change_event()); - // 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) { @@ -483,7 +458,6 @@ static ID_SOURCE: AtomicUsize = AtomicUsize::new(1); impl QueuedEvent { fn new(trigger: EventSubscriptionInternal, callback: CallbackEntry) -> Self { let id = ID_SOURCE.fetch_add(1, Ordering::Relaxed); - // let trigger: EventSubscriptionInternal = event.into_inner(); let inner = trigger.inner; let event_fd = match &inner { EventType::Triggered { From 7556398ea5d8eac3e6593a4c184fc22b1a207196 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 19 Jun 2025 17:05:18 +0200 Subject: [PATCH 652/672] small but important correction --- crates/symmetric_executor/cpp-client/async_support.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/symmetric_executor/cpp-client/async_support.h b/crates/symmetric_executor/cpp-client/async_support.h index 9f1eb417e..6d83a4f59 100644 --- a/crates/symmetric_executor/cpp-client/async_support.h +++ b/crates/symmetric_executor/cpp-client/async_support.h @@ -178,7 +178,7 @@ static symmetric::runtime::symmetric_executor::CallbackState write_to_future(voi // is future ready? if (ptr->fut.wait_for(std::chrono::seconds::zero()) == std::future_status::ready) { auto buffer = ptr->wr.handle.StartWriting(); - assert(buffer.GetSize()==1); //sizeof(T)); + assert(buffer.Capacity()==1); uint8_t* dataptr = (uint8_t*)(buffer.GetAddress().into_handle()); auto result = ptr->fut.get(); LOWER::lower(std::move(result), dataptr); From 0f7fc221f87280d16cd4b22eb8ab6461bd260fe0 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 19 Jun 2025 21:30:19 +0200 Subject: [PATCH 653/672] c++ future string works --- crates/cpp/src/lib.rs | 2 ++ crates/test/src/cpp.rs | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 7ecdc6769..3b8b20b41 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -2709,6 +2709,8 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { String::new() }; let mut bindgen = FunctionBindgen::new(self.gen, Vec::new()); + // GuestImport doesn't leak the objects (string) + bindgen.variant = AbiVariant::GuestExport; let lower = if let Some(ty) = payload { wit_bindgen_core::abi::lower_to_memory( bindgen.r#gen.resolve, diff --git a/crates/test/src/cpp.rs b/crates/test/src/cpp.rs index 08e90720b..6dad80498 100644 --- a/crates/test/src/cpp.rs +++ b/crates/test/src/cpp.rs @@ -258,4 +258,8 @@ impl LanguageMethods for Cpp17 { .arg(verify.artifacts_dir.join("tmp.o")); runner.run_command(&mut cmd) } + + fn default_bindgen_args(&self) -> &[&str] { + &["--format"] + } } From 95cd7d833cb834fc6e64bf3537af258ba14ecd25 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Thu, 19 Jun 2025 21:45:07 +0200 Subject: [PATCH 654/672] correct payload size --- crates/rust/src/interface.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/rust/src/interface.rs b/crates/rust/src/interface.rs index 1ad278e5c..595353b00 100644 --- a/crates/rust/src/interface.rs +++ b/crates/rust/src/interface.rs @@ -636,8 +636,6 @@ macro_rules! {macro_name} {{ Alignment::default(), ) }; - let size = size.size_wasm32(); - let align = align.align_wasm32(); let lift; let lower; let dealloc_lists; @@ -775,6 +773,8 @@ pub mod vtable{ordinal} {{ }} }} "#, + size = size.format_term(POINTER_SIZE_EXPRESSION, true), + align = align.format(POINTER_SIZE_EXPRESSION), )); let map = match payload_for { From 21a8f36a4b1e80dba0fec682bea69e51184a5c24 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Fri, 20 Jun 2025 12:21:00 +0200 Subject: [PATCH 655/672] fix publishing logic --- Cargo.lock | 4 ++-- ci/publish.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d432d3491..206eeb494 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1256,8 +1256,8 @@ dependencies = [ "clap", "heck 0.5.0", "test-helpers", - "wasm-encoder 0.232.0", - "wasm-metadata 0.232.0", + "wasm-encoder 0.234.0", + "wasm-metadata 0.234.0", "wit-bindgen-c", "wit-bindgen-core", "wit-component", diff --git a/ci/publish.rs b/ci/publish.rs index 84a6ebce4..274a939f2 100644 --- a/ci/publish.rs +++ b/ci/publish.rs @@ -19,6 +19,7 @@ use std::time::Duration; const CRATES_TO_PUBLISH: &[&str] = &[ "wit-bindgen-core", "wit-bindgen-c", + "wit-bindgen-cpp", "wit-bindgen-rust", "wit-bindgen-csharp", "wit-bindgen-markdown", From 98b43e3cc87e8baae9e7e114372d47956c619c8b Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 21 Jun 2025 14:44:48 +0200 Subject: [PATCH 656/672] attempt to use waitable --- .../rt/src/async_support/future_support.rs | 2 + .../rt/src/async_support/stream_support.rs | 2 + .../rt/src/async_support/subtask.rs | 1 + .../rt/src/async_support/waitable.rs | 16 +- .../rust-client/src/async_support.rs | 5 +- .../src/async_support/rust_buffer.rs | 22 ++ .../src/async_support/stream_support.rs | 203 +++++++++++++++--- 7 files changed, 210 insertions(+), 41 deletions(-) create mode 100644 crates/symmetric_executor/rust-client/src/async_support/rust_buffer.rs diff --git a/crates/guest-rust/rt/src/async_support/future_support.rs b/crates/guest-rust/rt/src/async_support/future_support.rs index da39d1296..0e241aa98 100644 --- a/crates/guest-rust/rt/src/async_support/future_support.rs +++ b/crates/guest-rust/rt/src/async_support/future_support.rs @@ -338,6 +338,7 @@ where type InProgress = (FutureWriter, Option); type Result = (WriteComplete, FutureWriter); type Cancel = FutureWriteCancel; + type Handle = u32; fn start((writer, value): Self::Start) -> (u32, Self::InProgress) { // TODO: it should be safe to store the lower-destination in @@ -621,6 +622,7 @@ where type InProgress = (FutureReader, Option); type Result = (ReadComplete, FutureReader); type Cancel = Result>; + type Handle = u32; fn start(reader: Self::Start) -> (u32, Self::InProgress) { let (ptr, cleanup) = Cleanup::new(reader.vtable.layout); diff --git a/crates/guest-rust/rt/src/async_support/stream_support.rs b/crates/guest-rust/rt/src/async_support/stream_support.rs index fd4d0d5f6..389c878bd 100644 --- a/crates/guest-rust/rt/src/async_support/stream_support.rs +++ b/crates/guest-rust/rt/src/async_support/stream_support.rs @@ -242,6 +242,7 @@ where type InProgress = (&'a mut StreamWriter, AbiBuffer); type Result = (StreamResult, AbiBuffer); type Cancel = (StreamResult, AbiBuffer); + type Handle = u32; fn start((writer, buf): Self::Start) -> (u32, Self::InProgress) { if writer.done { @@ -458,6 +459,7 @@ where type InProgress = (&'a mut StreamReader, Vec, Option); type Result = (StreamResult, Vec); type Cancel = (StreamResult, Vec); + type Handle = u32; fn start((reader, mut buf): Self::Start) -> (u32, Self::InProgress) { if reader.done { diff --git a/crates/guest-rust/rt/src/async_support/subtask.rs b/crates/guest-rust/rt/src/async_support/subtask.rs index 79804783d..c490c5666 100644 --- a/crates/guest-rust/rt/src/async_support/subtask.rs +++ b/crates/guest-rust/rt/src/async_support/subtask.rs @@ -102,6 +102,7 @@ unsafe impl WaitableOp for SubtaskOps { type InProgress = InProgress; type Result = Result; type Cancel = Result; + type Handle = u32; fn start(state: Self::Start) -> (u32, Self::InProgress) { unsafe { diff --git a/crates/guest-rust/rt/src/async_support/waitable.rs b/crates/guest-rust/rt/src/async_support/waitable.rs index d64aeec6a..52331ad41 100644 --- a/crates/guest-rust/rt/src/async_support/waitable.rs +++ b/crates/guest-rust/rt/src/async_support/waitable.rs @@ -9,6 +9,11 @@ use std::task::{Context, Poll, Waker}; #[cfg(not(feature = "symmetric"))] use {super::cabi, std::ffi::c_void, std::ptr}; +#[cfg(not(feature = "symmetric"))] +pub type Handle = u32; +#[cfg(feature = "symmetric")] +pub type Handle = *mut u8; + /// Generic future-based operation on any "waitable" in the component model. /// /// This is used right now to power futures and streams for both read/write @@ -77,6 +82,9 @@ pub unsafe trait WaitableOp { /// Result of when this operation is cancelled. type Cancel; + /// Waitable type, u32 for wasm + type Handle; + /// Starts the async operation. /// /// This method will actually call `{future,stream}.{read,write}` with @@ -103,7 +111,7 @@ pub unsafe trait WaitableOp { /// Acquires the component-model `waitable` index that the `InProgress` /// state is waiting on. - fn in_progress_waitable(state: &Self::InProgress) -> u32; + fn in_progress_waitable(state: &Self::InProgress) -> Handle; /// Initiates a request for cancellation of this operation. Returns the /// status code returned by the `{future,stream}.cancel-{read,write}` @@ -163,7 +171,7 @@ where /// * Fill in `completion_status` with the result of a completion event. /// * Call `cx.waker().wake()`. #[cfg(not(feature = "symmetric"))] - pub fn register_waker(self: Pin<&mut Self>, waitable: u32, cx: &mut Context) { + pub fn register_waker(self: Pin<&mut Self>, waitable: Handle, cx: &mut Context) { let (_, mut completion_status) = self.pin_project(); debug_assert!(completion_status.as_mut().code_mut().is_none()); *completion_status.as_mut().waker_mut() = Some(cx.waker().clone()); @@ -200,11 +208,11 @@ where } #[cfg(feature = "symmetric")] - pub fn register_waker(self: Pin<&mut Self>, _waitable: u32, _cx: &mut Context) { + pub fn register_waker(self: Pin<&mut Self>, _waitable: Handle, _cx: &mut Context) { todo!() } #[cfg(feature = "symmetric")] - pub fn unregister_waker(self: Pin<&mut Self>, _waitable: u32) { + pub fn unregister_waker(self: Pin<&mut Self>, _waitable: Handle) { todo!() } diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 0ebd0fff9..11c06e11f 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -17,9 +17,10 @@ pub use stream_support::{ }; pub use subtask::Subtask; -#[path = "../../../guest-rust/rt/src/async_support/abi_buffer.rs"] -pub mod abi_buffer; +// #[path = "../../../guest-rust/rt/src/async_support/abi_buffer.rs"] +// pub mod abi_buffer; pub mod future_support; +pub mod rust_buffer; pub mod stream_support; mod subtask; #[path = "../../../guest-rust/rt/src/async_support/waitable.rs"] diff --git a/crates/symmetric_executor/rust-client/src/async_support/rust_buffer.rs b/crates/symmetric_executor/rust-client/src/async_support/rust_buffer.rs new file mode 100644 index 000000000..ed22541d4 --- /dev/null +++ b/crates/symmetric_executor/rust-client/src/async_support/rust_buffer.rs @@ -0,0 +1,22 @@ +// unlike abi_buffer this caches high level elements as we directly write into the +// read buffer and don't need a storage for lowered elements + +use std::collections::VecDeque; + +pub struct RustBuffer { + buf: VecDeque, +} + +impl RustBuffer { + pub(crate) fn new(vec: Vec) -> Self { + Self { buf: vec.into() } + } + + pub fn remaining(&self) -> usize { + self.buf.len() + } + + pub(crate) fn take_n(&mut self, _n: usize, _f: F) { + todo!() + } +} 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 index 9ab9865fc..8d5563a79 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -1,6 +1,10 @@ pub use crate::module::symmetric::runtime::symmetric_stream::StreamObj as Stream; use crate::{ - async_support::{abi_buffer::AbiBuffer, wait_on}, + async_support::{ + rust_buffer::RustBuffer, + wait_on, + waitable::{WaitableOp, WaitableOperation}, + }, symmetric_stream::{Address, Buffer}, }; use { @@ -11,7 +15,7 @@ use { fmt, future::Future, iter, - marker::PhantomData, + marker::{self, PhantomData}, mem::{self, MaybeUninit}, pin::Pin, task::{Context, Poll}, @@ -23,9 +27,10 @@ pub struct StreamVtable { pub layout: Layout, pub lower: Option, pub lift: Option T>, + //pub dealloc_lists: Option, } -fn ceiling(x: usize, y: usize) -> usize { +const fn ceiling(x: usize, y: usize) -> usize { (x / y) + if x % y == 0 { 0 } else { 1 } } @@ -35,54 +40,174 @@ pub mod results { pub const CANCELED: isize = 0; } +// Used within Waitable +pub mod new_state { + pub const DROPPED: u32 = 1; + pub const WAITING_FOR_READY: u32 = 2; + pub const WAITING_FOR_FINISH: u32 = 3; + pub const FINISHED: u32 = 0; + pub const UNKNOWN: u32 = 4; +} + #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum StreamResult { Complete(usize), Dropped, - // Cancelled, + Cancelled, } pub struct StreamWrite<'a, T: 'static> { - _phantom: PhantomData<&'a T>, + op: WaitableOperation>, + // _phantom: PhantomData<&'a T>, + // writer: &'a mut StreamWriter, + // _future: Option + 'static + Send>>>, + // values: Vec, +} + +struct WriteInProgress<'a, T: 'static> { writer: &'a mut StreamWriter, - _future: Option + 'static + Send>>>, - values: Vec, + buf: RustBuffer, + event: Option, + amount: usize, } -impl Future for StreamWrite<'_, T> { - type Output = (StreamResult, AbiBuffer); +unsafe impl<'a, T> WaitableOp for StreamWriteOp<'a, T> +where + T: 'static, +{ + type Start = (&'a mut StreamWriter, RustBuffer); + type InProgress = WriteInProgress<'a, T>; + type Result = (StreamResult, RustBuffer); + type Cancel = (StreamResult, RustBuffer); + type Handle = crate::EventSubscription; + + fn start((writer, buf): Self::Start) -> (u32, Self::InProgress) { + ( + new_state::UNKNOWN, + WriteInProgress { + writer, + buf, + event: None, + amount: 0, + }, + ) + } + fn in_progress_update( + WriteInProgress { + writer, + mut buf, + event, + mut amount, + }: Self::InProgress, + code: u32, + ) -> Result { + loop { + if writer.done { + return Ok(( + if amount == 0 { + StreamResult::Dropped + } else { + StreamResult::Complete(amount) + }, + buf, + )); + } - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let me = self.get_mut(); - match Pin::new(&mut me.writer).poll_ready(cx) { - Poll::Ready(_) => { - let values: Vec<_> = me.values.drain(..).collect(); - if values.is_empty() { - // delayed flush - Poll::Ready(( - StreamResult::Complete(1), - AbiBuffer::new(Vec::new(), me.writer._vtable), - )) - } else { - Pin::new(&mut me.writer).start_send(values).unwrap(); - match Pin::new(&mut me.writer).poll_ready(cx) { - Poll::Ready(_) => Poll::Ready(( - StreamResult::Complete(1), - AbiBuffer::new(Vec::new(), me.writer._vtable), - )), - Poll::Pending => Poll::Pending, - } + // was poll_ready + let ready = writer.handle.is_ready_to_write(); + + if !ready { + let subscr = writer.handle.write_ready_subscribe(); + subscr.reset(); + break Err(WriteInProgress { + writer, + buf, + event: Some(subscr), + amount, + }); + //(new_state::WAITING_FOR_READY, (writer, buf, Some(subscr))); + } else { + // was start_send + let buffer = writer.handle.start_writing(); + let addr = buffer.get_address().take_handle() as *mut u8; + let size = buffer.capacity() as usize; + buf.take_n(size, |v| todo!()); + writer.handle.finish_writing(Some(buffer)); + amount += size; + if buf.remaining() == 0 { + break Ok((StreamResult::Complete(amount), buf)); + //(new_state::FINISHED, (writer, buf, None)); } } - Poll::Pending => Poll::Pending, } } + fn start_cancelled((writer, buf): Self::Start) -> Self::Cancel { + todo!() + // WriteInProgress{StreamResult::Cancelled, writer, buf, amount: 0} + } + fn in_progress_waitable(_progr: &Self::InProgress) -> crate::async_support::waitable::Handle { + todo!() + // writer.handle.handle() as crate::async_support::waitable::Handle + } + fn in_progress_cancel(_progr: &Self::InProgress) -> u32 { + todo!() + } + fn result_into_cancel(result: Self::Result) -> Self::Cancel { + result + } +} + +struct StreamWriteOp<'a, T: 'static>(marker::PhantomData<(&'a mut StreamWriter, T)>); + +impl Future for StreamWrite<'_, T> { + type Output = (StreamResult, RustBuffer); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.pin_project().poll_complete(cx) + // todo!() + // let me = self.get_mut(); + // match Pin::new(&mut me.writer).poll_ready(cx) { + // Poll::Ready(_) => { + // let values: Vec<_> = me.values.drain(..).collect(); + // if values.is_empty() { + // // delayed flush + // Poll::Ready(( + // StreamResult::Complete(1), + // RustBuffer::new(Vec::new(), me.writer._vtable), + // )) + // } else { + // Pin::new(&mut me.writer).start_send(values).unwrap(); + // match Pin::new(&mut me.writer).poll_ready(cx) { + // Poll::Ready(_) => Poll::Ready(( + // StreamResult::Complete(1), + // RustBuffer::new(Vec::new(), me.writer._vtable), + // )), + // Poll::Pending => Poll::Pending, + // } + // } + // } + // Poll::Pending => Poll::Pending, + // } + } +} + +impl<'a, T: 'static> StreamWrite<'a, T> { + fn pin_project(self: Pin<&mut Self>) -> Pin<&mut WaitableOperation>> { + // SAFETY: we've chosen that when `Self` is pinned that it translates to + // always pinning the inner field, so that's codified here. + unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().op) } + } + + pub fn cancel(self: Pin<&mut Self>) -> (StreamResult, RustBuffer) { + self.pin_project().cancel() + } } pub struct StreamWriter { handle: Stream, - future: Option + 'static + Send>>>, + /*?*/ future: Option + 'static + Send>>>, _vtable: &'static StreamVtable, + done: bool, } impl StreamWriter { @@ -92,15 +217,23 @@ impl StreamWriter { handle, future: None, _vtable: vtable, + done: false, } } pub fn write(&mut self, values: Vec) -> StreamWrite<'_, T> { + self.write_buf(RustBuffer::new(values)) //, self._vtable)) + // StreamWrite { + // writer: self, + // _future: None, + // _phantom: PhantomData, + // values, + // } + } + + pub fn write_buf(&mut self, values: RustBuffer) -> StreamWrite<'_, T> { StreamWrite { - writer: self, - _future: None, - _phantom: PhantomData, - values, + op: WaitableOperation::new((self, values)), } } From 1473d1197d190c16b4a488ceb7455df65d2fd91e Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 21 Jun 2025 22:13:26 +0200 Subject: [PATCH 657/672] trying to thread the subscription through the executor call stack --- .../rt/src/async_support/waitable.rs | 24 ++++++++++++------- .../src/async_support/stream_support.rs | 9 +++++-- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/crates/guest-rust/rt/src/async_support/waitable.rs b/crates/guest-rust/rt/src/async_support/waitable.rs index 52331ad41..87a3473d7 100644 --- a/crates/guest-rust/rt/src/async_support/waitable.rs +++ b/crates/guest-rust/rt/src/async_support/waitable.rs @@ -9,10 +9,10 @@ use std::task::{Context, Poll, Waker}; #[cfg(not(feature = "symmetric"))] use {super::cabi, std::ffi::c_void, std::ptr}; -#[cfg(not(feature = "symmetric"))] -pub type Handle = u32; -#[cfg(feature = "symmetric")] -pub type Handle = *mut u8; +// #[cfg(not(feature = "symmetric"))] +// pub type Handle = u32; +// #[cfg(feature = "symmetric")] +// pub type Handle = *mut u8; /// Generic future-based operation on any "waitable" in the component model. /// @@ -111,7 +111,7 @@ pub unsafe trait WaitableOp { /// Acquires the component-model `waitable` index that the `InProgress` /// state is waiting on. - fn in_progress_waitable(state: &Self::InProgress) -> Handle; + fn in_progress_waitable(state: &Self::InProgress) -> Self::Handle; /// Initiates a request for cancellation of this operation. Returns the /// status code returned by the `{future,stream}.cancel-{read,write}` @@ -171,7 +171,7 @@ where /// * Fill in `completion_status` with the result of a completion event. /// * Call `cx.waker().wake()`. #[cfg(not(feature = "symmetric"))] - pub fn register_waker(self: Pin<&mut Self>, waitable: Handle, cx: &mut Context) { + pub fn register_waker(self: Pin<&mut Self>, waitable: S::Handle, cx: &mut Context) { let (_, mut completion_status) = self.pin_project(); debug_assert!(completion_status.as_mut().code_mut().is_none()); *completion_status.as_mut().waker_mut() = Some(cx.waker().clone()); @@ -208,11 +208,17 @@ where } #[cfg(feature = "symmetric")] - pub fn register_waker(self: Pin<&mut Self>, _waitable: Handle, _cx: &mut Context) { - todo!() + pub fn register_waker(self: Pin<&mut Self>, waitable: S::Handle, cx: &mut Context) { + let data = cx.waker().data(); + let mut copy = Some(waitable); + std::mem::swap( + unsafe { &mut *(data.cast::>().cast_mut()) }, + &mut copy, + ); +// todo!() } #[cfg(feature = "symmetric")] - pub fn unregister_waker(self: Pin<&mut Self>, _waitable: Handle) { + pub fn unregister_waker(self: Pin<&mut Self>, _waitable: S::Handle) { todo!() } 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 index 8d5563a79..aa37b8248 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -145,8 +145,13 @@ where todo!() // WriteInProgress{StreamResult::Cancelled, writer, buf, amount: 0} } - fn in_progress_waitable(_progr: &Self::InProgress) -> crate::async_support::waitable::Handle { - todo!() + fn in_progress_waitable(_progr: &Self::InProgress) -> Self::Handle { + if let Some(evt) = _progr.event.as_ref() { + evt.dup() + // symmetric_executor::register() + } else { + todo!() + } // writer.handle.handle() as crate::async_support::waitable::Handle } fn in_progress_cancel(_progr: &Self::InProgress) -> u32 { From 5cbd175be9220b22d520b8631eb425d53470ca54 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 21 Jun 2025 22:14:06 +0200 Subject: [PATCH 658/672] fmt --- crates/guest-rust/rt/src/async_support/waitable.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/guest-rust/rt/src/async_support/waitable.rs b/crates/guest-rust/rt/src/async_support/waitable.rs index 87a3473d7..49416bfb5 100644 --- a/crates/guest-rust/rt/src/async_support/waitable.rs +++ b/crates/guest-rust/rt/src/async_support/waitable.rs @@ -215,7 +215,7 @@ where unsafe { &mut *(data.cast::>().cast_mut()) }, &mut copy, ); -// todo!() + // todo!() } #[cfg(feature = "symmetric")] pub fn unregister_waker(self: Pin<&mut Self>, _waitable: S::Handle) { From 676931b876b2d204157d4b26b78747b4101fe515 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 21 Jun 2025 22:36:44 +0200 Subject: [PATCH 659/672] turn on trace in the tests for now --- crates/test/src/rust.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/test/src/rust.rs b/crates/test/src/rust.rs index 29d5ec295..907902d5a 100644 --- a/crates/test/src/rust.rs +++ b/crates/test/src/rust.rs @@ -109,7 +109,7 @@ impl LanguageMethods for Rust { "symmetric_executor = {{ path = {executor_path:?} }}\n" )); symmetric_runtime.push_str(&format!( - "symmetric_stream = {{ path = \"{}/symmetric_stream\" }}\n", + "symmetric_stream = {{ path = \"{}/symmetric_stream\", features = [\"trace\"] }}\n", executor_path.display() )); format!("path = {bindgen_path:?}, package = \"mini-bindgen\"") From 33122720b24bf7f53f9d3faad950075c2c8d3414 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sat, 21 Jun 2025 22:58:29 +0200 Subject: [PATCH 660/672] abstract the low level interaction with the waker --- .../rt/src/async_support/waitable.rs | 9 ++------- .../rust-client/src/async_support.rs | 18 +++++++++++------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/crates/guest-rust/rt/src/async_support/waitable.rs b/crates/guest-rust/rt/src/async_support/waitable.rs index 49416bfb5..247e985c1 100644 --- a/crates/guest-rust/rt/src/async_support/waitable.rs +++ b/crates/guest-rust/rt/src/async_support/waitable.rs @@ -209,13 +209,8 @@ where #[cfg(feature = "symmetric")] pub fn register_waker(self: Pin<&mut Self>, waitable: S::Handle, cx: &mut Context) { - let data = cx.waker().data(); - let mut copy = Some(waitable); - std::mem::swap( - unsafe { &mut *(data.cast::>().cast_mut()) }, - &mut copy, - ); - // todo!() + // Safety: we assume S::Handle is a EventSubscription + crate::async_support::context_set_wait(cx, unsafe { std::mem::transmute(&waitable) }); } #[cfg(feature = "symmetric")] pub fn unregister_waker(self: Pin<&mut Self>, _waitable: S::Handle) { diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 11c06e11f..7bf90ab74 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -70,18 +70,22 @@ unsafe fn poll(state: *mut FutureState) -> Poll<()> { }) } +pub fn context_set_wait(cx: &Context, wait_for: &EventSubscription) { + // 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::>().cast_mut()) }, + &mut copy, + ); +} + 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::>().cast_mut()) }, - &mut copy, - ); + context_set_wait(cx, &wait_for); Poll::Pending } }) From 49c4ff1b97e32aed32f8eef91714454e1c4421a2 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 22 Jun 2025 00:24:37 +0200 Subject: [PATCH 661/672] regenerate test --- .../future/src/future_world.rs | 13 ++++---- .../future_cpp/future_world.cpp | 31 ++++++++++--------- crates/test/src/rust.rs | 2 +- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/crates/cpp/tests/symmetric_future/future/src/future_world.rs b/crates/cpp/tests/symmetric_future/future/src/future_world.rs index 96e95baaa..f990de5a5 100644 --- a/crates/cpp/tests/symmetric_future/future/src/future_world.rs +++ b/crates/cpp/tests/symmetric_future/future/src/future_world.rs @@ -161,13 +161,12 @@ pub mod wit_future { unsafe fn dealloc_lists(ptr: *mut u8) { unsafe {} } - pub static VTABLE: wit_bindgen::rt::async_support::FutureVtable = wit_bindgen::rt::async_support::FutureVtable::< - u32, - > { - layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(4, 4) }, - lift, - lower, - }; + pub static VTABLE: wit_bindgen::rt::async_support::FutureVtable = + wit_bindgen::rt::async_support::FutureVtable:: { + layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(4, 4) }, + lift, + lower, + }; impl super::FuturePayload for u32 { const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable = &VTABLE; } diff --git a/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp b/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp index ded808cdd..5593ed3e5 100644 --- a/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp +++ b/crates/cpp/tests/symmetric_future/future_cpp/future_world.cpp @@ -22,31 +22,32 @@ void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) { } -#include "async_support.h" - -template -struct IntLifting { - static constexpr size_t SIZE = sizeof(T); - static T lift(uint8_t const*ptr) { - return *(T const*)ptr; - } - static void lower(T&& obj, uint8_t *ptr) { -// new ((T*)ptr) T(std::move(obj)); - *(T*)ptr = obj; - } +struct Lift1 { + static uint32_t lift(uint8_t const* ptr) { int32_t l0 = *((int32_t const*)(ptr + 0)); + return (uint32_t(l0));} + static void lower(uint32_t && value, uint8_t *ptr) { *((int32_t*)(ptr + 0)) = (int32_t(value)); +} +static constexpr size_t SIZE = 4; }; - +struct Lift2 { + static uint32_t lift(uint8_t const* ptr) { int32_t l0 = *((int32_t const*)(ptr + 0)); + return (uint32_t(l0));} + static void lower(uint32_t && value, uint8_t *ptr) { *((int32_t*)(ptr + 0)) = (int32_t(value)); +} +static constexpr size_t SIZE = 4; +}; +#include "async_support.h" extern "C" uint8_t* testX3AtestX2Ffuture_sourceX00create(); std::future test::test::future_source::Create() { auto ret = testX3AtestX2Ffuture_sourceX00create(); - return lift_future>(ret); + return lift_future(ret); } extern "C" uint8_t* testX3AtestX2Ffuture_testX00create() { auto result0 = exports::test::test::future_test::Create(); - return lower_future>(std::move(result0)); + return lower_future(std::move(result0)); } // Component Adapters diff --git a/crates/test/src/rust.rs b/crates/test/src/rust.rs index 907902d5a..2465b6854 100644 --- a/crates/test/src/rust.rs +++ b/crates/test/src/rust.rs @@ -106,7 +106,7 @@ impl LanguageMethods for Rust { let bindgen_path = cwd.join("crates/symmetric_executor/dummy-bindgen"); let executor_path = cwd.join("crates/symmetric_executor"); symmetric_runtime.push_str(&format!( - "symmetric_executor = {{ path = {executor_path:?} }}\n" + "symmetric_executor = {{ path = {executor_path:?}, features = [\"trace\"] }}\n" )); symmetric_runtime.push_str(&format!( "symmetric_stream = {{ path = \"{}/symmetric_stream\", features = [\"trace\"] }}\n", From 75b68fd3e5ee3f46cb55d8029273079437f201b9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 22 Jun 2025 00:29:37 +0200 Subject: [PATCH 662/672] regenerate the other future example --- .../tests/symmetric_future_string/Cargo.lock | 7 ++ .../bindings/runner.rs | 59 ++++++------ .../symmetric_future_string/bindings/test.rs | 90 ++++++++++--------- .../tests/symmetric_future_string/generate.sh | 4 +- .../tests/symmetric_future_string/runner.rs | 2 +- .../cpp/tests/symmetric_future_string/test.rs | 2 +- 6 files changed, 88 insertions(+), 76 deletions(-) diff --git a/crates/cpp/tests/symmetric_future_string/Cargo.lock b/crates/cpp/tests/symmetric_future_string/Cargo.lock index 38c0682a8..f7f169f23 100644 --- a/crates/cpp/tests/symmetric_future_string/Cargo.lock +++ b/crates/cpp/tests/symmetric_future_string/Cargo.lock @@ -8,6 +8,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + [[package]] name = "dummy-rt" version = "0.1.0" @@ -209,6 +215,7 @@ checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" name = "wit-bindgen-symmetric-rt" version = "0.36.0" dependencies = [ + "bitflags", "dummy-rt", "futures", ] diff --git a/crates/cpp/tests/symmetric_future_string/bindings/runner.rs b/crates/cpp/tests/symmetric_future_string/bindings/runner.rs index 4d7969cac..648b91f25 100644 --- a/crates/cpp/tests/symmetric_future_string/bindings/runner.rs +++ b/crates/cpp/tests/symmetric_future_string/bindings/runner.rs @@ -1,38 +1,36 @@ -// Generated by `wit-bindgen` 0.41.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.42.1. DO NOT EDIT! // Options used: +#[rustfmt::skip] #[allow(dead_code, clippy::all)] pub mod a { pub mod b { - #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] pub mod the_test { #[used] #[doc(hidden)] - static __FORCE_SECTION_REF: fn() = - super::super::super::__link_custom_section_describing_imports; - - use crate::wit_future::vtable0::VTABLE; - + static __FORCE_SECTION_REF: fn() = super::super::super::__link_custom_section_describing_imports; use super::super::super::_rt; #[allow(unused_unsafe, clippy::all)] #[allow(async_fn_in_trait)] - pub fn f() -> wit_bindgen_symmetric_rt::async_support::FutureReader<_rt::String> { + pub fn f() -> wit_bindgen::rt::async_support::FutureReader<_rt::String> { unsafe { #[link(wasm_import_module = "a:b/the-test")] unsafe extern "C" { + #[allow(non_snake_case)] #[cfg_attr(target_arch = "wasm32", link_name = "f")] fn aX3AbX2Fthe_testX00f() -> *mut u8; } let ret = aX3AbX2Fthe_testX00f(); - wit_bindgen_symmetric_rt::async_support::FutureReader::from_handle( + wit_bindgen::rt::async_support::FutureReader::new( ret, - <_rt::String as super::super::super::wit_future::FuturePayload>::VTABLE, + &super::super::super::wit_future::vtable0::VTABLE, ) } } } } } +#[rustfmt::skip] mod _rt { #![allow(dead_code, clippy::all)] pub use alloc_crate::string::String; @@ -58,7 +56,6 @@ mod _rt { } pub mod wit_future { #![allow(dead_code, unused_variables, clippy::all)] - #[doc(hidden)] pub trait FuturePayload: Unpin + Sized + 'static { const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable; @@ -66,7 +63,6 @@ pub mod wit_future { #[doc(hidden)] #[allow(unused_unsafe)] pub mod vtable0 { - unsafe fn lift(ptr: *mut u8) -> super::super::_rt::String { unsafe { let l0 = *ptr.add(0).cast::<*mut u8>(); @@ -77,7 +73,6 @@ pub mod wit_future { } else { Default::default() }; - super::super::_rt::string_lift(bytes2) } } @@ -97,37 +92,45 @@ pub mod wit_future { super::super::_rt::cabi_dealloc(l0, l1, 1); } } - - pub static VTABLE: wit_bindgen::rt::async_support::FutureVtable = - wit_bindgen::rt::async_support::FutureVtable:: { - layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(8, 4) }, - lift, - lower, + pub static VTABLE: wit_bindgen::rt::async_support::FutureVtable< + super::super::_rt::String, + > = wit_bindgen::rt::async_support::FutureVtable:: { + layout: unsafe { + ::std::alloc::Layout::from_size_align_unchecked( + 2 * ::core::mem::size_of::<*const u8>(), + ::core::mem::size_of::<*const u8>(), + ) + }, + lift, + lower, }; - impl super::FuturePayload for super::super::_rt::String { const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable = &VTABLE; } } /// Creates a new Component Model `future` with the specified payload type. - pub fn new() -> ( - wit_bindgen_symmetric_rt::async_support::FutureWriter, - wit_bindgen_symmetric_rt::async_support::FutureReader, + /// + /// The `default` function provided computes the default value to be sent in + /// this future if no other value was otherwise sent. + pub fn new( + default: fn() -> T, + ) -> ( + wit_bindgen::rt::async_support::FutureWriter, + wit_bindgen::rt::async_support::FutureReader, ) { - wit_bindgen_symmetric_rt::async_support::future_support::new_future(T::VTABLE) + unsafe { wit_bindgen::rt::async_support::future_new::(default, T::VTABLE) } } } - +#[rustfmt::skip] #[cfg(target_arch = "wasm32")] -#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:a:b:runner:encoded world")] +#[unsafe(link_section = "component-type:wit-bindgen:0.42.1:a:b:runner:encoded world")] #[doc(hidden)] #[allow(clippy::octal_escapes)] pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 180] = *b"\ \0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x078\x01A\x02\x01A\x02\x01\ B\x03\x01e\x01s\x01@\0\0\0\x04\0\x01f\x01\x01\x03\0\x0ca:b/the-test\x05\0\x04\0\x0a\ a:b/runner\x04\0\x0b\x0c\x01\0\x06runner\x03\0\0\0G\x09producers\x01\x0cprocesse\ -d-by\x02\x0dwit-component\x070.229.0\x10wit-bindgen-rust\x060.41.0"; - +d-by\x02\x0dwit-component\x070.235.0\x10wit-bindgen-rust\x060.42.1"; #[inline(never)] #[doc(hidden)] pub fn __link_custom_section_describing_imports() { diff --git a/crates/cpp/tests/symmetric_future_string/bindings/test.rs b/crates/cpp/tests/symmetric_future_string/bindings/test.rs index ea9307c79..c117e0e14 100644 --- a/crates/cpp/tests/symmetric_future_string/bindings/test.rs +++ b/crates/cpp/tests/symmetric_future_string/bindings/test.rs @@ -1,54 +1,49 @@ -// Generated by `wit-bindgen` 0.41.0. DO NOT EDIT! +// Generated by `wit-bindgen` 0.42.1. DO NOT EDIT! // Options used: +#[rustfmt::skip] #[allow(dead_code, clippy::all)] pub mod exports { pub mod a { pub mod b { - #[allow(dead_code, async_fn_in_trait, unused_imports, clippy::all)] pub mod the_test { #[used] #[doc(hidden)] - static __FORCE_SECTION_REF: fn() = - super::super::super::super::__link_custom_section_describing_imports; - + 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, unused_unsafe)] pub unsafe fn _export_f_cabi() -> *mut u8 { unsafe { - #[cfg(target_arch = "wasm32")] - _rt::run_ctors_once(); + #[cfg(target_arch = "wasm32")] _rt::run_ctors_once(); let result0 = { T::f() }; (result0).take_handle() as *mut u8 } } pub trait Guest { #[allow(async_fn_in_trait)] - fn f() -> wit_bindgen_symmetric_rt::async_support::FutureReader<_rt::String>; + fn f() -> wit_bindgen::rt::async_support::FutureReader<_rt::String>; } #[doc(hidden)] - - macro_rules! __export_a_b_the_test_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 aX3AbX2Fthe_testX00f() -> *mut u8 { - unsafe { $($path_to_types)*::_export_f_cabi::<$ty>() } - } - };); - } + macro_rules! __export_a_b_the_test_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)] + #[allow(non_snake_case)] unsafe extern "C" fn + aX3AbX2Fthe_testX00f() -> * mut u8 { unsafe { + $($path_to_types)*:: _export_f_cabi::<$ty > () } } }; + }; + } #[doc(hidden)] pub(crate) use __export_a_b_the_test_cabi; } } } } +#[rustfmt::skip] mod _rt { #![allow(dead_code, clippy::all)] pub use alloc_crate::string::String; - pub use alloc_crate::vec::Vec; pub unsafe fn cabi_dealloc(ptr: *mut u8, size: usize, align: usize) { if size == 0 { return; @@ -58,7 +53,6 @@ mod _rt { alloc::dealloc(ptr, layout); } } - #[cfg(target_arch = "wasm32")] pub fn run_ctors_once() { wit_bindgen::rt::run_ctors_once(); @@ -68,7 +62,6 @@ mod _rt { } pub mod wit_future { #![allow(dead_code, unused_variables, clippy::all)] - #[doc(hidden)] pub trait FuturePayload: Unpin + Sized + 'static { const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable; @@ -84,7 +77,6 @@ pub mod wit_future { let string2 = String::from( std::str::from_utf8(std::slice::from_raw_parts(l0, len2)).unwrap(), ); - string2 } } @@ -105,27 +97,35 @@ pub mod wit_future { super::super::_rt::cabi_dealloc(l0, l1, 1); } } - - pub static VTABLE: wit_bindgen::rt::async_support::FutureVtable = - wit_bindgen::rt::async_support::FutureVtable:: { - layout: unsafe { ::std::alloc::Layout::from_size_align_unchecked(8, 4) }, - lift, - lower, + pub static VTABLE: wit_bindgen::rt::async_support::FutureVtable< + super::super::_rt::String, + > = wit_bindgen::rt::async_support::FutureVtable:: { + layout: unsafe { + ::std::alloc::Layout::from_size_align_unchecked( + 2 * ::core::mem::size_of::<*const u8>(), + ::core::mem::size_of::<*const u8>(), + ) + }, + lift, + lower, }; - impl super::FuturePayload for super::super::_rt::String { const VTABLE: &'static wit_bindgen::rt::async_support::FutureVtable = &VTABLE; } } /// Creates a new Component Model `future` with the specified payload type. - pub fn new() -> ( - wit_bindgen_symmetric_rt::async_support::FutureWriter, - wit_bindgen_symmetric_rt::async_support::FutureReader, + /// + /// The `default` function provided computes the default value to be sent in + /// this future if no other value was otherwise sent. + pub fn new( + default: fn() -> T, + ) -> ( + wit_bindgen::rt::async_support::FutureWriter, + wit_bindgen::rt::async_support::FutureReader, ) { - wit_bindgen_symmetric_rt::async_support::future_support::new_future(T::VTABLE) + unsafe { wit_bindgen::rt::async_support::future_new::(default, T::VTABLE) } } } - /// Generates `#[unsafe(no_mangle)]` functions to export the specified type as /// the root implementation of all generated traits. /// @@ -144,26 +144,28 @@ pub mod wit_future { /// ``` #[allow(unused_macros)] #[doc(hidden)] - macro_rules! __export_test_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::a::b::the_test::__export_a_b_the_test_cabi!($ty with_types_in $($path_to_types_root)*::exports::a::b::the_test); - ) + ($ty:ident) => { + self::export!($ty with_types_in self); + }; + ($ty:ident with_types_in $($path_to_types_root:tt)*) => { + $($path_to_types_root)*:: + exports::a::b::the_test::__export_a_b_the_test_cabi!($ty with_types_in + $($path_to_types_root)*:: exports::a::b::the_test); + }; } #[doc(inline)] pub(crate) use __export_test_impl as export; - +#[rustfmt::skip] #[cfg(target_arch = "wasm32")] -#[unsafe(link_section = "component-type:wit-bindgen:0.41.0:a:b:test:encoded world")] +#[unsafe(link_section = "component-type:wit-bindgen:0.42.1:a:b:test:encoded world")] #[doc(hidden)] #[allow(clippy::octal_escapes)] pub static __WIT_BINDGEN_COMPONENT_TYPE: [u8; 176] = *b"\ \0asm\x0d\0\x01\0\0\x19\x16wit-component-encoding\x04\0\x076\x01A\x02\x01A\x02\x01\ B\x03\x01e\x01s\x01@\0\0\0\x04\0\x01f\x01\x01\x04\0\x0ca:b/the-test\x05\0\x04\0\x08\ a:b/test\x04\0\x0b\x0a\x01\0\x04test\x03\0\0\0G\x09producers\x01\x0cprocessed-by\ -\x02\x0dwit-component\x070.229.0\x10wit-bindgen-rust\x060.41.0"; - +\x02\x0dwit-component\x070.235.0\x10wit-bindgen-rust\x060.42.1"; #[inline(never)] #[doc(hidden)] pub fn __link_custom_section_describing_imports() { diff --git a/crates/cpp/tests/symmetric_future_string/generate.sh b/crates/cpp/tests/symmetric_future_string/generate.sh index 31888752d..727a922a5 100755 --- a/crates/cpp/tests/symmetric_future_string/generate.sh +++ b/crates/cpp/tests/symmetric_future_string/generate.sh @@ -1,5 +1,5 @@ #!/bin/sh cd bindings -../../../../../target/debug/wit-bindgen rust ../test.wit --symmetric -w test -../../../../../target/debug/wit-bindgen rust ../test.wit --symmetric -w runner +../../../../../target/debug/wit-bindgen rust ../test.wit --symmetric -w test --format +../../../../../target/debug/wit-bindgen rust ../test.wit --symmetric -w runner --format diff --git a/crates/cpp/tests/symmetric_future_string/runner.rs b/crates/cpp/tests/symmetric_future_string/runner.rs index f67ac0a49..815647e47 100644 --- a/crates/cpp/tests/symmetric_future_string/runner.rs +++ b/crates/cpp/tests/symmetric_future_string/runner.rs @@ -10,6 +10,6 @@ use crate::a::b::the_test::f; fn main() { async_support::block_on(async { let result = f().await; - assert_eq!(result, Some(String::from("Hello"))); + assert_eq!(result, String::from("Hello")); }); } diff --git a/crates/cpp/tests/symmetric_future_string/test.rs b/crates/cpp/tests/symmetric_future_string/test.rs index a071813f9..c591bc930 100644 --- a/crates/cpp/tests/symmetric_future_string/test.rs +++ b/crates/cpp/tests/symmetric_future_string/test.rs @@ -11,7 +11,7 @@ use wit_bindgen::rt::async_support::{self, FutureReader}; impl Guest for Component { fn f() -> FutureReader { - let (wr, rd) = wit_future::new(); + let (wr, rd) = wit_future::new(String::default); async_support::spawn(async move { wr.write(String::from("Hello")).await; }); From 72e43d8403ddefcfc6fc3f8ce6030134d14ca911 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 22 Jun 2025 11:18:19 +0200 Subject: [PATCH 663/672] waitable didn't fit well enough --- .../rust-client/src/async_support.rs | 4 ++-- .../rust-client/src/async_support/stream_support.rs | 10 +++++----- crates/symmetric_executor/src/lib.rs | 5 ++++- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/crates/symmetric_executor/rust-client/src/async_support.rs b/crates/symmetric_executor/rust-client/src/async_support.rs index 7bf90ab74..1cab79974 100644 --- a/crates/symmetric_executor/rust-client/src/async_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support.rs @@ -23,8 +23,8 @@ pub mod future_support; pub mod rust_buffer; pub mod stream_support; mod subtask; -#[path = "../../../guest-rust/rt/src/async_support/waitable.rs"] -mod waitable; +// #[path = "../../../guest-rust/rt/src/async_support/waitable.rs"] +// mod waitable; // See https://github.com/rust-lang/rust/issues/13231 for the limitation // / Send constraint on futures for spawn, loosen later 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 index aa37b8248..5bce7ef5f 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -1,10 +1,6 @@ pub use crate::module::symmetric::runtime::symmetric_stream::StreamObj as Stream; use crate::{ - async_support::{ - rust_buffer::RustBuffer, - wait_on, - waitable::{WaitableOp, WaitableOperation}, - }, + async_support::{rust_buffer::RustBuffer, wait_on}, symmetric_stream::{Address, Buffer}, }; use { @@ -22,6 +18,10 @@ use { }, }; +// waitable::{WaitableOp, WaitableOperation} looked cool, but +// as it waits for the runtime and uses Wakers I don't think the +// logic fits + #[doc(hidden)] pub struct StreamVtable { pub layout: Layout, diff --git a/crates/symmetric_executor/src/lib.rs b/crates/symmetric_executor/src/lib.rs index 99a80b7a7..1d35b4b72 100644 --- a/crates/symmetric_executor/src/lib.rs +++ b/crates/symmetric_executor/src/lib.rs @@ -191,7 +191,10 @@ impl symmetric_executor::GuestEventSubscription for EventSubscriptionInternal { } fn dup(&self) -> symmetric_executor::EventSubscription { - symmetric_executor::EventSubscription::new(self.dup()) + let res = symmetric_executor::EventSubscription::new(self.dup()); + // to avoid endless recursion de-activate the original + self.reset(); + res } fn reset(&self) { From a7d3b02ccfc49d68cd2e65849accd21e88f29809 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 22 Jun 2025 17:43:01 +0200 Subject: [PATCH 664/672] revert changes to waitable --- .../rt/src/async_support/future_support.rs | 2 -- .../rt/src/async_support/stream_support.rs | 2 -- .../rt/src/async_support/subtask.rs | 1 - .../rt/src/async_support/waitable.rs | 29 ++++--------------- 4 files changed, 5 insertions(+), 29 deletions(-) diff --git a/crates/guest-rust/rt/src/async_support/future_support.rs b/crates/guest-rust/rt/src/async_support/future_support.rs index 0e241aa98..da39d1296 100644 --- a/crates/guest-rust/rt/src/async_support/future_support.rs +++ b/crates/guest-rust/rt/src/async_support/future_support.rs @@ -338,7 +338,6 @@ where type InProgress = (FutureWriter, Option); type Result = (WriteComplete, FutureWriter); type Cancel = FutureWriteCancel; - type Handle = u32; fn start((writer, value): Self::Start) -> (u32, Self::InProgress) { // TODO: it should be safe to store the lower-destination in @@ -622,7 +621,6 @@ where type InProgress = (FutureReader, Option); type Result = (ReadComplete, FutureReader); type Cancel = Result>; - type Handle = u32; fn start(reader: Self::Start) -> (u32, Self::InProgress) { let (ptr, cleanup) = Cleanup::new(reader.vtable.layout); diff --git a/crates/guest-rust/rt/src/async_support/stream_support.rs b/crates/guest-rust/rt/src/async_support/stream_support.rs index 389c878bd..fd4d0d5f6 100644 --- a/crates/guest-rust/rt/src/async_support/stream_support.rs +++ b/crates/guest-rust/rt/src/async_support/stream_support.rs @@ -242,7 +242,6 @@ where type InProgress = (&'a mut StreamWriter, AbiBuffer); type Result = (StreamResult, AbiBuffer); type Cancel = (StreamResult, AbiBuffer); - type Handle = u32; fn start((writer, buf): Self::Start) -> (u32, Self::InProgress) { if writer.done { @@ -459,7 +458,6 @@ where type InProgress = (&'a mut StreamReader, Vec, Option); type Result = (StreamResult, Vec); type Cancel = (StreamResult, Vec); - type Handle = u32; fn start((reader, mut buf): Self::Start) -> (u32, Self::InProgress) { if reader.done { diff --git a/crates/guest-rust/rt/src/async_support/subtask.rs b/crates/guest-rust/rt/src/async_support/subtask.rs index c490c5666..79804783d 100644 --- a/crates/guest-rust/rt/src/async_support/subtask.rs +++ b/crates/guest-rust/rt/src/async_support/subtask.rs @@ -102,7 +102,6 @@ unsafe impl WaitableOp for SubtaskOps { type InProgress = InProgress; type Result = Result; type Cancel = Result; - type Handle = u32; fn start(state: Self::Start) -> (u32, Self::InProgress) { unsafe { diff --git a/crates/guest-rust/rt/src/async_support/waitable.rs b/crates/guest-rust/rt/src/async_support/waitable.rs index 247e985c1..65fcdde9b 100644 --- a/crates/guest-rust/rt/src/async_support/waitable.rs +++ b/crates/guest-rust/rt/src/async_support/waitable.rs @@ -1,18 +1,14 @@ //! Generic support for "any waitable" and performing asynchronous operations on //! that waitable. +use super::cabi; +use std::ffi::c_void; use std::future::Future; use std::marker; use std::mem; use std::pin::Pin; +use std::ptr; use std::task::{Context, Poll, Waker}; -#[cfg(not(feature = "symmetric"))] -use {super::cabi, std::ffi::c_void, std::ptr}; - -// #[cfg(not(feature = "symmetric"))] -// pub type Handle = u32; -// #[cfg(feature = "symmetric")] -// pub type Handle = *mut u8; /// Generic future-based operation on any "waitable" in the component model. /// @@ -82,9 +78,6 @@ pub unsafe trait WaitableOp { /// Result of when this operation is cancelled. type Cancel; - /// Waitable type, u32 for wasm - type Handle; - /// Starts the async operation. /// /// This method will actually call `{future,stream}.{read,write}` with @@ -111,7 +104,7 @@ pub unsafe trait WaitableOp { /// Acquires the component-model `waitable` index that the `InProgress` /// state is waiting on. - fn in_progress_waitable(state: &Self::InProgress) -> Self::Handle; + fn in_progress_waitable(state: &Self::InProgress) -> u32; /// Initiates a request for cancellation of this operation. Returns the /// status code returned by the `{future,stream}.cancel-{read,write}` @@ -170,8 +163,7 @@ where /// /// * Fill in `completion_status` with the result of a completion event. /// * Call `cx.waker().wake()`. - #[cfg(not(feature = "symmetric"))] - pub fn register_waker(self: Pin<&mut Self>, waitable: S::Handle, cx: &mut Context) { + pub fn register_waker(self: Pin<&mut Self>, waitable: u32, cx: &mut Context) { let (_, mut completion_status) = self.pin_project(); debug_assert!(completion_status.as_mut().code_mut().is_none()); *completion_status.as_mut().waker_mut() = Some(cx.waker().clone()); @@ -207,22 +199,11 @@ where } } - #[cfg(feature = "symmetric")] - pub fn register_waker(self: Pin<&mut Self>, waitable: S::Handle, cx: &mut Context) { - // Safety: we assume S::Handle is a EventSubscription - crate::async_support::context_set_wait(cx, unsafe { std::mem::transmute(&waitable) }); - } - #[cfg(feature = "symmetric")] - pub fn unregister_waker(self: Pin<&mut Self>, _waitable: S::Handle) { - todo!() - } - /// Deregisters the corresponding `register_waker` within the current task /// for the `waitable` passed here. /// /// This relinquishes control of the original `completion_status` pointer /// passed to `register_waker` after this call has completed. - #[cfg(not(feature = "symmetric"))] pub fn unregister_waker(self: Pin<&mut Self>, waitable: u32) { // SAFETY: the contract of `wasip3_task_set` is that the returned // pointer is valid for the lifetime of our entire task, so it's valid From 8a7bad43f1a1cd7a091cbe3872067015ce352dfc Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 22 Jun 2025 17:43:12 +0200 Subject: [PATCH 665/672] document ideas --- crates/cpp/DESIGN.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/crates/cpp/DESIGN.md b/crates/cpp/DESIGN.md index c750a906c..bb6610c94 100644 --- a/crates/cpp/DESIGN.md +++ b/crates/cpp/DESIGN.md @@ -96,3 +96,32 @@ For now for functions the guest import convention is used in both directions: with one modification: Resource IDs become usize, so you can optimize the resource table away. + +## Structs proposal + +See also the explanation of ownership at https://docs.rs/wit-bindgen/0.42.1/wit_bindgen/macro.generate.html + +``` +resource r; +record d { s: string, l: list } +arg: func(d: d); +result: func() -> d; +``` + +``` +struct DResult { + wit::string s; + wit::list l; +} +struct DParam { + std::string_view s; + std::span l; +} +``` + +|direction|style| +|---|---| +|GIA|void arg(DParam d);| +|GIR|DResult result();| +|GEA|void arg(DResult d);| +|GER|DResult result();| From 439feef190ca4f4f06ecbb0815706f8204847658 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 22 Jun 2025 17:45:49 +0200 Subject: [PATCH 666/672] clean the code, make it compile with the newest Rust guest library --- .../src/async_support/stream_support.rs | 180 +++--------------- .../async/stream-string/runner.rs | 11 +- 2 files changed, 34 insertions(+), 157 deletions(-) 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 index 5bce7ef5f..c7dae872c 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -41,14 +41,6 @@ pub mod results { } // Used within Waitable -pub mod new_state { - pub const DROPPED: u32 = 1; - pub const WAITING_FOR_READY: u32 = 2; - pub const WAITING_FOR_FINISH: u32 = 3; - pub const FINISHED: u32 = 0; - pub const UNKNOWN: u32 = 4; -} - #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum StreamResult { Complete(usize), @@ -57,162 +49,46 @@ pub enum StreamResult { } pub struct StreamWrite<'a, T: 'static> { - op: WaitableOperation>, - // _phantom: PhantomData<&'a T>, - // writer: &'a mut StreamWriter, - // _future: Option + 'static + Send>>>, - // values: Vec, -} - -struct WriteInProgress<'a, T: 'static> { + // op: WaitableOperation>, + _phantom: PhantomData<&'a T>, writer: &'a mut StreamWriter, - buf: RustBuffer, - event: Option, - amount: usize, + _future: Option + 'static + Send>>>, + values: RustBuffer, } -unsafe impl<'a, T> WaitableOp for StreamWriteOp<'a, T> -where - T: 'static, -{ - type Start = (&'a mut StreamWriter, RustBuffer); - type InProgress = WriteInProgress<'a, T>; - type Result = (StreamResult, RustBuffer); - type Cancel = (StreamResult, RustBuffer); - type Handle = crate::EventSubscription; - - fn start((writer, buf): Self::Start) -> (u32, Self::InProgress) { - ( - new_state::UNKNOWN, - WriteInProgress { - writer, - buf, - event: None, - amount: 0, - }, - ) - } - fn in_progress_update( - WriteInProgress { - writer, - mut buf, - event, - mut amount, - }: Self::InProgress, - code: u32, - ) -> Result { - loop { - if writer.done { - return Ok(( - if amount == 0 { - StreamResult::Dropped - } else { - StreamResult::Complete(amount) - }, - buf, - )); - } - - // was poll_ready - let ready = writer.handle.is_ready_to_write(); - - if !ready { - let subscr = writer.handle.write_ready_subscribe(); - subscr.reset(); - break Err(WriteInProgress { - writer, - buf, - event: Some(subscr), - amount, - }); - //(new_state::WAITING_FOR_READY, (writer, buf, Some(subscr))); - } else { - // was start_send - let buffer = writer.handle.start_writing(); - let addr = buffer.get_address().take_handle() as *mut u8; - let size = buffer.capacity() as usize; - buf.take_n(size, |v| todo!()); - writer.handle.finish_writing(Some(buffer)); - amount += size; - if buf.remaining() == 0 { - break Ok((StreamResult::Complete(amount), buf)); - //(new_state::FINISHED, (writer, buf, None)); - } - } - } - } - fn start_cancelled((writer, buf): Self::Start) -> Self::Cancel { - todo!() - // WriteInProgress{StreamResult::Cancelled, writer, buf, amount: 0} - } - fn in_progress_waitable(_progr: &Self::InProgress) -> Self::Handle { - if let Some(evt) = _progr.event.as_ref() { - evt.dup() - // symmetric_executor::register() - } else { - todo!() - } - // writer.handle.handle() as crate::async_support::waitable::Handle - } - fn in_progress_cancel(_progr: &Self::InProgress) -> u32 { - todo!() - } - fn result_into_cancel(result: Self::Result) -> Self::Cancel { - result - } -} - -struct StreamWriteOp<'a, T: 'static>(marker::PhantomData<(&'a mut StreamWriter, T)>); - impl Future for StreamWrite<'_, T> { type Output = (StreamResult, RustBuffer); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.pin_project().poll_complete(cx) - // todo!() - // let me = self.get_mut(); - // match Pin::new(&mut me.writer).poll_ready(cx) { - // Poll::Ready(_) => { - // let values: Vec<_> = me.values.drain(..).collect(); - // if values.is_empty() { - // // delayed flush - // Poll::Ready(( - // StreamResult::Complete(1), - // RustBuffer::new(Vec::new(), me.writer._vtable), - // )) - // } else { - // Pin::new(&mut me.writer).start_send(values).unwrap(); - // match Pin::new(&mut me.writer).poll_ready(cx) { - // Poll::Ready(_) => Poll::Ready(( - // StreamResult::Complete(1), - // RustBuffer::new(Vec::new(), me.writer._vtable), - // )), - // Poll::Pending => Poll::Pending, - // } - // } - // } - // Poll::Pending => Poll::Pending, - // } + let me = self.get_mut(); + match Pin::new(&mut me.writer).poll_ready(cx) { + Poll::Ready(_) => { + let mut values = RustBuffer::new(Vec::new()); + if me.values.remaining() == 0 { + // delayed flush + std::mem::swap(&mut me.values, &mut values); + Poll::Ready((StreamResult::Complete(0), values)) + } else { + Pin::new(&mut me.writer).start_send(Vec::new()).unwrap(); + match Pin::new(&mut me.writer).poll_ready(cx) { + Poll::Ready(_) => { + std::mem::swap(&mut me.values, &mut values); + Poll::Ready((StreamResult::Complete(0), values)) + } + Poll::Pending => Poll::Pending, + } + } + } + Poll::Pending => Poll::Pending, + } } } -impl<'a, T: 'static> StreamWrite<'a, T> { - fn pin_project(self: Pin<&mut Self>) -> Pin<&mut WaitableOperation>> { - // SAFETY: we've chosen that when `Self` is pinned that it translates to - // always pinning the inner field, so that's codified here. - unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().op) } - } - - pub fn cancel(self: Pin<&mut Self>) -> (StreamResult, RustBuffer) { - self.pin_project().cancel() - } -} pub struct StreamWriter { handle: Stream, /*?*/ future: Option + 'static + Send>>>, _vtable: &'static StreamVtable, - done: bool, } impl StreamWriter { @@ -222,7 +98,6 @@ impl StreamWriter { handle, future: None, _vtable: vtable, - done: false, } } @@ -238,7 +113,10 @@ impl StreamWriter { pub fn write_buf(&mut self, values: RustBuffer) -> StreamWrite<'_, T> { StreamWrite { - op: WaitableOperation::new((self, values)), + writer: self, + _future: None, + _phantom: PhantomData, + values, } } diff --git a/tests/runtime-async/async/stream-string/runner.rs b/tests/runtime-async/async/stream-string/runner.rs index f0364efc9..8a489396a 100644 --- a/tests/runtime-async/async/stream-string/runner.rs +++ b/tests/runtime-async/async/stream-string/runner.rs @@ -3,21 +3,20 @@ include!(env!("BINDINGS")); use wit_bindgen::rt::async_support; use crate::a::b::the_test::f; -use futures::StreamExt; fn main() { async_support::block_on(async { let mut stream = f(); let result = stream.next().await; - assert_eq!(result, Some(vec![String::from("Hello")])); + assert_eq!(result, Some(String::from("Hello"))); let result = stream.next().await; - assert_eq!(result, Some(vec![String::from("World!")])); + assert_eq!(result, Some(String::from("World!"))); let result = stream.next().await; - assert_eq!(result, Some(vec![String::from("From")])); + assert_eq!(result, Some(String::from("From"))); let result = stream.next().await; - assert_eq!(result, Some(vec![String::from("a")])); + assert_eq!(result, Some(String::from("a"))); let result = stream.next().await; - assert_eq!(result, Some(vec![String::from("stream.")])); + assert_eq!(result, Some(String::from("stream."))); let result = stream.next().await; assert_eq!(result, None); }); From 8602561046d1ef8114d6e142b2d380d3b38257c9 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 22 Jun 2025 19:47:13 +0200 Subject: [PATCH 667/672] make the read start --- .../src/async_support/stream_support.rs | 142 +++++++++--------- 1 file changed, 74 insertions(+), 68 deletions(-) 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 index c7dae872c..6b2b38611 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -84,7 +84,6 @@ impl Future for StreamWrite<'_, T> { } } - pub struct StreamWriter { handle: Stream, /*?*/ future: Option + 'static + Send>>>, @@ -258,20 +257,89 @@ impl StreamReader { } } -impl futures::stream::Stream for StreamReader { - type Item = Vec; +impl StreamReader { + pub async fn next(&mut self) -> Option { + let buf = self.read(Vec::with_capacity(1)).await; + buf.and_then(|mut v| v.pop()) + } +} - fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { - let me = self.get_mut(); +// impl futures::stream::Stream for StreamReader { +// type Item = Vec; + +// fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { +// 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::())) +// .collect::>(); +// 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); +// // TODO: lift +// Some(unsafe { mem::transmute::>, Vec>(buffer0) }) +// } else { +// None +// } +// }) as Pin + 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 Drop for StreamReader { + fn drop(&mut self) { + if self.handle.handle() != 0 { + self.handle.write_ready_activate(); + } + } +} + +pub struct StreamRead<'a, T: 'static> { + // marker: PhantomData<(&'a mut StreamReader, T)>, + buf: Vec, + reader: &'a mut StreamReader, +} + +impl Future for StreamRead<'_, T> { + type Output = Option>; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + // TODO: Check whether WaitableOperation helps here + //self.pin_project().poll_complete(cx) + + // todo!() + + let me2 = self.get_mut(); + let me = &mut me2.reader; if me.future.is_none() { + let mut buffer2 = Vec::new(); + std::mem::swap(&mut buffer2, &mut me2.buf); 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::())) .collect::>(); let address = unsafe { Address::from_handle(buffer0.as_mut_ptr() as usize) }; - let buffer = Buffer::new(address, buffer0.len() as u64); + let buffer = Buffer::new(address, buffer0.capacity() as u64); handle.start_reading(buffer); let subsc = handle.read_ready_subscribe(); subsc.reset(); @@ -280,7 +348,6 @@ impl futures::stream::Stream for StreamReader { if let Some(buffer2) = buffer2 { let count = buffer2.get_size(); buffer0.truncate(count as usize); - // TODO: lift Some(unsafe { mem::transmute::>, Vec>(buffer0) }) } else { None @@ -298,67 +365,6 @@ impl futures::stream::Stream for StreamReader { } } -impl Drop for StreamReader { - fn drop(&mut self) { - if self.handle.handle() != 0 { - self.handle.write_ready_activate(); - } - } -} - -pub struct StreamRead<'a, T: 'static> { - // marker: PhantomData<(&'a mut StreamReader, T)>, - buf: Vec, - reader: &'a mut StreamReader, -} - -impl Future for StreamRead<'_, T> { - type Output = (StreamResult, Vec); - - fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { - // TODO: Check whether WaitableOperation helps here - //self.pin_project().poll_complete(cx) - - todo!() - - // let me2 = self.get_mut(); - // let me = &mut me2.reader; - - // if me.future.is_none() { - // let mut buffer2 = Vec::new(); - // std::mem::swap(&mut buffer2, &mut me2.buf); - // 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::())) - // .collect::>(); - // let address = unsafe { Address::from_handle(buffer0.as_mut_ptr() as usize) }; - // let buffer = Buffer::new(address, buffer0.capacity() 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>(buffer0) }) - // } else { - // None - // } - // }) as Pin + 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<'a, T> StreamRead<'a, T> { // fn pin_project(self: Pin<&mut Self>) -> Pin<&mut WaitableOperation>> { // // SAFETY: we've chosen that when `Self` is pinned that it translates to From 964a9e9c446ef7f821604fd36f15acff4a01d877 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 22 Jun 2025 20:53:43 +0200 Subject: [PATCH 668/672] test doesn't end but the values are passed correctly --- .../src/async_support/rust_buffer.rs | 8 +- .../src/async_support/stream_support.rs | 101 +++++++++++------- 2 files changed, 69 insertions(+), 40 deletions(-) diff --git a/crates/symmetric_executor/rust-client/src/async_support/rust_buffer.rs b/crates/symmetric_executor/rust-client/src/async_support/rust_buffer.rs index ed22541d4..886b0d48c 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/rust_buffer.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/rust_buffer.rs @@ -7,6 +7,10 @@ pub struct RustBuffer { buf: VecDeque, } +// struct BufIterator<'a, T: 'static> { +// buf: &'a mut RustBuffer, +// } + impl RustBuffer { pub(crate) fn new(vec: Vec) -> Self { Self { buf: vec.into() } @@ -16,7 +20,7 @@ impl RustBuffer { self.buf.len() } - pub(crate) fn take_n(&mut self, _n: usize, _f: F) { - todo!() + pub(crate) fn drain_n(&mut self, n: usize) -> impl Iterator + use<'_, T> { + self.buf.drain(0..n) } } 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 index 6b2b38611..44b945309 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -11,8 +11,8 @@ use { fmt, future::Future, iter, - marker::{self, PhantomData}, - mem::{self, MaybeUninit}, + marker::PhantomData, + mem::MaybeUninit, pin::Pin, task::{Context, Poll}, }, @@ -30,9 +30,9 @@ pub struct StreamVtable { //pub dealloc_lists: Option, } -const fn ceiling(x: usize, y: usize) -> usize { - (x / y) + if x % y == 0 { 0 } else { 1 } -} +// const fn ceiling(x: usize, y: usize) -> usize { +// (x / y) + if x % y == 0 { 0 } else { 1 } +// } pub mod results { pub const BLOCKED: isize = -1; @@ -63,20 +63,33 @@ impl Future for StreamWrite<'_, T> { let me = self.get_mut(); match Pin::new(&mut me.writer).poll_ready(cx) { Poll::Ready(_) => { - let mut values = RustBuffer::new(Vec::new()); if me.values.remaining() == 0 { // delayed flush - std::mem::swap(&mut me.values, &mut values); + let values = RustBuffer::new(Vec::new()); + // std::mem::swap(&mut me.values, &mut values); + // I assume EOF + me.writer.handle.finish_writing(None); Poll::Ready((StreamResult::Complete(0), values)) } else { - Pin::new(&mut me.writer).start_send(Vec::new()).unwrap(); - match Pin::new(&mut me.writer).poll_ready(cx) { - Poll::Ready(_) => { - std::mem::swap(&mut me.values, &mut values); - Poll::Ready((StreamResult::Complete(0), values)) + // send data + // Pin::new(&mut me.writer).start_send(Vec::new()).unwrap(); + let buffer = me.writer.handle.start_writing(); + let addr = buffer.get_address().take_handle() as *mut u8; + let size = (buffer.capacity() as usize).min(me.values.remaining()); + let mut dest = addr; + if let Some(lower) = me.writer._vtable.lower { + for i in me.values.drain_n(size) { + unsafe { (lower)(i, dest) }; + dest = unsafe { dest.byte_add(me.writer._vtable.layout.size()) }; } - Poll::Pending => Poll::Pending, + } else { + todo!(); } + buffer.set_size(size as u64); + me.writer.handle.finish_writing(Some(buffer)); + let mut values = RustBuffer::new(Vec::new()); + std::mem::swap(&mut me.values, &mut values); + Poll::Ready((StreamResult::Complete(size), values)) } } Poll::Pending => Poll::Pending, @@ -164,23 +177,24 @@ impl Sink> for StreamWriter { } } - fn start_send(self: Pin<&mut Self>, mut item: Vec) -> 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::>(), item_len) }; - for (a, b) in slice.iter_mut().zip(item.drain(..)) { - // TODO: lower - a.write(b); - } - buffer.set_size(item_len as u64); - stream.finish_writing(Some(buffer)); - Ok(()) + fn start_send(self: Pin<&mut Self>, _item: Vec) -> Result<(), Self::Error> { + todo!(); + // 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::>(), item_len) }; + // for (a, b) in slice.iter_mut().zip(item.drain(..)) { + // // TODO: lower + // 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> { @@ -334,21 +348,32 @@ impl Future for StreamRead<'_, T> { let mut buffer2 = Vec::new(); std::mem::swap(&mut buffer2, &mut me2.buf); let handle = me.handle.clone(); + let vtable = me._vtable; me.future = Some(Box::pin(async move { - let mut buffer0 = iter::repeat_with(MaybeUninit::uninit) - .take(ceiling(4 * 1024, mem::size_of::())) + let mut buffer0: Vec> = iter::repeat_with(MaybeUninit::uninit) + .take(vtable.layout.size() * buffer2.capacity()) .collect::>(); let address = unsafe { Address::from_handle(buffer0.as_mut_ptr() as usize) }; - let buffer = Buffer::new(address, buffer0.capacity() as u64); + let buffer = Buffer::new(address, buffer2.capacity() 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>(buffer0) }) + let buffer3 = handle.read_result(); + // let mut srcptr = buffer0.as_mut_ptr(); + if let Some(buffer3) = buffer3 { + let count = buffer3.get_size(); + let mut srcptr = buffer3.get_address().take_handle() as *mut u8; + if let Some(lift) = vtable.lift { + for _ in 0..count { + buffer2.push(unsafe { (lift)(srcptr) }); + srcptr = unsafe { srcptr.byte_add(vtable.layout.size()) }; + } + } else { + todo!() + } + //buffer0.truncate(count as usize); + Some(buffer2) } else { None } From 4a763cfc96ac3a607197abaa46cf6af75469b419 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 22 Jun 2025 21:38:48 +0200 Subject: [PATCH 669/672] fully operational Rust string streams --- .../rust-client/src/async_support/stream_support.rs | 3 +++ crates/symmetric_executor/symmetric_stream/src/lib.rs | 2 ++ 2 files changed, 5 insertions(+) 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 index 44b945309..2f347b74f 100644 --- a/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs +++ b/crates/symmetric_executor/rust-client/src/async_support/stream_support.rs @@ -345,6 +345,9 @@ impl Future for StreamRead<'_, T> { let me = &mut me2.reader; if me.future.is_none() { + if me.handle.is_write_closed() { + return Poll::Ready(None); + } let mut buffer2 = Vec::new(); std::mem::swap(&mut buffer2, &mut me2.buf); let handle = me.handle.clone(); diff --git a/crates/symmetric_executor/symmetric_stream/src/lib.rs b/crates/symmetric_executor/symmetric_stream/src/lib.rs index 16c21e1c1..6c750f0a2 100644 --- a/crates/symmetric_executor/symmetric_stream/src/lib.rs +++ b/crates/symmetric_executor/symmetric_stream/src/lib.rs @@ -168,6 +168,8 @@ impl GuestStreamObj for StreamObj { } if !self.0.ready_addr.load(Ordering::Relaxed).is_null() { self.0.write_closed.store(true, Ordering::Release); + #[cfg(feature = "trace")] + println!("Stream::finish_write CLOSE {:x}", self.0.read_ready_event_send.handle()); return; } (0, EOF_MARKER as usize as *mut ()) From b65b5de659f3f2800ee1045f3a0bafa5156d6016 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 22 Jun 2025 22:38:06 +0200 Subject: [PATCH 670/672] still some confusion about how the templates are designed but everything is there --- crates/cpp/src/lib.rs | 5 ++++ .../cpp-client/stream_support.h | 4 +-- .../async/stream-string/runner.cpp | 24 ++++++++++++++++ .../async/stream-string/test.cpp | 28 +++++++++++++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 tests/runtime-async/async/stream-string/runner.cpp create mode 100644 tests/runtime-async/async/stream-string/test.cpp diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 3b8b20b41..4e1809fbb 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -84,6 +84,7 @@ struct Includes { needs_wit: bool, needs_memory: bool, needs_future: bool, + needs_stream: bool, } #[derive(Clone)] @@ -412,6 +413,9 @@ impl Cpp { if self.dependencies.needs_future { self.include(""); } + if self.dependencies.needs_stream { + self.include(""); + } if self.dependencies.needs_optional { self.include(""); } @@ -2062,6 +2066,7 @@ impl CppInterfaceGenerator<'_> { + ">" } TypeDefKind::Stream(ty) => { + self.gen.dependencies.needs_stream = true; "wit::stream<".to_string() + &self.optional_type_name(ty.as_ref(), from_namespace, flavor) + ">" diff --git a/crates/symmetric_executor/cpp-client/stream_support.h b/crates/symmetric_executor/cpp-client/stream_support.h index ba2732dfb..31912f36f 100644 --- a/crates/symmetric_executor/cpp-client/stream_support.h +++ b/crates/symmetric_executor/cpp-client/stream_support.h @@ -1,6 +1,6 @@ #pragma once -#ifndef _STREAM_SUPPORT_H -#define _STREAM_SUPPORT_H +#ifndef WIT_STREAM_SUPPORT_H +#define WIT_STREAM_SUPPORT_H #include "module_cpp.h" #include diff --git a/tests/runtime-async/async/stream-string/runner.cpp b/tests/runtime-async/async/stream-string/runner.cpp new file mode 100644 index 000000000..945f08770 --- /dev/null +++ b/tests/runtime-async/async/stream-string/runner.cpp @@ -0,0 +1,24 @@ +#include +#include "module_cpp.h" + +static constexpr uint32_t SIZE = 5; +static const char *(expected)[SIZE] = { + "Hello", "World!", "From", "a", "stream." +}; +static uint32_t next = 0; + +int main() { + wit::stream stream = a::b::the_test::F(); + stream.buffering(1); + + std::move(stream).set_reader(|streampointer|(wit::span data) { + if data.size() > 0 { + assert!(data.size()==1); + assert!(next +#include +#include + +wit::stream exports::a::b::the_test::F() { + auto streampair = create_wasi_stream(); +#if 1 + stream_writer* streampointer = std::make_unique>(std::move(std::move(streampair).first)).release(); + + std::async(std::launch::async, [streampointer](){ + streampointer->write(std::vector(1, wit::string::from_view("Hello"))); + streampointer->write(std::vector(1, wit::string::from_view("Hello"))); + streampointer->write(std::vector(1, wit::string::from_view("Hello"))); + streampointer->write(std::vector(1, wit::string::from_view("Hello"))); + streampointer->write(std::vector(1, wit::string::from_view("Hello"))); + auto release = std::unique_ptr>(streampointer); + }); +#else + std::vector feed; + feed.push_back(wit::string::from_view("Hello")); + feed.push_back(wit::string::from_view("World!")); + feed.push_back(wit::string::from_view("From")); + feed.push_back(wit::string::from_view("a")); + feed.push_back(wit::string::from_view("stream.")); + streampair.first.write(std::move(feed)); +#endif + return streampair.second; +} From 8aa07a793092db7fa841472d3a5a999f770eb2f6 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 22 Jun 2025 23:35:33 +0200 Subject: [PATCH 671/672] cpp runner doesn't exit with rust test, but everything else works --- crates/cpp/src/lib.rs | 62 +++++++++++++++++-- .../async/stream-string/runner.cpp | 16 +++-- .../async/stream-string/test.cpp | 33 +++++++++- 3 files changed, 98 insertions(+), 13 deletions(-) diff --git a/crates/cpp/src/lib.rs b/crates/cpp/src/lib.rs index 4e1809fbb..e3e1f9814 100644 --- a/crates/cpp/src/lib.rs +++ b/crates/cpp/src/lib.rs @@ -700,7 +700,7 @@ impl WorldGenerator for Cpp { world.name.to_shouty_snake_case(), ); } - if self.dependencies.needs_future { + if self.dependencies.needs_future || self.dependencies.needs_stream { uwriteln!(self.c_src_head, "#include \"async_support.h\"") } self.finish_includes(); @@ -2746,6 +2746,58 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> { uwriteln!(self.r#gen.r#gen.c_src_head, "}};"); format!("Lift{tmpnr}") } + + fn lower_lift_stream(&mut self, payload: Option<&Type>) { + let typestr = self + .gen + .optional_type_name(payload, &self.namespace, Flavor::InStruct); + // let tmpnr = self.r#gen.r#gen.tmp(); + let mut bindgen = FunctionBindgen::new(self.gen, Vec::new()); + let lift = if let Some(ty) = payload { + let res = wit_bindgen_core::abi::lift_from_memory( + bindgen.r#gen.resolve, + &mut bindgen, + "ptr".into(), + ty, + ); + format!("{} return {res};", String::from(bindgen.src)) + } else { + String::new() + }; + let mut bindgen = FunctionBindgen::new(self.gen, Vec::new()); + // GuestImport doesn't leak the objects (string) + bindgen.variant = AbiVariant::GuestExport; + let lower = if let Some(ty) = payload { + wit_bindgen_core::abi::lower_to_memory( + bindgen.r#gen.resolve, + &mut bindgen, + "ptr".into(), + "value".into(), + ty, + ); + String::from(bindgen.src) + } else { + String::new() + }; + let lowered_size = if let Some(ty) = payload { + self.r#gen + .sizes + .size(ty) + .format_term(POINTER_SIZE_EXPRESSION, true) + } else { + String::from("1") + }; + + uwriteln!(self.r#gen.r#gen.c_src_head, "template <> + const uint32_t wit::StreamProperties<{typestr}>::lowered_size = {lowered_size}; + template <> + {typestr} wit::StreamProperties<{typestr}>::lift(uint8_t const*ptr) {{ {lift} }} + template <> + void wit::StreamProperties<{typestr}>::lower({typestr} && value, uint8_t *ptr) {{ {lower} }}" + ); + // uwriteln!(self.r#gen.r#gen.c_src_head, "}};"); + // format!("Lift{tmpnr}") + } } fn move_if_necessary(arg: &str) -> String { @@ -3891,9 +3943,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { )); } abi::Instruction::StreamLower { payload, .. } => { - let lower_lift = self.lower_lift(payload.as_ref()); + self.lower_lift_stream(payload.as_ref()); results.push(format!( - "lower_stream<{}, {lower_lift}>(std::move({}))", + "lower_stream<{}>(std::move({}))", self.gen.optional_type_name( payload.as_ref(), &self.namespace, @@ -3903,9 +3955,9 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> { )); } abi::Instruction::StreamLift { payload, .. } => { - let lower_lift = self.lower_lift(payload.as_ref()); + self.lower_lift_stream(payload.as_ref()); results.push(format!( - "lift_stream<{}, {lower_lift}>({})", + "lift_stream<{}>({})", self.gen.optional_type_name( payload.as_ref(), &self.namespace, diff --git a/tests/runtime-async/async/stream-string/runner.cpp b/tests/runtime-async/async/stream-string/runner.cpp index 945f08770..3aaad209f 100644 --- a/tests/runtime-async/async/stream-string/runner.cpp +++ b/tests/runtime-async/async/stream-string/runner.cpp @@ -1,5 +1,6 @@ #include #include "module_cpp.h" +#include static constexpr uint32_t SIZE = 5; static const char *(expected)[SIZE] = { @@ -7,15 +8,20 @@ static const char *(expected)[SIZE] = { }; static uint32_t next = 0; +static bool equal(wit::string const& a, char const* b) { + if (a.size()!=strlen(b)) return false; + return !memcmp(a.data(), b, a.size()); +} + int main() { wit::stream stream = a::b::the_test::F(); stream.buffering(1); - std::move(stream).set_reader(|streampointer|(wit::span data) { - if data.size() > 0 { - assert!(data.size()==1); - assert!(next data) { + if (data.size() > 0) { + assert(data.size()==1); + assert(next +#include #include #include +static constexpr uint32_t SIZE = 5; +static const char *(pattern)[SIZE] = { + "Hello", "World!", "From", "a", "stream." +}; +static uint32_t next = 0; + +static symmetric::runtime::symmetric_executor::CallbackState ready_to_write(stream_writer* data) { + if (nextwrite(std::vector(1, wit::string::from_view(pattern[next]))); + ++next; + return symmetric::runtime::symmetric_executor::CallbackState::kPending; + } else { + data->write(std::vector()); + auto release = std::unique_ptr>(data); + return symmetric::runtime::symmetric_executor::CallbackState::kReady; + } +} + wit::stream exports::a::b::the_test::F() { auto streampair = create_wasi_stream(); -#if 1 +#if 0 stream_writer* streampointer = std::make_unique>(std::move(std::move(streampair).first)).release(); std::async(std::launch::async, [streampointer](){ @@ -15,6 +33,14 @@ wit::stream exports::a::b::the_test::F() { streampointer->write(std::vector(1, wit::string::from_view("Hello"))); auto release = std::unique_ptr>(streampointer); }); + // TODO: How to handle the returned future +#elif 1 + stream_writer* streampointer = std::make_unique>(std::move(std::move(streampair).first)).release(); + // manual handling for now + symmetric::runtime::symmetric_executor::Register(streampointer->handle.WriteReadySubscribe(), + symmetric::runtime::symmetric_executor::CallbackFunction(wit::ResourceImportBase{(wit::ResourceImportBase::handle_t)&ready_to_write}), + symmetric::runtime::symmetric_executor::CallbackData(wit::ResourceImportBase{(wit::ResourceImportBase::handle_t)streampointer}) + ); #else std::vector feed; feed.push_back(wit::string::from_view("Hello")); @@ -23,6 +49,7 @@ wit::stream exports::a::b::the_test::F() { feed.push_back(wit::string::from_view("a")); feed.push_back(wit::string::from_view("stream.")); streampair.first.write(std::move(feed)); + // TODO: Blocking doesn't work well in this test #endif - return streampair.second; + return std::move(streampair).second; } From 80b467f29868506f54b1457337633dfd885c3934 Mon Sep 17 00:00:00 2001 From: Christof Petig Date: Sun, 22 Jun 2025 23:46:56 +0200 Subject: [PATCH 672/672] proper EOF handling in C++ --- crates/symmetric_executor/cpp-client/stream_support.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/symmetric_executor/cpp-client/stream_support.h b/crates/symmetric_executor/cpp-client/stream_support.h index 31912f36f..871fc54a3 100644 --- a/crates/symmetric_executor/cpp-client/stream_support.h +++ b/crates/symmetric_executor/cpp-client/stream_support.h @@ -65,8 +65,15 @@ namespace wit { } if (size>0) data->reader(wit::span(lifted.data(), size)); - data->handle.StartReading(std::move(*buffer)); - return symmetric::runtime::symmetric_executor::CallbackState::kPending; + // if closed we won't get another notification + if (data->handle.IsWriteClosed()) { + data->reader(wit::span(nullptr, 0)); + auto release = std::unique_ptr(data); + return symmetric::runtime::symmetric_executor::CallbackState::kReady; + } else { + data->handle.StartReading(std::move(*buffer)); + return symmetric::runtime::symmetric_executor::CallbackState::kPending; + } } else { data->reader(wit::span(nullptr, 0)); auto release = std::unique_ptr(data);