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
#![deny(rust_2018_compatibility)]
#![deny(rust_2018_idioms)]
#![deny(warnings)]
#![recursion_limit = "128"]
extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{parse, parse_macro_input, FnArg, ItemFn, ItemStatic, ReturnType, Type};
#[proc_macro_attribute]
pub fn allocator(args: TokenStream, input: TokenStream) -> TokenStream {
if !args.is_empty() {
return parse::Error::new(Span::call_site(), "`#[allocator]` takes no arguments")
.to_compile_error()
.into();
}
let item = parse_macro_input!(input as ItemStatic);
let attrs = &item.attrs;
let expr = &item.expr;
let ident = &item.ident;
let ty = &item.ty;
let vis = &item.vis;
quote!(
#vis struct #ident;
impl core::ops::Deref for #ident {
type Target = #ty;
fn deref(&self) -> &#ty {
#(#attrs)*
static #ident: #ty = #expr;
&#ident
}
}
unsafe impl alloc_many::Alloc for #ident {
#[inline(always)]
unsafe fn alloc(layout: Layout) -> *mut u8 {
<#ty as core::alloc::GlobalAlloc>::alloc(&#ident, layout)
}
#[inline(always)]
unsafe fn dealloc(ptr: *mut u8, layout: Layout) {
<#ty as core::alloc::GlobalAlloc>::dealloc(&#ident, ptr, layout)
}
#[inline(always)]
unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 {
<#ty as core::alloc::GlobalAlloc>::alloc_zeroed(&#ident, layout)
}
#[inline(always)]
unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
<#ty as core::alloc::GlobalAlloc>::realloc(&#ident, ptr, layout, new_size)
}
}
)
.into()
}
#[proc_macro_attribute]
pub fn oom(args: TokenStream, input: TokenStream) -> TokenStream {
if !args.is_empty() {
return parse::Error::new(Span::call_site(), "`#[oom]` takes no arguments")
.to_compile_error()
.into();
}
let item = parse_macro_input!(input as ItemFn);
let arg = item.decl.inputs.iter().next().and_then(|arg| {
if let FnArg::Captured(arg) = arg {
Some(arg)
} else {
None
}
});
if item.constness.is_some()
|| item.asyncness.is_some()
|| item.abi.is_some()
|| !item.decl.generics.params.is_empty()
|| item.decl.generics.where_clause.is_some()
|| item.decl.inputs.len() != 1
|| arg.is_none()
|| item.decl.variadic.is_some()
|| !is_bottom(&item.decl.output)
{
return parse::Error::new(
Span::call_site(),
"`#[oom]` must have signature `fn(core::alloc::Layout) -> !`",
)
.to_compile_error()
.into();
}
let arg = arg.expect("UNREACHABLE");
let ident = &item.ident;
let block = &item.block;
quote!(
#[export_name = "alloc_many_oom"]
fn #ident(#arg) -> ! {
let _: fn(core::alloc::Layout) -> ! = #ident;
#block
}
)
.into()
}
fn is_bottom(ty: &ReturnType) -> bool {
match ty {
ReturnType::Default => false,
ReturnType::Type(_, ty) => match **ty {
Type::Never(_) => true,
_ => false,
},
}
}