├── .gitignore ├── docs ├── examples │ ├── guest │ │ ├── .cargo │ │ │ └── config.toml │ │ ├── Cargo.toml │ │ └── src │ │ │ └── lib.rs │ ├── README.md │ └── host │ │ ├── Cargo.toml │ │ └── src │ │ └── main.rs └── README.md ├── rustfmt.toml ├── crates ├── host │ ├── tests │ │ ├── guest.wasm │ │ └── runtime.rs │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ ├── func.rs │ │ └── runtime.rs ├── guest │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── guest_macro │ ├── Cargo.toml │ └── src │ │ └── lib.rs └── host_macro │ ├── Cargo.toml │ └── src │ └── lib.rs ├── Cargo.toml ├── .github └── workflows │ └── rust.yml ├── LICENSE-MIT └── UNLICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /docs/examples/guest/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "wasm32-unknown-unknown" -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | use_field_init_shorthand = true 2 | newline_style = "Unix" 3 | hard_tabs = true 4 | max_width = 150 -------------------------------------------------------------------------------- /crates/host/tests/guest.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Heraclito-Q-Saldanha/wasmtime_serde/HEAD/crates/host/tests/guest.wasm -------------------------------------------------------------------------------- /docs/examples/README.md: -------------------------------------------------------------------------------- 1 | # examples 2 | 3 | add wasm target 4 | 5 | ``` 6 | rustup target add wasm32-unknown-unknown 7 | ``` 8 | 9 | compile guest 10 | ``` 11 | (cd guest; cargo build) 12 | ``` 13 | 14 | compile and run host 15 | ``` 16 | (cd host; cargo run) 17 | ``` -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "crates/guest", 4 | "crates/guest_macro", 5 | "crates/host", 6 | "crates/host_macro" 7 | ] 8 | exclude = [ 9 | "docs/examples/guest", 10 | "docs/examples/host" 11 | ] 12 | 13 | [profile.release] 14 | opt-level = "z" 15 | lto = true 16 | strip = true 17 | -------------------------------------------------------------------------------- /docs/examples/host/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "host" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "Unlicense OR MIT" 6 | 7 | [dependencies] 8 | wasmtime_serde_host = {path = "../../../crates/host"} 9 | serde = {version = "1.0.159", features = ["derive"]} 10 | 11 | [profile.release] 12 | opt-level = "z" 13 | lto = true 14 | strip = true -------------------------------------------------------------------------------- /docs/examples/guest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "guest" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "Unlicense OR MIT" 6 | 7 | [lib] 8 | crate-type = ['cdylib'] 9 | 10 | [dependencies] 11 | wasmtime_serde_guest = {path = "../../../crates/guest"} 12 | serde = {version = "1.0.159", features = ["derive"]} 13 | 14 | [profile.release] 15 | opt-level = "z" 16 | lto = true 17 | strip = true -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /docs/examples/guest/src/lib.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use wasmtime_serde_guest::*; 3 | 4 | #[derive(Debug, Deserialize, Serialize)] 5 | struct Human { 6 | name: String, 7 | age: u8, 8 | } 9 | 10 | #[export_fn] 11 | fn add(a: i32, b: i32) -> i32 { 12 | let human = get_human(); 13 | println(format!("{human:?}")); 14 | a + b 15 | } 16 | 17 | import_fn!( 18 | fn get_human() -> Human; 19 | fn println(msg: String); 20 | ); 21 | -------------------------------------------------------------------------------- /crates/guest/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasmtime_serde_guest" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "Unlicense OR MIT" 6 | authors = ["Heráclito "] 7 | description = "Simple library for serializing complex types to the wasmtime runtime using serde" 8 | repository = "https://github.com/Heraclito-Q-Saldanha/wasmtime_serde" 9 | 10 | [dependencies] 11 | serde = "1.0.163" 12 | bincode = "1.3.3" 13 | wasmtime_serde_guest_macro = "0.1.0" -------------------------------------------------------------------------------- /crates/guest_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasmtime_serde_guest_macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "Unlicense OR MIT" 6 | authors = ["Heráclito "] 7 | description = "Simple library for serializing complex types to the wasmtime runtime using serde" 8 | repository = "https://github.com/Heraclito-Q-Saldanha/wasmtime_serde" 9 | 10 | [lib] 11 | proc-macro = true 12 | 13 | [dependencies] 14 | syn = {version = "2.0.18", features = ["full"] } 15 | quote = "1.0.28" -------------------------------------------------------------------------------- /crates/host_macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasmtime_serde_host_macro" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "Unlicense OR MIT" 6 | authors = ["Heráclito "] 7 | description = "Simple library for serializing complex types to the wasmtime runtime using serde" 8 | repository = "https://github.com/Heraclito-Q-Saldanha/wasmtime_serde" 9 | 10 | [lib] 11 | proc-macro = true 12 | 13 | [dependencies] 14 | syn = {version = "2.0.18", features = ["full"] } 15 | proc-macro2 = "1.0.59" 16 | quote = "1.0.28" -------------------------------------------------------------------------------- /crates/host/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasmtime_serde_host" 3 | version = "0.1.3" 4 | edition = "2021" 5 | license = "Unlicense OR MIT" 6 | authors = ["Heráclito "] 7 | description = "Simple library for serializing complex types to the wasmtime runtime using serde" 8 | repository = "https://github.com/Heraclito-Q-Saldanha/wasmtime_serde" 9 | 10 | [dependencies] 11 | wasmtime_serde_host_macro = "0.1.0" 12 | wasmtime = {version = "9.0.2", default-features = false, features = ["cranelift"]} 13 | serde = "1.0.163" 14 | bincode = "1.3.3" 15 | anyhow = "1.0.71" -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # wasmtime serde 2 | Simple library for serializing complex types to the wasmtime runtime using serde 3 | 4 | ### using 5 | 6 | ```Rust 7 | // guest 8 | use wasmtime_serde_guest::*; 9 | 10 | #[export_fn] 11 | fn add(a: i32, b: i32) -> i32 { 12 | a + b 13 | } 14 | 15 | // host 16 | use wasmtime_serde_host::*; 17 | 18 | fn main(){ 19 | let runtime = Runtime::from_file("file.wasm", &[]).unwrap(); 20 | let add_fn = runtime.get_func::<(i32, i32), i32>("add").unwrap(); 21 | let result = add_fn.call(&(1, 2)); 22 | println!("{result}"); 23 | } 24 | 25 | ``` 26 | 27 | See the [example code](examples) 28 | 29 | Dual-licensed under [MIT](../LICENSE-MIT) or the [UNLICENSE](../UNLICENSE). -------------------------------------------------------------------------------- /docs/examples/host/src/main.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use wasmtime_serde_host::*; 3 | 4 | #[derive(Debug, Deserialize, Serialize)] 5 | struct Human { 6 | name: String, 7 | age: u8, 8 | } 9 | 10 | #[export_fn] 11 | fn get_human() -> Human { 12 | Human { 13 | name: "Ferros".to_string(), 14 | age: 192, 15 | } 16 | } 17 | 18 | #[export_fn] 19 | fn println(msg: String) { 20 | println!("{msg}") 21 | } 22 | 23 | fn main() { 24 | let host_fns = host_funcs![println, get_human]; 25 | let runtime = Runtime::from_file("../guest/target/wasm32-unknown-unknown/debug/guest.wasm", host_fns).unwrap(); 26 | let add_fn = runtime.get_func::<(i32, i32), i32>("add").unwrap(); 27 | let result = add_fn.call(&(1, 2)); 28 | println!("{result}"); 29 | } 30 | -------------------------------------------------------------------------------- /crates/host/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Simple library for serializing complex types to the wasmtime runtime using serde 2 | 3 | mod func; 4 | mod runtime; 5 | 6 | pub use bincode::{deserialize, serialize}; 7 | pub use func::*; 8 | pub use runtime::*; 9 | pub use wasmtime_serde_host_macro::*; 10 | 11 | #[inline(always)] 12 | const fn from_bitwise(value: u64) -> (u32, u32) { 13 | ((value << 32 >> 32) as u32, (value >> 32) as u32) 14 | } 15 | 16 | #[inline(always)] 17 | const fn into_bitwise(a: u32, b: u32) -> u64 { 18 | (a as u64) | (b as u64) << 32 19 | } 20 | 21 | #[cfg(test)] 22 | mod test { 23 | use crate::*; 24 | 25 | #[test] 26 | fn bitwise() { 27 | const DATA: (u32, u32) = (10, 20); 28 | const INTO: u64 = into_bitwise(DATA.0, DATA.1); 29 | const FROM: (u32, u32) = from_bitwise(INTO); 30 | assert_eq!(DATA, FROM) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /crates/host/tests/runtime.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod test { 3 | use wasmtime_serde_host::*; 4 | const GUEST_DATA: &[u8] = include_bytes!("guest.wasm"); 5 | 6 | #[test] 7 | fn load_runtime() { 8 | assert!(Runtime::new(GUEST_DATA, &[]).is_ok()) 9 | } 10 | 11 | #[test] 12 | fn get_func() { 13 | let runtime = Runtime::new(GUEST_DATA, &[]).unwrap(); 14 | assert!(runtime.get_func::<(i32, i32), i32>("add").is_ok()) 15 | } 16 | 17 | #[test] 18 | fn call() { 19 | let runtime = Runtime::new(GUEST_DATA, &[]).unwrap(); 20 | let add_fn = runtime.get_func::<(i32, i32), i32>("add").unwrap(); 21 | let result = add_fn.call(&(10, 10)); 22 | assert_eq!(result, 20) 23 | } 24 | 25 | #[test] 26 | fn checked_call() { 27 | let runtime = Runtime::new(GUEST_DATA, &[]).unwrap(); 28 | let panic_fn = runtime.get_func::<(), ()>("panic").unwrap(); 29 | let result = panic_fn.checked_call(&()); 30 | assert!(result.is_err()) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Heraclito-Q-Saldanha 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /crates/host/src/func.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::{cell::RefCell, rc::Rc}; 3 | 4 | pub struct Func { 5 | pub(crate) wasm_fn: wasmtime::TypedFunc, 6 | pub(crate) store: Rc>>>, 7 | pub(crate) par: std::marker::PhantomData

