Procedural Macros - The Rust Reference
let’s take a further look at function like procedural macro
Function-like - The Little Book of Rust Macros
Let’s use the knowledge to tackle our proble:
#[allow(clippy::too_many_lines)]
pub fn proc_macro(input: TokenStream) -> TokenStream {
let Input { layout: heap_layout, metadata, instance, trace_stream } = parse_macro_input!(input);
let Metadata { attrs: metadata_attrs, vis: metadata_vis, ident: metadata_ident } = &metadata;
let Instance { attrs: instance_attrs, vis: instance_vis, ident: instance_ident } = &instance;
let layout = match Layout::read_from_cargo() {
Ok(layout) => layout,
Err(err) => parse_error!("{err:#?}"),
};
let pools = match layout.heap.get(&heap_layout.to_string()) {
Some(heap) => &heap.pools,
None => parse_error!("Couldn't find heap.{heap_layout} in {LAYOUT_CONFIG}"),
};
let heap_layout_shouty_snk = heap_layout.to_string().to_shouty_snake_case();
let heap_rt_load = format_ident!("HEAP_{}_RT_LOAD", heap_layout_shouty_snk);
let heap_rt_base = format_ident!("HEAP_{}_RT_BASE", heap_layout_shouty_snk);
let heap_rt_end = format_ident!("HEAP_{}_RT_END", heap_layout_shouty_snk);
let section = LitStr::new(&format!(".heap_{heap_layout}_rt"), Span::call_site());
let pools_len = pools.len();
let pools_tokens = iter::repeat(quote! {
// Actual parameters will be set by drone-ld.
::drone_core::heap::Pool::new(0, 0, 0),
})
.take(pools_len)
.collect::<Vec<_>>();
let core_alloc = def_core_alloc(&metadata, trace_stream.as_ref());
let global_alloc = instance_attrs
.clone()
.into_iter()
.any(|attr| {
fn any_global_alloc(stream: TokenStream2) -> bool {
stream.into_iter().any(|tt| match tt {
TokenTree2::Group(group) => any_global_alloc(group.stream()),
TokenTree2::Ident(ident) => ident == "global_allocator",
_ => false,
})
}
attr.path.get_ident().map_or(false, |ident| ident == "global_allocator")
|| any_global_alloc(attr.tokens)
})
.then(|| def_global_alloc(&metadata));
quote! {
#(#metadata_attrs)*
#[repr(C)]
#metadata_vis struct #metadata_ident {
base: *mut u8,
pools: [::drone_core::heap::Pool; #pools_len],
}
#(#instance_attrs)*
#[link_section = #section]
#instance_vis static #instance_ident: #metadata_ident = #metadata_ident::new();
unsafe impl ::core::marker::Sync for #metadata_ident {}
impl #metadata_ident {
/// Creates a instance of this new heap metadata.
pub const fn new() -> Self {
Self {
base: ::core::ptr::null_mut(), // actual address will be set by drone-ld
pools: [
#(#pools_tokens)*
],
}
}
/// Initializes this heap metadata.
///
/// This function **must** be called as early as possible.
///
/// # Safety
///
/// This function reverts the state of the heap.
pub unsafe fn init() {
extern "C" {
static #heap_rt_load: ::core::cell::UnsafeCell<usize>;
static #heap_rt_base: ::core::cell::UnsafeCell<usize>;
static #heap_rt_end: ::core::cell::UnsafeCell<usize>;
}
unsafe {
::core::ptr::copy_nonoverlapping(
#heap_rt_load.get(),
#heap_rt_base.get(),
(#heap_rt_end.get() as usize - #heap_rt_base.get() as usize) >> 2,
);
}
}
}
#core_alloc
#global_alloc
}
.into()
}
let Input { layout: heap_layout, metadata, instance, trace_stream } = parse_macro_input!(input);
there it use parse_macro_input
to parse the input to the Input struct.
So Input
struct should implement the required parse
trait
below is the code in drone:
impl Parse for Input {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let mut layout = None;
let mut metadata = None;
let mut instance = None;
let mut trace_stream = None;
while !input.is_empty() {
let attrs = input.call(Attribute::parse_outer)?;
let ident = input.parse::<Ident>()?;
input.parse::<Token![=>]>()?;
if attrs.is_empty() && ident == "layout" {
if layout.is_none() {
layout = Some(input.parse()?);
} else {
return Err(input.error("multiple `layout` specifications"));
}
} else if ident == "metadata" {
if metadata.is_none() {
metadata = Some(Metadata::parse(input, attrs)?);
} else {
return Err(input.error("multiple `metadata` specifications"));
}
} else if ident == "instance" {
if instance.is_none() {
instance = Some(Instance::parse(input, attrs)?);
} else {
return Err(input.error("multiple `instance` specifications"));
}
} else if attrs.is_empty() && ident == "enable_trace_stream" {
if trace_stream.is_none() {
trace_stream = Some(input.parse()?);
} else {
return Err(input.error("multiple `trace_stream` specifications"));
}
} else {
return Err(input.error(format!("unknown key: `{ident}`")));
}
if !input.is_empty() {
input.parse::<Token![;]>()?;
}
}
Ok(Self {
layout: layout.ok_or_else(|| input.error("missing `layout` specification"))?,
metadata: metadata.ok_or_else(|| input.error("missing `metadata` specification"))?,
instance: instance.ok_or_else(|| input.error("missing `instance` specification"))?,
trace_stream,
})
}
}