-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add shared_fn
macro
#55
Changes from 5 commits
4311703
a8afd95
3bfc192
3d78015
355e45a
70d1862
53b1a0e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
use proc_macro2::{Ident, Span}; | ||
use quote::quote; | ||
use syn::{parse_macro_input, ItemFn, ItemForeignMod}; | ||
use syn::{parse_macro_input, FnArg, ItemFn, ItemForeignMod}; | ||
|
||
/// `plugin_fn` is used to define a function that will be exported by a plugin | ||
/// `plugin_fn` is used to define an Extism callable function to export | ||
/// | ||
/// It should be added to a function you would like to export, the function should | ||
/// accept a parameter that implements `extism_pdk::FromBytes` and return a | ||
|
@@ -103,7 +104,112 @@ pub fn plugin_fn( | |
} | ||
} | ||
|
||
/// `host_fn` is used to define a host function that will be callable from within a plugin | ||
/// `export_fn` is used to define a function that will be exported by a plugin, but is not directly | ||
/// callable by an Extism runtime. These functions can be used for runtime linking and mocking host | ||
/// functions for tests. If direct access to Wasm native parameters is needed, then a bare | ||
/// `extern "C" fn` should be used instead. | ||
/// | ||
/// All arguments should implement `extism_pdk::ToBytes` and the return value should implement | ||
/// `extism_pdk::FromBytes` | ||
#[proc_macro_attribute] | ||
pub fn export_fn( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a little unsure of name. The docs make it clear. But it's hard to tell which to use just from the name. I can't think of a better name though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, agreed that although the docs make it clear, the name is a little ambiguous. What about something like I think my main concern is that an Extism user, with even rudimentary knowledge of Wasm is going to see There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like |
||
_attr: proc_macro::TokenStream, | ||
item: proc_macro::TokenStream, | ||
) -> proc_macro::TokenStream { | ||
let mut function = parse_macro_input!(item as ItemFn); | ||
|
||
if !matches!(function.vis, syn::Visibility::Public(..)) { | ||
panic!("extism_pdk::export_fn expects a public function"); | ||
} | ||
|
||
let name = &function.sig.ident; | ||
let constness = &function.sig.constness; | ||
let unsafety = &function.sig.unsafety; | ||
let generics = &function.sig.generics; | ||
let inputs = &mut function.sig.inputs; | ||
let output = &mut function.sig.output; | ||
let block = &function.block; | ||
|
||
let (raw_inputs, raw_args): (Vec<_>, Vec<_>) = inputs | ||
.iter() | ||
.enumerate() | ||
.map(|(i, x)| { | ||
let t = match x { | ||
FnArg::Receiver(_) => { | ||
panic!("Receiver argument (self) cannot be used in extism_pdk::export_fn") | ||
} | ||
FnArg::Typed(t) => &t.ty, | ||
}; | ||
let arg = Ident::new(&format!("arg{i}"), Span::call_site()); | ||
( | ||
quote! { #arg: extism_pdk::MemoryPointer<#t> }, | ||
quote! { #arg.get()? }, | ||
) | ||
}) | ||
.unzip(); | ||
|
||
if name == "main" { | ||
panic!( | ||
"export_pdk::export_fn must not be applied to a `main` function. To fix, rename this to something other than `main`." | ||
) | ||
} | ||
|
||
let (no_result, raw_output) = match output { | ||
syn::ReturnType::Default => (true, quote! {}), | ||
syn::ReturnType::Type(_, t) => { | ||
if let syn::Type::Path(p) = t.as_ref() { | ||
if let Some(t) = p.path.segments.last() { | ||
if t.ident != "ExportResult" { | ||
panic!("extism_pdk::export_fn expects a function that returns extism_pdk::ExportResult"); | ||
} | ||
} else { | ||
panic!("extism_pdk::export_fn expects a function that returns extism_pdk::ExportResult"); | ||
} | ||
}; | ||
(false, quote! {-> u64 }) | ||
} | ||
}; | ||
|
||
if no_result { | ||
quote! { | ||
#[no_mangle] | ||
pub #constness #unsafety extern "C" fn #name(#(#raw_inputs,)*) { | ||
#constness #unsafety fn inner #generics(#inputs) -> extism_pdk::ExportResult<()> { | ||
#block | ||
} | ||
|
||
|
||
let r = || inner(#(#raw_args,)*); | ||
if let Err(rc) = r() { | ||
panic!("{}", rc.to_string()); | ||
} | ||
} | ||
} | ||
.into() | ||
} else { | ||
quote! { | ||
#[no_mangle] | ||
pub #constness #unsafety extern "C" fn #name(#(#raw_inputs,)*) #raw_output { | ||
#constness #unsafety fn inner #generics(#inputs) #output { | ||
#block | ||
} | ||
|
||
let r = || inner(#(#raw_args,)*); | ||
match r().and_then(|x| extism_pdk::Memory::new(&x)) { | ||
Ok(mem) => { | ||
mem.offset() | ||
}, | ||
Err(rc) => { | ||
panic!("{}", rc.to_string()); | ||
} | ||
} | ||
} | ||
} | ||
.into() | ||
} | ||
} | ||
|
||
/// `host_fn` is used to import a host function from an `extern` block | ||
#[proc_macro_attribute] | ||
pub fn host_fn( | ||
attr: proc_macro::TokenStream, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#![no_main] | ||
|
||
use extism_pdk::*; | ||
|
||
#[export_fn] | ||
pub fn host_reflect(input: String) -> ExportResult<Vec<u8>> { | ||
Ok(input.to_lowercase().into_bytes()) | ||
} | ||
|
||
#[export_fn] | ||
pub fn nothing() -> ExportResult<()> { | ||
Ok(()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe ass an example of this here