, 8 | pub(crate) rtn: std::marker::PhantomData, 9 | } 10 | 11 | impl Func { 12 | /// a more ergonomic version of the check_call function, which panic if it fails, using an analogy to an array, if checked_call were array.get(i), call would be array\[i\] 13 | pub fn call(&self, value: &P) -> R { 14 | self.checked_call(value).unwrap() 15 | } 16 | /// fail if the function in the guest panic and does not return 17 | pub fn checked_call(&self, value: &P) -> anyhow::Result { 18 | let RuntimeCaller { memory, alloc_fn, .. } = self.store.borrow().data().unwrap(); 19 | let buffer = serialize(value)?; 20 | let len = buffer.len() as _; 21 | let ptr = alloc_fn.call(&mut *self.store.borrow_mut(), len)?; 22 | memory.write(&mut *self.store.borrow_mut(), ptr as _, &buffer)?; 23 | let ptr = self.wasm_fn.call(&mut *self.store.borrow_mut(), into_bitwise(ptr, len))?; 24 | let (ptr, len) = from_bitwise(ptr); 25 | let mut buffer = vec![0u8; len as _]; 26 | memory.read(&*self.store.borrow(), ptr as _, &mut buffer)?; 27 | Ok(deserialize(&buffer)?) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /crates/guest/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Simple library for serializing complex types to the wasmtime runtime using serde 2 | 3 | pub use bincode::{deserialize, serialize}; 4 | pub use wasmtime_serde_guest_macro::*; 5 | 6 | #[inline] 7 | #[no_mangle] 8 | pub extern "C" fn alloc(len: u32) -> *mut u8 { 9 | let mut buf = Vec::with_capacity(len as _); 10 | let ptr = buf.as_mut_ptr(); 11 | std::mem::forget(buf); 12 | return ptr; 13 | } 14 | 15 | #[inline] 16 | #[no_mangle] 17 | pub unsafe extern "C" fn dealloc(value: u64) { 18 | let (ptr, len) = from_bitwise(value); 19 | let ptr = std::mem::transmute::(ptr as _); 20 | let buffer = Vec::from_raw_parts(ptr, len as _, len as _); 21 | std::mem::drop(buffer); 22 | } 23 | 24 | pub fn write_msg(value: &T) -> u64 { 25 | let mut buffer = bincode::serialize(value).unwrap(); 26 | let len = buffer.len(); 27 | let ptr = buffer.as_mut_ptr(); 28 | std::mem::forget(buffer); 29 | into_bitwise(ptr as _, len as _) 30 | } 31 | 32 | pub unsafe fn read_msg(value: u64) -> T { 33 | let (ptr, len) = from_bitwise(value); 34 | let ptr = std::mem::transmute::(ptr as _); 35 | let buffer = Vec::from_raw_parts(ptr, len as _, len as _); 36 | bincode::deserialize(&buffer).unwrap() 37 | } 38 | 39 | #[inline(always)] 40 | const fn from_bitwise(value: u64) -> (u32, u32) { 41 | ((value << 32 >> 32) as u32, (value >> 32) as u32) 42 | } 43 | 44 | #[inline(always)] 45 | const fn into_bitwise(a: u32, b: u32) -> u64 { 46 | (a as u64) | (b as u64) << 32 47 | } 48 | 49 | #[cfg(test)] 50 | mod test { 51 | use crate::*; 52 | 53 | #[test] 54 | fn bitwise() { 55 | const DATA: (u32, u32) = (10, 20); 56 | const INTO: u64 = into_bitwise(DATA.0, DATA.1); 57 | const FROM: (u32, u32) = from_bitwise(INTO); 58 | assert_eq!(DATA, FROM) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /crates/host_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Simple library for serializing complex types to the wasmtime runtime using serde 2 | 3 | use proc_macro::TokenStream; 4 | use quote::{format_ident, quote}; 5 | use syn::{parse_macro_input, ItemFn}; 6 | 7 | struct FnHost { 8 | functions: Vec, 9 | } 10 | 11 | impl syn::parse::Parse for FnHost { 12 | fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { 13 | let mut functions = vec![]; 14 | let mut end = false; 15 | while let Ok(f) = input.parse::() { 16 | if end { 17 | panic!("comma") 18 | } 19 | functions.push(f); 20 | end = input.parse::().is_err(); 21 | } 22 | Ok(Self { functions }) 23 | } 24 | } 25 | 26 | #[proc_macro] 27 | pub fn host_funcs(input: TokenStream) -> TokenStream { 28 | let input = parse_macro_input!(input as FnHost); 29 | let mut list = quote!(); 30 | let len = input.functions.len(); 31 | for name in input.functions { 32 | let name = format_ident!("_wasm_host_{}", name); 33 | let str_name: proc_macro2::TokenStream = format!(r#""{name}""#).parse().unwrap(); 34 | list = quote!(#list (#str_name, #name),); 35 | } 36 | quote!({ 37 | const HOST_FUNC:[(&str, fn(&[u8]) -> Vec);#len] = [#list]; 38 | &HOST_FUNC 39 | }) 40 | .into() 41 | } 42 | 43 | #[proc_macro_attribute] 44 | pub fn export_fn(_attr: TokenStream, item: TokenStream) -> TokenStream { 45 | let data = parse_macro_input!(item as ItemFn); 46 | let name = &data.sig.ident; 47 | let extern_name = format_ident!("_wasm_host_{}", name); 48 | let gen = { 49 | let mut argument_types = quote!(); 50 | let mut call = quote!(); 51 | for (i, arg) in data.sig.inputs.iter().enumerate() { 52 | let i = syn::Index::from(i); 53 | call = quote!(#call message.#i,); 54 | if let syn::FnArg::Typed(t) = arg { 55 | let ty = &t.ty; 56 | argument_types = quote!(#argument_types #ty,); 57 | } else { 58 | panic!(); 59 | } 60 | } 61 | argument_types = quote! { (#argument_types) }; 62 | quote! { 63 | fn #extern_name(value: &[u8]) -> Vec { 64 | let message:#argument_types = wasmtime_serde_host::deserialize(value).unwrap(); 65 | wasmtime_serde_host::serialize(&#name(#call)).unwrap() 66 | } 67 | } 68 | }; 69 | quote!(#gen #data).into() 70 | } 71 | -------------------------------------------------------------------------------- /crates/host/src/runtime.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::{cell::RefCell, rc::Rc}; 3 | 4 | pub struct Runtime { 5 | instance: wasmtime::Instance, 6 | store: Rc>>>, 7 | } 8 | 9 | #[derive(Clone, Copy)] 10 | pub(crate) struct RuntimeCaller { 11 | pub(crate) memory: wasmtime::Memory, 12 | pub(crate) alloc_fn: wasmtime::TypedFunc, 13 | pub(crate) dealloc_fn: wasmtime::TypedFunc, 14 | } 15 | 16 | impl Runtime { 17 | pub fn from_file(file: impl AsRef, imports: &'static [(&'static str, fn(&[u8]) -> Vec)]) -> anyhow::Result { 18 | Self::new(&std::fs::read(&file)?, imports) 19 | } 20 | pub fn new(bytes: impl AsRef<[u8]>, imports: &'static [(&'static str, fn(&[u8]) -> Vec)]) -> anyhow::Result { 21 | let engine = wasmtime::Engine::default(); 22 | let module = wasmtime::Module::new(&engine, bytes)?; 23 | let mut store = wasmtime::Store::new(&engine, None); 24 | let mut linker = wasmtime::Linker::new(&engine); 25 | for (name, callback) in imports { 26 | linker.func_wrap("env", name, |mut caller: wasmtime::Caller>, ptr: u64| -> u64 { 27 | let RuntimeCaller { 28 | memory, 29 | alloc_fn, 30 | dealloc_fn, 31 | } = caller.data().unwrap(); 32 | let (ptr, len) = from_bitwise(ptr); 33 | let mut buffer = vec![0u8; len as _]; 34 | memory.read(&caller, ptr as _, &mut buffer).unwrap(); 35 | dealloc_fn.call(&mut caller, into_bitwise(ptr, len)).unwrap(); 36 | let buffer = (callback)(&buffer); 37 | let ptr = alloc_fn.call(&mut caller, buffer.len() as _).unwrap(); 38 | memory.write(&mut caller, ptr as _, &buffer).unwrap(); 39 | into_bitwise(ptr, buffer.len() as _) 40 | })?; 41 | } 42 | let instance = linker.instantiate(&mut store, &module)?; 43 | let memory = instance.get_memory(&mut store, "memory").unwrap(); 44 | let alloc_fn = instance.get_typed_func(&mut store, "alloc")?; 45 | let dealloc_fn = instance.get_typed_func(&mut store, "dealloc")?; 46 | *store.data_mut() = Some(RuntimeCaller { 47 | memory, 48 | alloc_fn, 49 | dealloc_fn, 50 | }); 51 | Ok(Self { 52 | instance, 53 | store: Rc::new(RefCell::new(store)), 54 | }) 55 | } 56 | 57 | pub fn get_func(&self, name: &str) -> anyhow::Result> { 58 | let wasm_fn = self 59 | .instance 60 | .get_typed_func::(&mut *self.store.borrow_mut(), &format!("_wasm_guest_{name}"))?; 61 | Ok(Func { 62 | wasm_fn, 63 | store: self.store.clone(), 64 | par: std::marker::PhantomData::

, 65 | rtn: std::marker::PhantomData::, 66 | }) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /crates/guest_macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Simple library for serializing complex types to the wasmtime runtime using serde 2 | 3 | use quote::quote; 4 | 5 | #[proc_macro_attribute] 6 | pub fn export_fn(_attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream { 7 | let data = syn::parse_macro_input!(item as syn::ItemFn); 8 | let name = &data.sig.ident; 9 | let extern_name = quote::format_ident!("_wasm_guest_{}", name); 10 | let gen = { 11 | let mut argument_types = quote!(); 12 | let mut call = quote!(); 13 | for (i, arg) in data.sig.inputs.iter().enumerate() { 14 | let i = syn::Index::from(i); 15 | call = quote!(#call message.#i,); 16 | if let syn::FnArg::Typed(t) = arg { 17 | let ty = &t.ty; 18 | argument_types = quote!(#argument_types #ty,); 19 | } else { 20 | panic!(); 21 | } 22 | } 23 | argument_types = quote! { (#argument_types) }; 24 | quote! { 25 | #[no_mangle] 26 | pub unsafe extern "C" fn #extern_name(value: u64) -> u64 { 27 | let message:#argument_types = wasmtime_serde_guest::read_msg(value); 28 | wasmtime_serde_guest::write_msg(&#name(#call)) 29 | } 30 | } 31 | }; 32 | quote!(#gen #data).into() 33 | } 34 | 35 | struct FnImports { 36 | functions: Vec, 37 | } 38 | 39 | impl syn::parse::Parse for FnImports { 40 | fn parse(input: syn::parse::ParseStream) -> syn::parse::Result { 41 | let mut functions = vec![]; 42 | while let Ok(f) = input.parse::() { 43 | functions.push(f); 44 | input.parse::()?; 45 | } 46 | Ok(FnImports { functions }) 47 | } 48 | } 49 | 50 | #[proc_macro] 51 | pub fn import_fn(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 52 | let mut remote_fns = quote!(); 53 | let mut local_fns = quote!(); 54 | let data = syn::parse_macro_input!(input as FnImports); 55 | for f in data.functions.iter().cloned() { 56 | let remote_name = quote::format_ident!("_wasm_host_{}", f.ident); 57 | let mut inputs = quote!(); 58 | for item in &f.inputs { 59 | if let syn::FnArg::Typed(syn::PatType { pat: p, .. }) = item { 60 | if let syn::Pat::Ident(i) = p.as_ref() { 61 | inputs = quote!(#inputs #i,); 62 | } else { 63 | panic!() 64 | } 65 | } else { 66 | panic!() 67 | } 68 | } 69 | inputs = quote!((#inputs)); 70 | local_fns = quote!( 71 | #local_fns 72 | #f { 73 | let ptr = wasmtime_serde_guest::write_msg(&#inputs); 74 | unsafe{wasmtime_serde_guest::read_msg(#remote_name(ptr))} 75 | } 76 | ); 77 | remote_fns = quote!( 78 | #remote_fns 79 | fn #remote_name(ptr: u64) -> u64; 80 | ); 81 | } 82 | quote! { 83 | #local_fns 84 | extern "C" { 85 | #remote_fns 86 | } 87 | } 88 | .into() 89 | } 90 | --------------------------------------------------------------------------------