Work on plugin builder
This commit is contained in:
parent
53e56f1284
commit
96c2559d2c
5 changed files with 97 additions and 56 deletions
|
@ -7,7 +7,8 @@ edition = "2021"
|
||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
syn = { version = "1.0", features = ["full"] }
|
# TODO: remove "extra-traits"
|
||||||
|
syn = { version = "1.0", features = ["full", "extra-traits"] }
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
|
|
|
@ -2,7 +2,7 @@ use core::panic;
|
||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
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]
|
#[proc_macro_attribute]
|
||||||
pub fn export(args: TokenStream, function: TokenStream) -> TokenStream {
|
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);
|
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 {
|
if let Visibility::Public(_) = inner_fn.vis {
|
||||||
} else {
|
} else {
|
||||||
panic!("The export attribute only works for public functions");
|
panic!("The export attribute only works for public functions");
|
||||||
|
@ -77,15 +82,27 @@ pub fn export(args: TokenStream, function: TokenStream) -> TokenStream {
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn import(args: TokenStream, function: TokenStream) -> TokenStream {
|
pub fn import(args: TokenStream, function: TokenStream) -> TokenStream {
|
||||||
todo!()
|
if !args.is_empty() {
|
||||||
// if !args.is_empty() {
|
panic!("The import attribute does not take any arguments");
|
||||||
// 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);
|
||||||
|
|
||||||
|
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 inner_fn_name = format_ident!("{}", inner_fn.sig.ident);
|
||||||
// // let outer_fn_name = format_ident!("__{}", inner_fn_name);
|
// let outer_fn_name = format_ident!("__{}", inner_fn_name);
|
||||||
|
|
||||||
// let variadic = inner_fn.sig.inputs.len();
|
// let variadic = inner_fn.sig.inputs.len();
|
||||||
// let i = (0..variadic).map(syn::Index::from);
|
// let i = (0..variadic).map(syn::Index::from);
|
||||||
|
@ -117,27 +134,34 @@ pub fn import(args: TokenStream, function: TokenStream) -> TokenStream {
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// TokenStream::from(quote! {
|
// TokenStream::from(quote! {
|
||||||
|
// extern "C" {
|
||||||
// #[no_mangle]
|
// #[no_mangle]
|
||||||
// #inner_fn
|
// fn #outer_fn_name(ptr: *const u8, len: usize) -> *const ::plugin::__Buffer;
|
||||||
|
// }
|
||||||
|
|
||||||
// #[no_mangle]
|
// #[no_mangle]
|
||||||
// pub extern "C" fn #outer_fn_name(ptr: *const u8, len: usize) -> *const ::plugin::__Buffer {
|
// fn #inner_fn_name #params -> #output {
|
||||||
// // setup
|
// println!("executing command: {}", string);
|
||||||
// let buffer = ::plugin::__Buffer { ptr, len };
|
// // serialize data
|
||||||
// let data = unsafe { buffer.to_vec() };
|
// 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
|
// // deserialize data
|
||||||
// let data: #ty = match ::plugin::bincode::deserialize(&data) {
|
// let data: Option<String> = match ::plugin::bincode::deserialize(&data) {
|
||||||
// Ok(d) => d,
|
// Ok(d) => d,
|
||||||
// Err(e) => panic!("Data passed to function not deserializable."),
|
// Err(e) => panic!("Data passed to function not deserializable."),
|
||||||
// };
|
// };
|
||||||
// let result = #inner_fn_name(#args);
|
// return data;
|
||||||
// let new_data: Result<Vec<u8>, _> = ::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();
|
|
||||||
// }
|
// }
|
||||||
// })
|
// })
|
||||||
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,14 @@
|
||||||
pub mod wasi;
|
pub mod wasi;
|
||||||
pub use wasi::*;
|
pub use wasi::*;
|
||||||
|
|
||||||
|
// #[cfg(test)]
|
||||||
|
// mod tests {
|
||||||
|
// use super::*;
|
||||||
|
|
||||||
|
// pub fn init_wasi() {
|
||||||
|
// let plugin = WasiPluginBuilder::new().init(todo!()).unwrap();
|
||||||
|
// let handle: WasiFn<u32, String> = plugin.function("hello").unwrap();
|
||||||
|
// let result = plugin.call(handle, 27).unwrap();
|
||||||
|
// assert_eq!(result, "world 27");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
|
@ -72,7 +72,7 @@ pub struct Wasi {
|
||||||
pub type HostFunction = Box<dyn IntoFunc<WasiCtx, (u32, u32), u32>>;
|
pub type HostFunction = Box<dyn IntoFunc<WasiCtx, (u32, u32), u32>>;
|
||||||
|
|
||||||
pub struct WasiPluginBuilder {
|
pub struct WasiPluginBuilder {
|
||||||
host_functions: HashMap<String, HostFunction>,
|
host_functions: HashMap<String, Box<dyn Fn(&str, &mut Linker<WasiCtx>) -> Result<(), Error>>>,
|
||||||
wasi_ctx_builder: WasiCtxBuilder,
|
wasi_ctx_builder: WasiCtxBuilder,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,22 +90,22 @@ impl WasiPluginBuilder {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_host_function<A: Serialize, R: DeserializeOwned>(
|
|
||||||
function: impl Fn(A) -> R + Send + Sync + 'static,
|
|
||||||
) -> HostFunction {
|
|
||||||
Box::new(move |ptr, len| {
|
|
||||||
function(todo!());
|
|
||||||
todo!()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn host_function<A: Serialize, R: DeserializeOwned>(
|
pub fn host_function<A: Serialize, R: DeserializeOwned>(
|
||||||
mut self,
|
mut self,
|
||||||
name: &str,
|
name: &str,
|
||||||
function: impl Fn(A) -> R + Send + Sync + 'static,
|
function: &dyn Fn(A) -> R + Send + Sync + 'static,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
self.host_functions
|
let name = name.to_string();
|
||||||
.insert(name.to_string(), Self::wrap_host_function(function));
|
self.host_functions.insert(
|
||||||
|
name,
|
||||||
|
Box::new(move |name: &str, linker: &mut Linker<WasiCtx>| {
|
||||||
|
linker.func_wrap("env", name, |ptr: u32, len: u32| {
|
||||||
|
function(todo!());
|
||||||
|
7u32
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}),
|
||||||
|
);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +130,8 @@ impl WasiPluginBuilder {
|
||||||
pub struct WasiPlugin {
|
pub struct WasiPlugin {
|
||||||
pub module: Vec<u8>,
|
pub module: Vec<u8>,
|
||||||
pub wasi_ctx: WasiCtx,
|
pub wasi_ctx: WasiCtx,
|
||||||
pub host_functions: HashMap<String, HostFunction>,
|
pub host_functions:
|
||||||
|
HashMap<String, Box<dyn Fn(&str, &mut Linker<WasiCtx>) -> Result<(), Error>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wasi {
|
impl Wasi {
|
||||||
|
@ -159,6 +160,10 @@ impl Wasi {
|
||||||
let engine = Engine::new(&config)?;
|
let engine = Engine::new(&config)?;
|
||||||
let mut linker = Linker::new(&engine);
|
let mut linker = Linker::new(&engine);
|
||||||
|
|
||||||
|
for (name, add_to_linker) in plugin.host_functions.into_iter() {
|
||||||
|
add_to_linker(&name, &mut linker)?;
|
||||||
|
}
|
||||||
|
|
||||||
linker
|
linker
|
||||||
.func_wrap("env", "__command", |x: u32, y: u32| x + y)
|
.func_wrap("env", "__command", |x: u32, y: u32| x + y)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -5,12 +5,7 @@ use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
// #[import]
|
// #[import]
|
||||||
// fn command(string: &str) -> Option<String>;
|
// fn my_command(string: &str) -> Option<String>;
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#[no_mangle]
|
|
||||||
fn __command(ptr: *const u8, len: usize) -> *const ::plugin::__Buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
// #[no_mangle]
|
// #[no_mangle]
|
||||||
// // TODO: switch len from usize to u32?
|
// // TODO: switch len from usize to u32?
|
||||||
|
@ -33,9 +28,13 @@ extern "C" {
|
||||||
// return new_buffer.leak_to_heap();
|
// return new_buffer.leak_to_heap();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn __command(ptr: *const u8, len: usize) -> *mut ::plugin::__Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
fn command(string: &str) -> Option<String> {
|
fn command(string: &str) -> Option<String> {
|
||||||
println!("executing command: {}", string);
|
dbg!("executing command: {}", string);
|
||||||
// serialize data
|
// serialize data
|
||||||
let data = string;
|
let data = string;
|
||||||
let data = ::plugin::bincode::serialize(&data).unwrap();
|
let data = ::plugin::bincode::serialize(&data).unwrap();
|
||||||
|
@ -47,14 +46,15 @@ fn command(string: &str) -> Option<String> {
|
||||||
// call extern function
|
// call extern function
|
||||||
let result = unsafe { __command(ptr, len) };
|
let result = unsafe { __command(ptr, len) };
|
||||||
// get result
|
// 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
|
// deserialize data
|
||||||
let data: Option<String> = match ::plugin::bincode::deserialize(&data) {
|
let new_data: Option<String> = match ::plugin::bincode::deserialize(&new_data) {
|
||||||
Ok(d) => d,
|
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
|
// TODO: some sort of macro to generate ABI bindings
|
||||||
|
@ -81,7 +81,6 @@ const BIN_PATH: &'static str =
|
||||||
#[export]
|
#[export]
|
||||||
pub fn name() -> &'static str {
|
pub fn name() -> &'static str {
|
||||||
// let number = unsafe { hello(27) };
|
// let number = unsafe { hello(27) };
|
||||||
// println!("got: {}", number);
|
|
||||||
// let number = unsafe { bye(28) };
|
// let number = unsafe { bye(28) };
|
||||||
// println!("got: {}", number);
|
// println!("got: {}", number);
|
||||||
"vscode-json-languageserver"
|
"vscode-json-languageserver"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue