1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
//! Set of macros designed to help creating and implementing WebAssembly APIs and bindings,
//! designed to run in the Ark environment.
//!
//! See also the [private Ark
//! documentation](https://ark.embark.dev/internals/creating-apis/index.html) for details of how
//! things work, motivation behind this crate, as well as thorough examples of what it can do.
//!
//! # The problem
//!
//! Implementing WebAssembly FFI (foreign function interfaces) by hand is tedious and doesn't
//! allow using idiomatic Rust, like taking or returning non-plain types (user structs, enum, etc.
//! but also idiomatic Rust types like `Result`/`String` and so on and so forth).
//!
//! # The solution
//!
//! This crate provides macros that are designed to work together to solve this particular
//! problem, and allow the usage of high-level patterns in APIs.
//!
//! ## User-side `ark_bindgen` macro
//!
//! This macro applies on a `mod` defined in a Rust file that is to be compiled to WebAssembly.
//! The output of this macro depends on whether it's running on the host or not:
//!
//! - On the host, this will create an `HostShim` trait, that contains a few static internal
//! methods, as well as methods mirroring the extern C function declarations, using high-level,
//! idiomatic Rust patterns. This macro can be partially implemented automatically with the
//! `host_exports` macro, described below.
//! - In user code, this will also create the trait as well as a `safe` Rust `mod` containing
//! functions with the same signatures as in the declaration.
//!
//! Here are the different kind of declarations one can do with it:
//!
//! // TODO reinclude, see also #4516
//! //```rust,ignore
//! //#![doc = include_str!("../../api-ffi/src/ffi/example_automatic.rs")]
//! //```
//!
//! ## Host-side `host_export` macro
//!
//! This macro applied on impl blocks for the `HostShim` trait generated by the above macro. An end
//! user only needs to implement the `_shim` functions in the trait, and get the rest implemented
//! for free by this macro.
//!
//! // TODO reinclude, see also #4516
//! //```rust,ignore
//! //#![doc = include_str!("../../../components/module-host/src/host_api/examples/automatic_macro.rs")]
//! //```
//!
//! ## `ark_test` macro
//!
//! This macro is designed to replicate the behavior of `#[test]` in Rust modules compiled to
//! WebAssembly.
use proc_macro::TokenStream;
use quote::quote;
mod auto_pad_union;
mod bindgen;
mod host_exports;
mod test;
mod utils;
/// Replaces an impl block of an `HostShim` trait (generated by the `ark_bindgen` macro) by an
/// augmented one that automatically implements some functions, making it more ergonomic and
/// pleasant to use.
///
/// Note this macro is designed to be used on the **host**.
///
/// See the crate description for examples of what you can do with it.
#[proc_macro_attribute]
pub fn host_exports(_args: TokenStream, input: TokenStream) -> TokenStream {
let mut item = syn::parse_macro_input!(input as host_exports::Item);
match host_exports::expand(&mut item) {
Ok(_) => TokenStream::from(quote!(#item)),
Err(e) => e.to_compile_error().into(),
}
}
/// Replaces a `mod` block by another one that augments type/struct/external function declarations
/// to make them more concise, ergonomic and pleasant to use.
///
/// In particular, generates an `HostShim` trait that is implemented partially automatically thanks
/// to the `host_exports` macro above.
///
/// Note this macro is designed to be used in WebAssembly code (creating a `safe` Rust module
/// that can used directly) *and* on the host (creating the trait that will be implemented by the
/// above macro).
///
/// See the crate description for examples of what you can do with it.
///
/// This unfortunately can't be applied at module scope yet; see also
/// <https://github.com/rust-lang/rust/issues/54726>.
#[proc_macro_attribute]
pub fn ark_bindgen(args: TokenStream, input: TokenStream) -> TokenStream {
let args = syn::parse_macro_input!(args as bindgen::Args);
let mut item = syn::parse_macro_input!(input as bindgen::Item);
match bindgen::expand(&mut item, args) {
Ok(_) => TokenStream::from(quote!(#item)),
Err(e) => e.to_compile_error().into(),
}
}
/// Macro emulating the behavior of the standard `#[test]` macro, but for module code compiled to
/// WebAssembly. Such a test can then be executed with ark using `ark module test`.
///
/// ```rust,ignore
/// #[ark_test]
/// fn makes_a_sad() {
/// assert_eq!(1, 2, "me am gud in nummers");
/// }
/// ```
#[proc_macro_attribute]
pub fn ark_test(
attr: proc_macro::TokenStream,
body: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
match test::try_ark_test(attr, body) {
Ok(result) => result.into(),
Err(err) => err.to_compile_error().into(),
}
}
/// This macro wraps each field in a union into an `ark_api_ffi::TransparentPad<T, N>`
/// such that the total size of the new type is equal to the size specified in the
/// `auto_pad_union` macro. The size is specified in bytes.
///
/// This macro automatically derives `bytemuck::NoPadding` and `bytemuck::AnyBitPattern`
/// for the union it is applied to, which is necessary so that those derives use the
/// modified version of the union definition.
///
/// # Expected form
///
/// ```ignore
/// #[ark_api_macros::auto_pad_union(size = <size>, <pod_accessors | checked_accessors>)]
/// ```
///
/// # Example
///
/// ```ignore
/// #[ark_api_macros::auto_pad_union(size = 8, pod_accessors)]
/// union AutoPadExample {
/// field1: u64,
/// field2: u32,
/// }
/// ```
///
/// Usually, this would result in a union of size 8 but with padding for bytes 4-8
/// when the used field is `field2`. This macro will expand this to:
///
/// ```ignore
/// #[derive(NoPadding, AnyBitPattern)]
/// union AutoPadExample {
/// field1: TransparentPad<u64, 0>,
/// field2: TransparentPad<u32, 4>,
/// }
/// ```
///
/// which adds "explicit"/"manual" padding bytes to the end of `field2` -- making the
/// compiler not treat them as *real* padding.
///
/// This macro will also derive accessor methods on the union for each field in the form
/// `as_{field_name}` (and `try_as_{field_name}`, if using `checked_accessors`) that return references
/// to the field as the field's original type rather than as the wrapped `TransparentPad`.
///
/// These accessors will either use `bytemuck`'s raw or checked casting functions based on
/// the second argument to the proc macro, which can be either `pod_accessors` or `checked_accessors`.
///
/// All fields must implement `Pod` for `pod_accessors`, while all fields must implement
/// `CheckedBitPattern` for `checked_accessors`.
#[proc_macro_attribute]
pub fn auto_pad_union(
args: proc_macro::TokenStream,
body: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let args = syn::parse_macro_input!(args as auto_pad_union::Args);
let mut item = syn::parse_macro_input!(body as syn::ItemUnion);
let item_ident = item.ident.clone();
match auto_pad_union::expand(&mut item, args) {
Ok(auto_pad_union::ExpansionExtras { extras, accessors }) => TokenStream::from(quote!(
#(#extras)*
#item
impl #item_ident {
#(#accessors)*
}
)),
Err(e) => e.to_compile_error().into(),
}
}