diff options
| author | Dario Nieuwenhuis <[email protected]> | 2023-12-07 00:43:18 +0100 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-12-07 00:48:30 +0100 |
| commit | ac2aec4e7a8e8a4c17c611810d382892398c9eb7 (patch) | |
| tree | 6ac62c9f6d41e53d0f7f6d9737b1c0c206e531b3 /embassy-executor-macros/src/macros | |
| parent | ad2d9040d9f36d2523a22752e98f24c661643cb7 (diff) | |
executor: rename macro crate to embassy-executor-macros, bump it.
Diffstat (limited to 'embassy-executor-macros/src/macros')
| -rw-r--r-- | embassy-executor-macros/src/macros/main.rs | 138 | ||||
| -rw-r--r-- | embassy-executor-macros/src/macros/mod.rs | 2 | ||||
| -rw-r--r-- | embassy-executor-macros/src/macros/task.rs | 114 |
3 files changed, 254 insertions, 0 deletions
diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs new file mode 100644 index 000000000..3c0d58567 --- /dev/null +++ b/embassy-executor-macros/src/macros/main.rs | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | use darling::export::NestedMeta; | ||
| 2 | use darling::FromMeta; | ||
| 3 | use proc_macro2::TokenStream; | ||
| 4 | use quote::quote; | ||
| 5 | use syn::{Expr, ReturnType, Type}; | ||
| 6 | |||
| 7 | use crate::util::ctxt::Ctxt; | ||
| 8 | |||
| 9 | #[derive(Debug, FromMeta)] | ||
| 10 | struct Args { | ||
| 11 | #[darling(default)] | ||
| 12 | entry: Option<String>, | ||
| 13 | } | ||
| 14 | |||
| 15 | pub fn riscv(args: &[NestedMeta]) -> TokenStream { | ||
| 16 | let maybe_entry = match Args::from_list(args) { | ||
| 17 | Ok(args) => args.entry, | ||
| 18 | Err(e) => return e.write_errors(), | ||
| 19 | }; | ||
| 20 | |||
| 21 | let entry = maybe_entry.unwrap_or("riscv_rt::entry".into()); | ||
| 22 | let entry = match Expr::from_string(&entry) { | ||
| 23 | Ok(expr) => expr, | ||
| 24 | Err(e) => return e.write_errors(), | ||
| 25 | }; | ||
| 26 | |||
| 27 | quote! { | ||
| 28 | #[#entry] | ||
| 29 | fn main() -> ! { | ||
| 30 | let mut executor = ::embassy_executor::Executor::new(); | ||
| 31 | let executor = unsafe { __make_static(&mut executor) }; | ||
| 32 | executor.run(|spawner| { | ||
| 33 | spawner.must_spawn(__embassy_main(spawner)); | ||
| 34 | }) | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | pub fn cortex_m() -> TokenStream { | ||
| 40 | quote! { | ||
| 41 | #[cortex_m_rt::entry] | ||
| 42 | fn main() -> ! { | ||
| 43 | let mut executor = ::embassy_executor::Executor::new(); | ||
| 44 | let executor = unsafe { __make_static(&mut executor) }; | ||
| 45 | executor.run(|spawner| { | ||
| 46 | spawner.must_spawn(__embassy_main(spawner)); | ||
| 47 | }) | ||
| 48 | } | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | pub fn wasm() -> TokenStream { | ||
| 53 | quote! { | ||
| 54 | #[wasm_bindgen::prelude::wasm_bindgen(start)] | ||
| 55 | pub fn main() -> Result<(), wasm_bindgen::JsValue> { | ||
| 56 | let executor = ::std::boxed::Box::leak(::std::boxed::Box::new(::embassy_executor::Executor::new())); | ||
| 57 | |||
| 58 | executor.start(|spawner| { | ||
| 59 | spawner.spawn(__embassy_main(spawner)).unwrap(); | ||
| 60 | }); | ||
| 61 | |||
| 62 | Ok(()) | ||
| 63 | } | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | pub fn std() -> TokenStream { | ||
| 68 | quote! { | ||
| 69 | fn main() -> ! { | ||
| 70 | let mut executor = ::embassy_executor::Executor::new(); | ||
| 71 | let executor = unsafe { __make_static(&mut executor) }; | ||
| 72 | |||
| 73 | executor.run(|spawner| { | ||
| 74 | spawner.must_spawn(__embassy_main(spawner)); | ||
| 75 | }) | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | pub fn run(args: &[NestedMeta], f: syn::ItemFn, main: TokenStream) -> Result<TokenStream, TokenStream> { | ||
| 81 | #[allow(unused_variables)] | ||
| 82 | let args = Args::from_list(args).map_err(|e| e.write_errors())?; | ||
| 83 | |||
| 84 | let fargs = f.sig.inputs.clone(); | ||
| 85 | |||
| 86 | let ctxt = Ctxt::new(); | ||
| 87 | |||
| 88 | if f.sig.asyncness.is_none() { | ||
| 89 | ctxt.error_spanned_by(&f.sig, "main function must be async"); | ||
| 90 | } | ||
| 91 | if !f.sig.generics.params.is_empty() { | ||
| 92 | ctxt.error_spanned_by(&f.sig, "main function must not be generic"); | ||
| 93 | } | ||
| 94 | if !f.sig.generics.where_clause.is_none() { | ||
| 95 | ctxt.error_spanned_by(&f.sig, "main function must not have `where` clauses"); | ||
| 96 | } | ||
| 97 | if !f.sig.abi.is_none() { | ||
| 98 | ctxt.error_spanned_by(&f.sig, "main function must not have an ABI qualifier"); | ||
| 99 | } | ||
| 100 | if !f.sig.variadic.is_none() { | ||
| 101 | ctxt.error_spanned_by(&f.sig, "main function must not be variadic"); | ||
| 102 | } | ||
| 103 | match &f.sig.output { | ||
| 104 | ReturnType::Default => {} | ||
| 105 | ReturnType::Type(_, ty) => match &**ty { | ||
| 106 | Type::Tuple(tuple) if tuple.elems.is_empty() => {} | ||
| 107 | Type::Never(_) => {} | ||
| 108 | _ => ctxt.error_spanned_by( | ||
| 109 | &f.sig, | ||
| 110 | "main function must either not return a value, return `()` or return `!`", | ||
| 111 | ), | ||
| 112 | }, | ||
| 113 | } | ||
| 114 | |||
| 115 | if fargs.len() != 1 { | ||
| 116 | ctxt.error_spanned_by(&f.sig, "main function must have 1 argument: the spawner."); | ||
| 117 | } | ||
| 118 | |||
| 119 | ctxt.check()?; | ||
| 120 | |||
| 121 | let f_body = f.block; | ||
| 122 | let out = &f.sig.output; | ||
| 123 | |||
| 124 | let result = quote! { | ||
| 125 | #[::embassy_executor::task()] | ||
| 126 | async fn __embassy_main(#fargs) #out { | ||
| 127 | #f_body | ||
| 128 | } | ||
| 129 | |||
| 130 | unsafe fn __make_static<T>(t: &mut T) -> &'static mut T { | ||
| 131 | ::core::mem::transmute(t) | ||
| 132 | } | ||
| 133 | |||
| 134 | #main | ||
| 135 | }; | ||
| 136 | |||
| 137 | Ok(result) | ||
| 138 | } | ||
diff --git a/embassy-executor-macros/src/macros/mod.rs b/embassy-executor-macros/src/macros/mod.rs new file mode 100644 index 000000000..572094ca6 --- /dev/null +++ b/embassy-executor-macros/src/macros/mod.rs | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | pub mod main; | ||
| 2 | pub mod task; | ||
diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs new file mode 100644 index 000000000..5161e1020 --- /dev/null +++ b/embassy-executor-macros/src/macros/task.rs | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | use darling::export::NestedMeta; | ||
| 2 | use darling::FromMeta; | ||
| 3 | use proc_macro2::{Span, TokenStream}; | ||
| 4 | use quote::{format_ident, quote}; | ||
| 5 | use syn::{parse_quote, Expr, ExprLit, ItemFn, Lit, LitInt, ReturnType, Type}; | ||
| 6 | |||
| 7 | use crate::util::ctxt::Ctxt; | ||
| 8 | |||
| 9 | #[derive(Debug, FromMeta)] | ||
| 10 | struct Args { | ||
| 11 | #[darling(default)] | ||
| 12 | pool_size: Option<syn::Expr>, | ||
| 13 | } | ||
| 14 | |||
| 15 | pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result<TokenStream, TokenStream> { | ||
| 16 | let args = Args::from_list(args).map_err(|e| e.write_errors())?; | ||
| 17 | |||
| 18 | let pool_size = args.pool_size.unwrap_or(Expr::Lit(ExprLit { | ||
| 19 | attrs: vec![], | ||
| 20 | lit: Lit::Int(LitInt::new("1", Span::call_site())), | ||
| 21 | })); | ||
| 22 | |||
| 23 | let ctxt = Ctxt::new(); | ||
| 24 | |||
| 25 | if f.sig.asyncness.is_none() { | ||
| 26 | ctxt.error_spanned_by(&f.sig, "task functions must be async"); | ||
| 27 | } | ||
| 28 | if !f.sig.generics.params.is_empty() { | ||
| 29 | ctxt.error_spanned_by(&f.sig, "task functions must not be generic"); | ||
| 30 | } | ||
| 31 | if !f.sig.generics.where_clause.is_none() { | ||
| 32 | ctxt.error_spanned_by(&f.sig, "task functions must not have `where` clauses"); | ||
| 33 | } | ||
| 34 | if !f.sig.abi.is_none() { | ||
| 35 | ctxt.error_spanned_by(&f.sig, "task functions must not have an ABI qualifier"); | ||
| 36 | } | ||
| 37 | if !f.sig.variadic.is_none() { | ||
| 38 | ctxt.error_spanned_by(&f.sig, "task functions must not be variadic"); | ||
| 39 | } | ||
| 40 | match &f.sig.output { | ||
| 41 | ReturnType::Default => {} | ||
| 42 | ReturnType::Type(_, ty) => match &**ty { | ||
| 43 | Type::Tuple(tuple) if tuple.elems.is_empty() => {} | ||
| 44 | Type::Never(_) => {} | ||
| 45 | _ => ctxt.error_spanned_by( | ||
| 46 | &f.sig, | ||
| 47 | "task functions must either not return a value, return `()` or return `!`", | ||
| 48 | ), | ||
| 49 | }, | ||
| 50 | } | ||
| 51 | |||
| 52 | let mut arg_names = Vec::new(); | ||
| 53 | let mut fargs = f.sig.inputs.clone(); | ||
| 54 | |||
| 55 | for arg in fargs.iter_mut() { | ||
| 56 | match arg { | ||
| 57 | syn::FnArg::Receiver(_) => { | ||
| 58 | ctxt.error_spanned_by(arg, "task functions must not have receiver arguments"); | ||
| 59 | } | ||
| 60 | syn::FnArg::Typed(t) => match t.pat.as_mut() { | ||
| 61 | syn::Pat::Ident(id) => { | ||
| 62 | arg_names.push(id.ident.clone()); | ||
| 63 | id.mutability = None; | ||
| 64 | } | ||
| 65 | _ => { | ||
| 66 | ctxt.error_spanned_by(arg, "pattern matching in task arguments is not yet supported"); | ||
| 67 | } | ||
| 68 | }, | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | ctxt.check()?; | ||
| 73 | |||
| 74 | let task_ident = f.sig.ident.clone(); | ||
| 75 | let task_inner_ident = format_ident!("__{}_task", task_ident); | ||
| 76 | |||
| 77 | let mut task_inner = f; | ||
| 78 | let visibility = task_inner.vis.clone(); | ||
| 79 | task_inner.vis = syn::Visibility::Inherited; | ||
| 80 | task_inner.sig.ident = task_inner_ident.clone(); | ||
| 81 | |||
| 82 | #[cfg(feature = "nightly")] | ||
| 83 | let mut task_outer: ItemFn = parse_quote! { | ||
| 84 | #visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> { | ||
| 85 | type Fut = impl ::core::future::Future + 'static; | ||
| 86 | const POOL_SIZE: usize = #pool_size; | ||
| 87 | static POOL: ::embassy_executor::raw::TaskPool<Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new(); | ||
| 88 | unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) } | ||
| 89 | } | ||
| 90 | }; | ||
| 91 | #[cfg(not(feature = "nightly"))] | ||
| 92 | let mut task_outer: ItemFn = parse_quote! { | ||
| 93 | #visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken<impl Sized> { | ||
| 94 | const POOL_SIZE: usize = #pool_size; | ||
| 95 | static POOL: ::embassy_executor::_export::TaskPoolRef = ::embassy_executor::_export::TaskPoolRef::new(); | ||
| 96 | unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#arg_names,)*)) } | ||
| 97 | } | ||
| 98 | }; | ||
| 99 | |||
| 100 | task_outer.attrs.append(&mut task_inner.attrs.clone()); | ||
| 101 | |||
| 102 | let result = quote! { | ||
| 103 | // This is the user's task function, renamed. | ||
| 104 | // We put it outside the #task_ident fn below, because otherwise | ||
| 105 | // the items defined there (such as POOL) would be in scope | ||
| 106 | // in the user's code. | ||
| 107 | #[doc(hidden)] | ||
| 108 | #task_inner | ||
| 109 | |||
| 110 | #task_outer | ||
| 111 | }; | ||
| 112 | |||
| 113 | Ok(result) | ||
| 114 | } | ||
