diff --git a/crates/plugin_macros/Cargo.toml b/crates/plugin_macros/Cargo.toml index 2d106105ab..1ba3c64831 100644 --- a/crates/plugin_macros/Cargo.toml +++ b/crates/plugin_macros/Cargo.toml @@ -7,7 +7,8 @@ edition = "2021" proc-macro = true [dependencies] -syn = { version = "1.0", features = ["full"] } +# TODO: remove "extra-traits" +syn = { version = "1.0", features = ["full", "extra-traits"] } quote = "1.0" proc-macro2 = "1.0" serde = "1.0" diff --git a/crates/plugin_macros/src/lib.rs b/crates/plugin_macros/src/lib.rs index 6455d99957..154b2f2a83 100644 --- a/crates/plugin_macros/src/lib.rs +++ b/crates/plugin_macros/src/lib.rs @@ -2,7 +2,7 @@ use core::panic; use proc_macro::TokenStream; use quote::{format_ident, quote}; -use syn::{parse_macro_input, FnArg, ItemFn, Type, Visibility}; +use syn::{parse_macro_input, FnArg, ForeignItemFn, ItemFn, Type, Visibility}; #[proc_macro_attribute] pub fn export(args: TokenStream, function: TokenStream) -> TokenStream { @@ -11,6 +11,11 @@ pub fn export(args: TokenStream, function: TokenStream) -> TokenStream { } let inner_fn = parse_macro_input!(function as ItemFn); + + if !inner_fn.sig.generics.params.is_empty() { + panic!("Exported functions can not take generic parameters"); + } + if let Visibility::Public(_) = inner_fn.vis { } else { panic!("The export attribute only works for public functions"); @@ -77,15 +82,27 @@ pub fn export(args: TokenStream, function: TokenStream) -> TokenStream { #[proc_macro_attribute] pub fn import(args: TokenStream, function: TokenStream) -> TokenStream { - todo!() - // if !args.is_empty() { - // panic!("The import attribute does not take any arguments"); - // } + if !args.is_empty() { + panic!("The import attribute does not take any arguments"); + } - // let inner_fn = parse_macro_input!(function as ItemFn); + let fn_declare = parse_macro_input!(function as ForeignItemFn); - // let inner_fn_name = format_ident!("{}", inner_fn.sig.ident); - // // let outer_fn_name = format_ident!("__{}", inner_fn_name); + if !fn_declare.sig.generics.params.is_empty() { + panic!("Exported functions can not take generic parameters"); + } + + dbg!(&fn_declare.sig); + + // let inner_fn = ItemFn { + // attrs: fn_declare.attrs, + // vis: fn_declare.vis, + // sig: fn_declare.sig, + // block: todo!(), + // }; + + // let inner_fn_name = format_ident!("{}", inner_fn.sig.ident); + // let outer_fn_name = format_ident!("__{}", inner_fn_name); // let variadic = inner_fn.sig.inputs.len(); // let i = (0..variadic).map(syn::Index::from); @@ -116,28 +133,35 @@ pub fn import(args: TokenStream, function: TokenStream) -> TokenStream { // (quote! { data }, quote! { #ty }) // }; - // TokenStream::from(quote! { + // TokenStream::from(quote! { + // extern "C" { // #[no_mangle] - // #inner_fn + // fn #outer_fn_name(ptr: *const u8, len: usize) -> *const ::plugin::__Buffer; + // } - // #[no_mangle] - // pub extern "C" fn #outer_fn_name(ptr: *const u8, len: usize) -> *const ::plugin::__Buffer { - // // setup - // let buffer = ::plugin::__Buffer { ptr, len }; - // let data = unsafe { buffer.to_vec() }; + // #[no_mangle] + // fn #inner_fn_name #params -> #output { + // println!("executing command: {}", string); + // // serialize data + // let data = #collect_params; + // let data = ::plugin::bincode::serialize(&data).unwrap(); + // let buffer = unsafe { ::plugin::__Buffer::from_vec(data) }; + // let ptr = buffer.ptr; + // let len = buffer.len; + // // leak data to heap + // buffer.leak_to_heap(); + // // call extern function + // let result = unsafe { __command(ptr, len) }; + // // get result + // let result = todo!(); // convert into box - // // operation - // let data: #ty = match ::plugin::bincode::deserialize(&data) { - // Ok(d) => d, - // Err(e) => panic!("Data passed to function not deserializable."), - // }; - // let result = #inner_fn_name(#args); - // let new_data: Result, _> = ::plugin::bincode::serialize(&result); - // let new_data = new_data.unwrap(); - - // // teardown - // let new_buffer = unsafe { ::plugin::__Buffer::from_vec(new_data) }; - // return new_buffer.leak_to_heap(); - // } - // }) + // // deserialize data + // let data: Option = match ::plugin::bincode::deserialize(&data) { + // Ok(d) => d, + // Err(e) => panic!("Data passed to function not deserializable."), + // }; + // return data; + // } + // }) + todo!() } diff --git a/crates/plugin_runtime/src/lib.rs b/crates/plugin_runtime/src/lib.rs index 460e824da3..109eb9d4e2 100644 --- a/crates/plugin_runtime/src/lib.rs +++ b/crates/plugin_runtime/src/lib.rs @@ -1,2 +1,14 @@ pub mod wasi; pub use wasi::*; + +// #[cfg(test)] +// mod tests { +// use super::*; + +// pub fn init_wasi() { +// let plugin = WasiPluginBuilder::new().init(todo!()).unwrap(); +// let handle: WasiFn = plugin.function("hello").unwrap(); +// let result = plugin.call(handle, 27).unwrap(); +// assert_eq!(result, "world 27"); +// } +// } diff --git a/crates/plugin_runtime/src/wasi.rs b/crates/plugin_runtime/src/wasi.rs index ed1e2149fe..ec4761c80a 100644 --- a/crates/plugin_runtime/src/wasi.rs +++ b/crates/plugin_runtime/src/wasi.rs @@ -72,7 +72,7 @@ pub struct Wasi { pub type HostFunction = Box>; pub struct WasiPluginBuilder { - host_functions: HashMap, + host_functions: HashMap) -> Result<(), Error>>>, wasi_ctx_builder: WasiCtxBuilder, } @@ -90,22 +90,22 @@ impl WasiPluginBuilder { this } - fn wrap_host_function( - function: impl Fn(A) -> R + Send + Sync + 'static, - ) -> HostFunction { - Box::new(move |ptr, len| { - function(todo!()); - todo!() - }) - } - pub fn host_function( mut self, name: &str, - function: impl Fn(A) -> R + Send + Sync + 'static, + function: &dyn Fn(A) -> R + Send + Sync + 'static, ) -> Self { - self.host_functions - .insert(name.to_string(), Self::wrap_host_function(function)); + let name = name.to_string(); + self.host_functions.insert( + name, + Box::new(move |name: &str, linker: &mut Linker| { + linker.func_wrap("env", name, |ptr: u32, len: u32| { + function(todo!()); + 7u32 + })?; + Ok(()) + }), + ); self } @@ -130,7 +130,8 @@ impl WasiPluginBuilder { pub struct WasiPlugin { pub module: Vec, pub wasi_ctx: WasiCtx, - pub host_functions: HashMap, + pub host_functions: + HashMap) -> Result<(), Error>>>, } impl Wasi { @@ -159,6 +160,10 @@ impl Wasi { let engine = Engine::new(&config)?; let mut linker = Linker::new(&engine); + for (name, add_to_linker) in plugin.host_functions.into_iter() { + add_to_linker(&name, &mut linker)?; + } + linker .func_wrap("env", "__command", |x: u32, y: u32| x + y) .unwrap(); diff --git a/plugins/json_language/src/lib.rs b/plugins/json_language/src/lib.rs index 3f2329f5d1..b611579340 100644 --- a/plugins/json_language/src/lib.rs +++ b/plugins/json_language/src/lib.rs @@ -5,12 +5,7 @@ use std::fs; use std::path::PathBuf; // #[import] -// fn command(string: &str) -> Option; - -extern "C" { - #[no_mangle] - fn __command(ptr: *const u8, len: usize) -> *const ::plugin::__Buffer; -} +// fn my_command(string: &str) -> Option; // #[no_mangle] // // TODO: switch len from usize to u32? @@ -33,9 +28,13 @@ extern "C" { // return new_buffer.leak_to_heap(); // } +extern "C" { + fn __command(ptr: *const u8, len: usize) -> *mut ::plugin::__Buffer; +} + #[no_mangle] fn command(string: &str) -> Option { - println!("executing command: {}", string); + dbg!("executing command: {}", string); // serialize data let data = string; let data = ::plugin::bincode::serialize(&data).unwrap(); @@ -47,14 +46,15 @@ fn command(string: &str) -> Option { // call extern function let result = unsafe { __command(ptr, len) }; // get result - let result = todo!(); // convert into box + let new_buffer = unsafe { Box::from_raw(result) }; // convert into box + let new_data = unsafe { new_buffer.to_vec() }; // deserialize data - let data: Option = match ::plugin::bincode::deserialize(&data) { + let new_data: Option = match ::plugin::bincode::deserialize(&new_data) { Ok(d) => d, - Err(e) => panic!("Data passed to function not deserializable."), + Err(e) => panic!("Data returned from function not deserializable."), }; - return data; + return new_data; } // TODO: some sort of macro to generate ABI bindings @@ -81,7 +81,6 @@ const BIN_PATH: &'static str = #[export] pub fn name() -> &'static str { // let number = unsafe { hello(27) }; - // println!("got: {}", number); // let number = unsafe { bye(28) }; // println!("got: {}", number); "vscode-json-languageserver"