diff options
| author | Dario Nieuwenhuis <[email protected]> | 2022-02-12 00:11:15 +0100 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2022-02-12 01:16:31 +0100 |
| commit | 611961b499f8d4fee4f556c56606a2481e944791 (patch) | |
| tree | 1380adfe37d29ccd89dc05f19d1999fd67a2f6ad | |
| parent | 6c925b2342708266f24d58020e89786811531d47 (diff) | |
macros: cleanup, make work in stable.
| -rw-r--r-- | embassy-macros/src/chip/nrf.rs | 11 | ||||
| -rw-r--r-- | embassy-macros/src/chip/rp.rs | 10 | ||||
| -rw-r--r-- | embassy-macros/src/chip/stm32.rs | 11 | ||||
| -rw-r--r-- | embassy-macros/src/lib.rs | 534 | ||||
| -rw-r--r-- | embassy-macros/src/macros/interrupt.rs | 66 | ||||
| -rw-r--r-- | embassy-macros/src/macros/interrupt_declare.rs | 37 | ||||
| -rw-r--r-- | embassy-macros/src/macros/interrupt_take.rs | 36 | ||||
| -rw-r--r-- | embassy-macros/src/macros/main.rs | 135 | ||||
| -rw-r--r-- | embassy-macros/src/macros/mod.rs | 5 | ||||
| -rw-r--r-- | embassy-macros/src/macros/task.rs | 90 | ||||
| -rw-r--r-- | embassy-macros/src/util/ctxt.rs | 72 | ||||
| -rw-r--r-- | embassy-macros/src/util/mod.rs | 2 | ||||
| -rw-r--r-- | embassy-macros/src/util/path.rs (renamed from embassy-macros/src/path.rs) | 0 |
13 files changed, 462 insertions, 547 deletions
diff --git a/embassy-macros/src/chip/nrf.rs b/embassy-macros/src/chip/nrf.rs deleted file mode 100644 index 3ff6a74cf..000000000 --- a/embassy-macros/src/chip/nrf.rs +++ /dev/null | |||
| @@ -1,11 +0,0 @@ | |||
| 1 | use crate::path::ModulePrefix; | ||
| 2 | use proc_macro2::TokenStream; | ||
| 3 | use quote::quote; | ||
| 4 | |||
| 5 | pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream { | ||
| 6 | let embassy_nrf_path = embassy_prefix.append("embassy_nrf").path(); | ||
| 7 | |||
| 8 | quote!( | ||
| 9 | let p = #embassy_nrf_path::init(#config); | ||
| 10 | ) | ||
| 11 | } | ||
diff --git a/embassy-macros/src/chip/rp.rs b/embassy-macros/src/chip/rp.rs deleted file mode 100644 index ba0a97ada..000000000 --- a/embassy-macros/src/chip/rp.rs +++ /dev/null | |||
| @@ -1,10 +0,0 @@ | |||
| 1 | use crate::path::ModulePrefix; | ||
| 2 | use proc_macro2::TokenStream; | ||
| 3 | use quote::quote; | ||
| 4 | |||
| 5 | pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream { | ||
| 6 | let embassy_rp_path = embassy_prefix.append("embassy_rp").path(); | ||
| 7 | quote!( | ||
| 8 | let p = #embassy_rp_path::init(#config); | ||
| 9 | ) | ||
| 10 | } | ||
diff --git a/embassy-macros/src/chip/stm32.rs b/embassy-macros/src/chip/stm32.rs deleted file mode 100644 index c6938836c..000000000 --- a/embassy-macros/src/chip/stm32.rs +++ /dev/null | |||
| @@ -1,11 +0,0 @@ | |||
| 1 | use crate::path::ModulePrefix; | ||
| 2 | use proc_macro2::TokenStream; | ||
| 3 | use quote::quote; | ||
| 4 | |||
| 5 | pub fn generate(embassy_prefix: &ModulePrefix, config: syn::Expr) -> TokenStream { | ||
| 6 | let embassy_stm32_path = embassy_prefix.append("embassy_stm32").path(); | ||
| 7 | |||
| 8 | quote!( | ||
| 9 | let p = #embassy_stm32_path::init(#config); | ||
| 10 | ) | ||
| 11 | } | ||
diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index 44a8d3b93..085f7889d 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs | |||
| @@ -1,228 +1,39 @@ | |||
| 1 | #![feature(proc_macro_diagnostic)] | ||
| 2 | |||
| 3 | extern crate proc_macro; | 1 | extern crate proc_macro; |
| 4 | 2 | ||
| 5 | use darling::FromMeta; | ||
| 6 | use proc_macro::TokenStream; | 3 | use proc_macro::TokenStream; |
| 7 | use proc_macro2::Span; | ||
| 8 | use quote::{format_ident, quote}; | ||
| 9 | use std::iter; | ||
| 10 | use syn::spanned::Spanned; | ||
| 11 | use syn::{parse, Type, Visibility}; | ||
| 12 | use syn::{ItemFn, ReturnType}; | ||
| 13 | |||
| 14 | mod path; | ||
| 15 | |||
| 16 | use path::ModulePrefix; | ||
| 17 | 4 | ||
| 18 | #[derive(Debug, FromMeta)] | 5 | mod macros; |
| 19 | struct TaskArgs { | 6 | mod util; |
| 20 | #[darling(default)] | 7 | use macros::*; |
| 21 | pool_size: Option<usize>, | ||
| 22 | #[darling(default)] | ||
| 23 | send: bool, | ||
| 24 | #[darling(default)] | ||
| 25 | embassy_prefix: ModulePrefix, | ||
| 26 | } | ||
| 27 | 8 | ||
| 28 | #[proc_macro_attribute] | 9 | #[proc_macro_attribute] |
| 29 | pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { | 10 | pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { |
| 30 | let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs); | 11 | let args = syn::parse_macro_input!(args as syn::AttributeArgs); |
| 31 | let mut task_fn = syn::parse_macro_input!(item as syn::ItemFn); | 12 | let f = syn::parse_macro_input!(item as syn::ItemFn); |
| 32 | |||
| 33 | let macro_args = match TaskArgs::from_list(¯o_args) { | ||
| 34 | Ok(v) => v, | ||
| 35 | Err(e) => { | ||
| 36 | return TokenStream::from(e.write_errors()); | ||
| 37 | } | ||
| 38 | }; | ||
| 39 | |||
| 40 | let embassy_prefix = macro_args.embassy_prefix.append("embassy"); | ||
| 41 | let embassy_path = embassy_prefix.path(); | ||
| 42 | |||
| 43 | let pool_size: usize = macro_args.pool_size.unwrap_or(1); | ||
| 44 | |||
| 45 | let mut fail = false; | ||
| 46 | if task_fn.sig.asyncness.is_none() { | ||
| 47 | task_fn | ||
| 48 | .sig | ||
| 49 | .span() | ||
| 50 | .unwrap() | ||
| 51 | .error("task functions must be async") | ||
| 52 | .emit(); | ||
| 53 | fail = true; | ||
| 54 | } | ||
| 55 | if !task_fn.sig.generics.params.is_empty() { | ||
| 56 | task_fn | ||
| 57 | .sig | ||
| 58 | .span() | ||
| 59 | .unwrap() | ||
| 60 | .error("task functions must not be generic") | ||
| 61 | .emit(); | ||
| 62 | fail = true; | ||
| 63 | } | ||
| 64 | if pool_size < 1 { | ||
| 65 | return parse::Error::new(Span::call_site(), "pool_size must be 1 or greater") | ||
| 66 | .to_compile_error() | ||
| 67 | .into(); | ||
| 68 | } | ||
| 69 | |||
| 70 | let mut arg_names: syn::punctuated::Punctuated<syn::Ident, syn::Token![,]> = | ||
| 71 | syn::punctuated::Punctuated::new(); | ||
| 72 | let mut args = task_fn.sig.inputs.clone(); | ||
| 73 | |||
| 74 | for arg in args.iter_mut() { | ||
| 75 | match arg { | ||
| 76 | syn::FnArg::Receiver(_) => { | ||
| 77 | arg.span() | ||
| 78 | .unwrap() | ||
| 79 | .error("task functions must not have receiver arguments") | ||
| 80 | .emit(); | ||
| 81 | fail = true; | ||
| 82 | } | ||
| 83 | syn::FnArg::Typed(t) => match t.pat.as_mut() { | ||
| 84 | syn::Pat::Ident(i) => { | ||
| 85 | arg_names.push(i.ident.clone()); | ||
| 86 | i.mutability = None; | ||
| 87 | } | ||
| 88 | _ => { | ||
| 89 | arg.span() | ||
| 90 | .unwrap() | ||
| 91 | .error("pattern matching in task arguments is not yet supporteds") | ||
| 92 | .emit(); | ||
| 93 | fail = true; | ||
| 94 | } | ||
| 95 | }, | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | if fail { | ||
| 100 | return TokenStream::new(); | ||
| 101 | } | ||
| 102 | 13 | ||
| 103 | let name = task_fn.sig.ident.clone(); | 14 | task::run(args, f).unwrap_or_else(|x| x).into() |
| 104 | |||
| 105 | let visibility = &task_fn.vis; | ||
| 106 | task_fn.sig.ident = format_ident!("task"); | ||
| 107 | let impl_ty = if macro_args.send { | ||
| 108 | quote!(impl ::core::future::Future + Send + 'static) | ||
| 109 | } else { | ||
| 110 | quote!(impl ::core::future::Future + 'static) | ||
| 111 | }; | ||
| 112 | |||
| 113 | let attrs = &task_fn.attrs; | ||
| 114 | |||
| 115 | let result = quote! { | ||
| 116 | #(#attrs)* | ||
| 117 | #visibility fn #name(#args) -> #embassy_path::executor::SpawnToken<#impl_ty> { | ||
| 118 | use #embassy_path::executor::raw::TaskStorage; | ||
| 119 | #task_fn | ||
| 120 | type F = #impl_ty; | ||
| 121 | #[allow(clippy::declare_interior_mutable_const)] | ||
| 122 | const NEW_TASK: TaskStorage<F> = TaskStorage::new(); | ||
| 123 | static POOL: [TaskStorage<F>; #pool_size] = [NEW_TASK; #pool_size]; | ||
| 124 | unsafe { TaskStorage::spawn_pool(&POOL, move || task(#arg_names)) } | ||
| 125 | } | ||
| 126 | }; | ||
| 127 | result.into() | ||
| 128 | } | 15 | } |
| 129 | 16 | ||
| 130 | #[proc_macro_attribute] | 17 | #[proc_macro_attribute] |
| 131 | pub fn interrupt(args: TokenStream, input: TokenStream) -> TokenStream { | 18 | pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { |
| 132 | let mut f: ItemFn = syn::parse(input).expect("`#[interrupt]` must be applied to a function"); | 19 | let args = syn::parse_macro_input!(args as syn::AttributeArgs); |
| 133 | 20 | let f = syn::parse_macro_input!(item as syn::ItemFn); | |
| 134 | if !args.is_empty() { | 21 | main::run(args, f).unwrap_or_else(|x| x).into() |
| 135 | return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") | 22 | } |
| 136 | .to_compile_error() | ||
| 137 | .into(); | ||
| 138 | } | ||
| 139 | |||
| 140 | let fspan = f.span(); | ||
| 141 | let ident = f.sig.ident.clone(); | ||
| 142 | let ident_s = ident.to_string(); | ||
| 143 | |||
| 144 | // XXX should we blacklist other attributes? | ||
| 145 | |||
| 146 | let valid_signature = f.sig.constness.is_none() | ||
| 147 | && f.vis == Visibility::Inherited | ||
| 148 | && f.sig.abi.is_none() | ||
| 149 | && f.sig.inputs.is_empty() | ||
| 150 | && f.sig.generics.params.is_empty() | ||
| 151 | && f.sig.generics.where_clause.is_none() | ||
| 152 | && f.sig.variadic.is_none() | ||
| 153 | && match f.sig.output { | ||
| 154 | ReturnType::Default => true, | ||
| 155 | ReturnType::Type(_, ref ty) => match **ty { | ||
| 156 | Type::Tuple(ref tuple) => tuple.elems.is_empty(), | ||
| 157 | Type::Never(..) => true, | ||
| 158 | _ => false, | ||
| 159 | }, | ||
| 160 | }; | ||
| 161 | |||
| 162 | if !valid_signature { | ||
| 163 | return parse::Error::new( | ||
| 164 | fspan, | ||
| 165 | "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`", | ||
| 166 | ) | ||
| 167 | .to_compile_error() | ||
| 168 | .into(); | ||
| 169 | } | ||
| 170 | |||
| 171 | f.block.stmts = iter::once( | ||
| 172 | syn::parse2(quote! {{ | ||
| 173 | // Check that this interrupt actually exists | ||
| 174 | let __irq_exists_check: interrupt::#ident; | ||
| 175 | }}) | ||
| 176 | .unwrap(), | ||
| 177 | ) | ||
| 178 | .chain(f.block.stmts) | ||
| 179 | .collect(); | ||
| 180 | 23 | ||
| 181 | quote!( | 24 | #[proc_macro_attribute] |
| 182 | #[doc(hidden)] | 25 | pub fn interrupt(args: TokenStream, item: TokenStream) -> TokenStream { |
| 183 | #[export_name = #ident_s] | 26 | let args = syn::parse_macro_input!(args as syn::AttributeArgs); |
| 184 | #[allow(non_snake_case)] | 27 | let f = syn::parse_macro_input!(item as syn::ItemFn); |
| 185 | #f | 28 | interrupt::run(args, f).unwrap_or_else(|x| x).into() |
| 186 | ) | ||
| 187 | .into() | ||
| 188 | } | 29 | } |
| 189 | 30 | ||
| 190 | #[proc_macro] | 31 | #[proc_macro] |
| 191 | pub fn interrupt_declare(item: TokenStream) -> TokenStream { | 32 | pub fn interrupt_declare(item: TokenStream) -> TokenStream { |
| 192 | let name = syn::parse_macro_input!(item as syn::Ident); | 33 | let name = syn::parse_macro_input!(item as syn::Ident); |
| 193 | let name = format_ident!("{}", name); | 34 | interrupt_declare::run(name).unwrap_or_else(|x| x).into() |
| 194 | let name_interrupt = format_ident!("{}", name); | ||
| 195 | let name_handler = format!("__EMBASSY_{}_HANDLER", name); | ||
| 196 | |||
| 197 | let result = quote! { | ||
| 198 | #[allow(non_camel_case_types)] | ||
| 199 | pub struct #name_interrupt(()); | ||
| 200 | unsafe impl ::embassy::interrupt::Interrupt for #name_interrupt { | ||
| 201 | type Priority = crate::interrupt::Priority; | ||
| 202 | fn number(&self) -> u16 { | ||
| 203 | use cortex_m::interrupt::InterruptNumber; | ||
| 204 | let irq = InterruptEnum::#name; | ||
| 205 | irq.number() as u16 | ||
| 206 | } | ||
| 207 | unsafe fn steal() -> Self { | ||
| 208 | Self(()) | ||
| 209 | } | ||
| 210 | unsafe fn __handler(&self) -> &'static ::embassy::interrupt::Handler { | ||
| 211 | #[export_name = #name_handler] | ||
| 212 | static HANDLER: ::embassy::interrupt::Handler = ::embassy::interrupt::Handler::new(); | ||
| 213 | &HANDLER | ||
| 214 | } | ||
| 215 | } | ||
| 216 | |||
| 217 | unsafe impl ::embassy::util::Unborrow for #name_interrupt { | ||
| 218 | type Target = #name_interrupt; | ||
| 219 | unsafe fn unborrow(self) -> #name_interrupt { | ||
| 220 | self | ||
| 221 | } | ||
| 222 | } | ||
| 223 | }; | ||
| 224 | result.into() | ||
| 225 | } | 35 | } |
| 36 | |||
| 226 | /// # interrupt_take procedural macro | 37 | /// # interrupt_take procedural macro |
| 227 | /// | 38 | /// |
| 228 | /// core::panic! is used as a default way to panic in this macro as there is no sensible way of enabling/disabling defmt for macro generation. | 39 | /// core::panic! is used as a default way to panic in this macro as there is no sensible way of enabling/disabling defmt for macro generation. |
| @@ -231,312 +42,5 @@ pub fn interrupt_declare(item: TokenStream) -> TokenStream { | |||
| 231 | #[proc_macro] | 42 | #[proc_macro] |
| 232 | pub fn interrupt_take(item: TokenStream) -> TokenStream { | 43 | pub fn interrupt_take(item: TokenStream) -> TokenStream { |
| 233 | let name = syn::parse_macro_input!(item as syn::Ident); | 44 | let name = syn::parse_macro_input!(item as syn::Ident); |
| 234 | let name = format!("{}", name); | 45 | interrupt_take::run(name).unwrap_or_else(|x| x).into() |
| 235 | let name_interrupt = format_ident!("{}", name); | ||
| 236 | let name_handler = format!("__EMBASSY_{}_HANDLER", name); | ||
| 237 | |||
| 238 | let result = quote! { | ||
| 239 | { | ||
| 240 | #[allow(non_snake_case)] | ||
| 241 | #[export_name = #name] | ||
| 242 | pub unsafe extern "C" fn trampoline() { | ||
| 243 | extern "C" { | ||
| 244 | #[link_name = #name_handler] | ||
| 245 | static HANDLER: ::embassy::interrupt::Handler; | ||
| 246 | } | ||
| 247 | |||
| 248 | let func = HANDLER.func.load(::embassy::export::atomic::Ordering::Relaxed); | ||
| 249 | let ctx = HANDLER.ctx.load(::embassy::export::atomic::Ordering::Relaxed); | ||
| 250 | let func: fn(*mut ()) = ::core::mem::transmute(func); | ||
| 251 | func(ctx) | ||
| 252 | } | ||
| 253 | |||
| 254 | static TAKEN: ::embassy::export::atomic::AtomicBool = ::embassy::export::atomic::AtomicBool::new(false); | ||
| 255 | |||
| 256 | if TAKEN.compare_exchange(false, true, ::embassy::export::atomic::Ordering::AcqRel, ::embassy::export::atomic::Ordering::Acquire).is_err() { | ||
| 257 | core::panic!("IRQ Already taken"); | ||
| 258 | } | ||
| 259 | |||
| 260 | let irq: interrupt::#name_interrupt = unsafe { ::core::mem::transmute(()) }; | ||
| 261 | irq | ||
| 262 | } | ||
| 263 | }; | ||
| 264 | result.into() | ||
| 265 | } | ||
| 266 | |||
| 267 | #[cfg(feature = "stm32")] | ||
| 268 | #[path = "chip/stm32.rs"] | ||
| 269 | mod chip; | ||
| 270 | |||
| 271 | #[cfg(feature = "nrf")] | ||
| 272 | #[path = "chip/nrf.rs"] | ||
| 273 | mod chip; | ||
| 274 | |||
| 275 | #[cfg(feature = "rp")] | ||
| 276 | #[path = "chip/rp.rs"] | ||
| 277 | mod chip; | ||
| 278 | |||
| 279 | #[cfg(any( | ||
| 280 | feature = "nrf", | ||
| 281 | feature = "rp", | ||
| 282 | feature = "stm32", | ||
| 283 | feature = "wasm", | ||
| 284 | feature = "std" | ||
| 285 | ))] | ||
| 286 | #[derive(Debug, FromMeta)] | ||
| 287 | struct MainArgs { | ||
| 288 | #[darling(default)] | ||
| 289 | embassy_prefix: ModulePrefix, | ||
| 290 | |||
| 291 | #[allow(unused)] | ||
| 292 | #[darling(default)] | ||
| 293 | config: Option<syn::LitStr>, | ||
| 294 | } | ||
| 295 | |||
| 296 | #[cfg(any(feature = "nrf", feature = "rp", feature = "stm32"))] | ||
| 297 | #[proc_macro_attribute] | ||
| 298 | pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { | ||
| 299 | let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs); | ||
| 300 | let task_fn = syn::parse_macro_input!(item as syn::ItemFn); | ||
| 301 | |||
| 302 | let macro_args = match MainArgs::from_list(¯o_args) { | ||
| 303 | Ok(v) => v, | ||
| 304 | Err(e) => { | ||
| 305 | return TokenStream::from(e.write_errors()); | ||
| 306 | } | ||
| 307 | }; | ||
| 308 | |||
| 309 | let mut fail = false; | ||
| 310 | if task_fn.sig.asyncness.is_none() { | ||
| 311 | task_fn | ||
| 312 | .sig | ||
| 313 | .span() | ||
| 314 | .unwrap() | ||
| 315 | .error("task functions must be async") | ||
| 316 | .emit(); | ||
| 317 | fail = true; | ||
| 318 | } | ||
| 319 | if !task_fn.sig.generics.params.is_empty() { | ||
| 320 | task_fn | ||
| 321 | .sig | ||
| 322 | .span() | ||
| 323 | .unwrap() | ||
| 324 | .error("main function must not be generic") | ||
| 325 | .emit(); | ||
| 326 | fail = true; | ||
| 327 | } | ||
| 328 | |||
| 329 | let args = task_fn.sig.inputs.clone(); | ||
| 330 | |||
| 331 | if args.len() != 2 { | ||
| 332 | task_fn | ||
| 333 | .sig | ||
| 334 | .span() | ||
| 335 | .unwrap() | ||
| 336 | .error("main function must have 2 arguments") | ||
| 337 | .emit(); | ||
| 338 | fail = true; | ||
| 339 | } | ||
| 340 | |||
| 341 | if fail { | ||
| 342 | return TokenStream::new(); | ||
| 343 | } | ||
| 344 | |||
| 345 | let embassy_prefix = macro_args.embassy_prefix; | ||
| 346 | let embassy_prefix_lit = embassy_prefix.literal(); | ||
| 347 | let embassy_path = embassy_prefix.append("embassy").path(); | ||
| 348 | let task_fn_body = task_fn.block; | ||
| 349 | |||
| 350 | let config = macro_args | ||
| 351 | .config | ||
| 352 | .map(|s| s.parse::<syn::Expr>().unwrap()) | ||
| 353 | .unwrap_or_else(|| { | ||
| 354 | syn::Expr::Verbatim(quote! { | ||
| 355 | Default::default() | ||
| 356 | }) | ||
| 357 | }); | ||
| 358 | |||
| 359 | let chip_setup = chip::generate(&embassy_prefix, config); | ||
| 360 | |||
| 361 | let result = quote! { | ||
| 362 | #[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)] | ||
| 363 | async fn __embassy_main(#args) { | ||
| 364 | #task_fn_body | ||
| 365 | } | ||
| 366 | |||
| 367 | #[cortex_m_rt::entry] | ||
| 368 | fn main() -> ! { | ||
| 369 | unsafe fn make_static<T>(t: &mut T) -> &'static mut T { | ||
| 370 | ::core::mem::transmute(t) | ||
| 371 | } | ||
| 372 | |||
| 373 | #chip_setup | ||
| 374 | |||
| 375 | let mut executor = #embassy_path::executor::Executor::new(); | ||
| 376 | let executor = unsafe { make_static(&mut executor) }; | ||
| 377 | |||
| 378 | executor.run(|spawner| { | ||
| 379 | spawner.must_spawn(__embassy_main(spawner, p)); | ||
| 380 | }) | ||
| 381 | } | ||
| 382 | }; | ||
| 383 | result.into() | ||
| 384 | } | ||
| 385 | |||
| 386 | #[cfg(feature = "std")] | ||
| 387 | #[proc_macro_attribute] | ||
| 388 | pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { | ||
| 389 | let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs); | ||
| 390 | let task_fn = syn::parse_macro_input!(item as syn::ItemFn); | ||
| 391 | |||
| 392 | let macro_args = match MainArgs::from_list(¯o_args) { | ||
| 393 | Ok(v) => v, | ||
| 394 | Err(e) => { | ||
| 395 | return TokenStream::from(e.write_errors()); | ||
| 396 | } | ||
| 397 | }; | ||
| 398 | |||
| 399 | let embassy_path = macro_args.embassy_prefix.append("embassy"); | ||
| 400 | |||
| 401 | let mut fail = false; | ||
| 402 | if task_fn.sig.asyncness.is_none() { | ||
| 403 | task_fn | ||
| 404 | .sig | ||
| 405 | .span() | ||
| 406 | .unwrap() | ||
| 407 | .error("task functions must be async") | ||
| 408 | .emit(); | ||
| 409 | fail = true; | ||
| 410 | } | ||
| 411 | if !task_fn.sig.generics.params.is_empty() { | ||
| 412 | task_fn | ||
| 413 | .sig | ||
| 414 | .span() | ||
| 415 | .unwrap() | ||
| 416 | .error("main function must not be generic") | ||
| 417 | .emit(); | ||
| 418 | fail = true; | ||
| 419 | } | ||
| 420 | |||
| 421 | let args = task_fn.sig.inputs.clone(); | ||
| 422 | |||
| 423 | if args.len() != 1 { | ||
| 424 | task_fn | ||
| 425 | .sig | ||
| 426 | .span() | ||
| 427 | .unwrap() | ||
| 428 | .error("main function must have one argument") | ||
| 429 | .emit(); | ||
| 430 | fail = true; | ||
| 431 | } | ||
| 432 | |||
| 433 | if fail { | ||
| 434 | return TokenStream::new(); | ||
| 435 | } | ||
| 436 | |||
| 437 | let task_fn_body = task_fn.block.clone(); | ||
| 438 | |||
| 439 | let embassy_path = embassy_path.path(); | ||
| 440 | let embassy_prefix_lit = macro_args.embassy_prefix.literal(); | ||
| 441 | |||
| 442 | let result = quote! { | ||
| 443 | #[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)] | ||
| 444 | async fn __embassy_main(#args) { | ||
| 445 | #task_fn_body | ||
| 446 | } | ||
| 447 | |||
| 448 | fn main() -> ! { | ||
| 449 | unsafe fn make_static<T>(t: &mut T) -> &'static mut T { | ||
| 450 | ::core::mem::transmute(t) | ||
| 451 | } | ||
| 452 | |||
| 453 | let mut executor = #embassy_path::executor::Executor::new(); | ||
| 454 | let executor = unsafe { make_static(&mut executor) }; | ||
| 455 | |||
| 456 | executor.run(|spawner| { | ||
| 457 | spawner.spawn(__embassy_main(spawner)).unwrap(); | ||
| 458 | }) | ||
| 459 | |||
| 460 | } | ||
| 461 | }; | ||
| 462 | result.into() | ||
| 463 | } | ||
| 464 | |||
| 465 | #[cfg(feature = "wasm")] | ||
| 466 | #[proc_macro_attribute] | ||
| 467 | pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { | ||
| 468 | let macro_args = syn::parse_macro_input!(args as syn::AttributeArgs); | ||
| 469 | let task_fn = syn::parse_macro_input!(item as syn::ItemFn); | ||
| 470 | |||
| 471 | let macro_args = match MainArgs::from_list(¯o_args) { | ||
| 472 | Ok(v) => v, | ||
| 473 | Err(e) => { | ||
| 474 | return TokenStream::from(e.write_errors()); | ||
| 475 | } | ||
| 476 | }; | ||
| 477 | |||
| 478 | let embassy_path = macro_args.embassy_prefix.append("embassy"); | ||
| 479 | |||
| 480 | let mut fail = false; | ||
| 481 | if task_fn.sig.asyncness.is_none() { | ||
| 482 | task_fn | ||
| 483 | .sig | ||
| 484 | .span() | ||
| 485 | .unwrap() | ||
| 486 | .error("task functions must be async") | ||
| 487 | .emit(); | ||
| 488 | fail = true; | ||
| 489 | } | ||
| 490 | if !task_fn.sig.generics.params.is_empty() { | ||
| 491 | task_fn | ||
| 492 | .sig | ||
| 493 | .span() | ||
| 494 | .unwrap() | ||
| 495 | .error("main function must not be generic") | ||
| 496 | .emit(); | ||
| 497 | fail = true; | ||
| 498 | } | ||
| 499 | |||
| 500 | let args = task_fn.sig.inputs.clone(); | ||
| 501 | |||
| 502 | if args.len() != 1 { | ||
| 503 | task_fn | ||
| 504 | .sig | ||
| 505 | .span() | ||
| 506 | .unwrap() | ||
| 507 | .error("main function must have one argument") | ||
| 508 | .emit(); | ||
| 509 | fail = true; | ||
| 510 | } | ||
| 511 | |||
| 512 | if fail { | ||
| 513 | return TokenStream::new(); | ||
| 514 | } | ||
| 515 | |||
| 516 | let task_fn_body = task_fn.block.clone(); | ||
| 517 | |||
| 518 | let embassy_path = embassy_path.path(); | ||
| 519 | let embassy_prefix_lit = macro_args.embassy_prefix.literal(); | ||
| 520 | |||
| 521 | let result = quote! { | ||
| 522 | #[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)] | ||
| 523 | async fn __embassy_main(#args) { | ||
| 524 | #task_fn_body | ||
| 525 | } | ||
| 526 | |||
| 527 | use wasm_bindgen::prelude::*; | ||
| 528 | |||
| 529 | #[wasm_bindgen(start)] | ||
| 530 | pub fn main() -> Result<(), JsValue> { | ||
| 531 | static EXECUTOR: #embassy_path::util::Forever<#embassy_path::executor::Executor> = #embassy_path::util::Forever::new(); | ||
| 532 | let executor = EXECUTOR.put(#embassy_path::executor::Executor::new()); | ||
| 533 | |||
| 534 | executor.start(|spawner| { | ||
| 535 | spawner.spawn(__embassy_main(spawner)).unwrap(); | ||
| 536 | }); | ||
| 537 | |||
| 538 | Ok(()) | ||
| 539 | } | ||
| 540 | }; | ||
| 541 | result.into() | ||
| 542 | } | 46 | } |
diff --git a/embassy-macros/src/macros/interrupt.rs b/embassy-macros/src/macros/interrupt.rs new file mode 100644 index 000000000..32cc0e010 --- /dev/null +++ b/embassy-macros/src/macros/interrupt.rs | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | use darling::FromMeta; | ||
| 2 | use proc_macro2::TokenStream; | ||
| 3 | use quote::quote; | ||
| 4 | use std::iter; | ||
| 5 | use syn::ReturnType; | ||
| 6 | use syn::{Type, Visibility}; | ||
| 7 | |||
| 8 | use crate::util::ctxt::Ctxt; | ||
| 9 | |||
| 10 | #[derive(Debug, FromMeta)] | ||
| 11 | struct Args {} | ||
| 12 | |||
| 13 | pub fn run(args: syn::AttributeArgs, mut f: syn::ItemFn) -> Result<TokenStream, TokenStream> { | ||
| 14 | let _args = Args::from_list(&args).map_err(|e| e.write_errors())?; | ||
| 15 | |||
| 16 | let ident = f.sig.ident.clone(); | ||
| 17 | let ident_s = ident.to_string(); | ||
| 18 | |||
| 19 | // XXX should we blacklist other attributes? | ||
| 20 | |||
| 21 | let valid_signature = f.sig.constness.is_none() | ||
| 22 | && f.vis == Visibility::Inherited | ||
| 23 | && f.sig.abi.is_none() | ||
| 24 | && f.sig.inputs.is_empty() | ||
| 25 | && f.sig.generics.params.is_empty() | ||
| 26 | && f.sig.generics.where_clause.is_none() | ||
| 27 | && f.sig.variadic.is_none() | ||
| 28 | && match f.sig.output { | ||
| 29 | ReturnType::Default => true, | ||
| 30 | ReturnType::Type(_, ref ty) => match **ty { | ||
| 31 | Type::Tuple(ref tuple) => tuple.elems.is_empty(), | ||
| 32 | Type::Never(..) => true, | ||
| 33 | _ => false, | ||
| 34 | }, | ||
| 35 | }; | ||
| 36 | |||
| 37 | let ctxt = Ctxt::new(); | ||
| 38 | |||
| 39 | if !valid_signature { | ||
| 40 | ctxt.error_spanned_by( | ||
| 41 | &f.sig, | ||
| 42 | "`#[interrupt]` handlers must have signature `[unsafe] fn() [-> !]`", | ||
| 43 | ); | ||
| 44 | } | ||
| 45 | |||
| 46 | ctxt.check()?; | ||
| 47 | |||
| 48 | f.block.stmts = iter::once( | ||
| 49 | syn::parse2(quote! {{ | ||
| 50 | // Check that this interrupt actually exists | ||
| 51 | let __irq_exists_check: interrupt::#ident; | ||
| 52 | }}) | ||
| 53 | .unwrap(), | ||
| 54 | ) | ||
| 55 | .chain(f.block.stmts) | ||
| 56 | .collect(); | ||
| 57 | |||
| 58 | let result = quote!( | ||
| 59 | #[doc(hidden)] | ||
| 60 | #[export_name = #ident_s] | ||
| 61 | #[allow(non_snake_case)] | ||
| 62 | #f | ||
| 63 | ); | ||
| 64 | |||
| 65 | Ok(result) | ||
| 66 | } | ||
diff --git a/embassy-macros/src/macros/interrupt_declare.rs b/embassy-macros/src/macros/interrupt_declare.rs new file mode 100644 index 000000000..0059936d9 --- /dev/null +++ b/embassy-macros/src/macros/interrupt_declare.rs | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | use proc_macro2::TokenStream; | ||
| 2 | use quote::{format_ident, quote}; | ||
| 3 | |||
| 4 | pub fn run(name: syn::Ident) -> Result<TokenStream, TokenStream> { | ||
| 5 | let name = format_ident!("{}", name); | ||
| 6 | let name_interrupt = format_ident!("{}", name); | ||
| 7 | let name_handler = format!("__EMBASSY_{}_HANDLER", name); | ||
| 8 | |||
| 9 | let result = quote! { | ||
| 10 | #[allow(non_camel_case_types)] | ||
| 11 | pub struct #name_interrupt(()); | ||
| 12 | unsafe impl ::embassy::interrupt::Interrupt for #name_interrupt { | ||
| 13 | type Priority = crate::interrupt::Priority; | ||
| 14 | fn number(&self) -> u16 { | ||
| 15 | use cortex_m::interrupt::InterruptNumber; | ||
| 16 | let irq = InterruptEnum::#name; | ||
| 17 | irq.number() as u16 | ||
| 18 | } | ||
| 19 | unsafe fn steal() -> Self { | ||
| 20 | Self(()) | ||
| 21 | } | ||
| 22 | unsafe fn __handler(&self) -> &'static ::embassy::interrupt::Handler { | ||
| 23 | #[export_name = #name_handler] | ||
| 24 | static HANDLER: ::embassy::interrupt::Handler = ::embassy::interrupt::Handler::new(); | ||
| 25 | &HANDLER | ||
| 26 | } | ||
| 27 | } | ||
| 28 | |||
| 29 | unsafe impl ::embassy::util::Unborrow for #name_interrupt { | ||
| 30 | type Target = #name_interrupt; | ||
| 31 | unsafe fn unborrow(self) -> #name_interrupt { | ||
| 32 | self | ||
| 33 | } | ||
| 34 | } | ||
| 35 | }; | ||
| 36 | Ok(result) | ||
| 37 | } | ||
diff --git a/embassy-macros/src/macros/interrupt_take.rs b/embassy-macros/src/macros/interrupt_take.rs new file mode 100644 index 000000000..230b9c741 --- /dev/null +++ b/embassy-macros/src/macros/interrupt_take.rs | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | use proc_macro2::TokenStream; | ||
| 2 | use quote::{format_ident, quote}; | ||
| 3 | |||
| 4 | pub fn run(name: syn::Ident) -> Result<TokenStream, TokenStream> { | ||
| 5 | let name = format!("{}", name); | ||
| 6 | let name_interrupt = format_ident!("{}", name); | ||
| 7 | let name_handler = format!("__EMBASSY_{}_HANDLER", name); | ||
| 8 | |||
| 9 | let result = quote! { | ||
| 10 | { | ||
| 11 | #[allow(non_snake_case)] | ||
| 12 | #[export_name = #name] | ||
| 13 | pub unsafe extern "C" fn trampoline() { | ||
| 14 | extern "C" { | ||
| 15 | #[link_name = #name_handler] | ||
| 16 | static HANDLER: ::embassy::interrupt::Handler; | ||
| 17 | } | ||
| 18 | |||
| 19 | let func = HANDLER.func.load(::embassy::export::atomic::Ordering::Relaxed); | ||
| 20 | let ctx = HANDLER.ctx.load(::embassy::export::atomic::Ordering::Relaxed); | ||
| 21 | let func: fn(*mut ()) = ::core::mem::transmute(func); | ||
| 22 | func(ctx) | ||
| 23 | } | ||
| 24 | |||
| 25 | static TAKEN: ::embassy::export::atomic::AtomicBool = ::embassy::export::atomic::AtomicBool::new(false); | ||
| 26 | |||
| 27 | if TAKEN.compare_exchange(false, true, ::embassy::export::atomic::Ordering::AcqRel, ::embassy::export::atomic::Ordering::Acquire).is_err() { | ||
| 28 | core::panic!("IRQ Already taken"); | ||
| 29 | } | ||
| 30 | |||
| 31 | let irq: interrupt::#name_interrupt = unsafe { ::core::mem::transmute(()) }; | ||
| 32 | irq | ||
| 33 | } | ||
| 34 | }; | ||
| 35 | Ok(result) | ||
| 36 | } | ||
diff --git a/embassy-macros/src/macros/main.rs b/embassy-macros/src/macros/main.rs new file mode 100644 index 000000000..01e302921 --- /dev/null +++ b/embassy-macros/src/macros/main.rs | |||
| @@ -0,0 +1,135 @@ | |||
| 1 | use darling::FromMeta; | ||
| 2 | use proc_macro2::TokenStream; | ||
| 3 | use quote::quote; | ||
| 4 | |||
| 5 | use crate::util::ctxt::Ctxt; | ||
| 6 | use crate::util::path::ModulePrefix; | ||
| 7 | |||
| 8 | #[cfg(feature = "stm32")] | ||
| 9 | const HAL: Option<&str> = Some("embassy_stm32"); | ||
| 10 | #[cfg(feature = "nrf")] | ||
| 11 | const HAL: Option<&str> = Some("embassy_nrf"); | ||
| 12 | #[cfg(feature = "rp")] | ||
| 13 | const HAL: Option<&str> = Some("embassy_rp"); | ||
| 14 | #[cfg(not(any(feature = "stm32", feature = "nrf", feature = "rp")))] | ||
| 15 | const HAL: Option<&str> = None; | ||
| 16 | |||
| 17 | #[derive(Debug, FromMeta)] | ||
| 18 | struct Args { | ||
| 19 | #[darling(default)] | ||
| 20 | embassy_prefix: ModulePrefix, | ||
| 21 | |||
| 22 | #[allow(unused)] | ||
| 23 | #[darling(default)] | ||
| 24 | config: Option<syn::LitStr>, | ||
| 25 | } | ||
| 26 | |||
| 27 | pub fn run(args: syn::AttributeArgs, f: syn::ItemFn) -> Result<TokenStream, TokenStream> { | ||
| 28 | let args = Args::from_list(&args).map_err(|e| e.write_errors())?; | ||
| 29 | |||
| 30 | let fargs = f.sig.inputs.clone(); | ||
| 31 | |||
| 32 | let ctxt = Ctxt::new(); | ||
| 33 | |||
| 34 | if f.sig.asyncness.is_none() { | ||
| 35 | ctxt.error_spanned_by(&f.sig, "task functions must be async"); | ||
| 36 | } | ||
| 37 | if !f.sig.generics.params.is_empty() { | ||
| 38 | ctxt.error_spanned_by(&f.sig, "task functions must not be generic"); | ||
| 39 | } | ||
| 40 | |||
| 41 | if HAL.is_some() && fargs.len() != 2 { | ||
| 42 | ctxt.error_spanned_by(&f.sig, "main function must have 2 arguments"); | ||
| 43 | } | ||
| 44 | if HAL.is_none() && fargs.len() != 1 { | ||
| 45 | ctxt.error_spanned_by(&f.sig, "main function must have 2 arguments"); | ||
| 46 | } | ||
| 47 | |||
| 48 | ctxt.check()?; | ||
| 49 | |||
| 50 | let embassy_prefix = args.embassy_prefix; | ||
| 51 | let embassy_prefix_lit = embassy_prefix.literal(); | ||
| 52 | let embassy_path = embassy_prefix.append("embassy").path(); | ||
| 53 | let f_body = f.block; | ||
| 54 | |||
| 55 | #[cfg(feature = "wasm")] | ||
| 56 | let main = quote! { | ||
| 57 | #[wasm_bindgen::prelude::wasm_bindgen(start)] | ||
| 58 | pub fn main() -> Result<(), wasm_bindgen::JsValue> { | ||
| 59 | static EXECUTOR: #embassy_path::util::Forever<#embassy_path::executor::Executor> = #embassy_path::util::Forever::new(); | ||
| 60 | let executor = EXECUTOR.put(#embassy_path::executor::Executor::new()); | ||
| 61 | |||
| 62 | executor.start(|spawner| { | ||
| 63 | spawner.spawn(__embassy_main(spawner)).unwrap(); | ||
| 64 | }); | ||
| 65 | |||
| 66 | Ok(()) | ||
| 67 | } | ||
| 68 | }; | ||
| 69 | |||
| 70 | #[cfg(all(feature = "std", not(feature = "wasm")))] | ||
| 71 | let main = quote! { | ||
| 72 | fn main() -> ! { | ||
| 73 | let mut executor = #embassy_path::executor::Executor::new(); | ||
| 74 | let executor = unsafe { __make_static(&mut executor) }; | ||
| 75 | |||
| 76 | executor.run(|spawner| { | ||
| 77 | spawner.must_spawn(__embassy_main(spawner)); | ||
| 78 | }) | ||
| 79 | } | ||
| 80 | }; | ||
| 81 | |||
| 82 | #[cfg(all(not(feature = "std"), not(feature = "wasm")))] | ||
| 83 | let main = { | ||
| 84 | let config = args | ||
| 85 | .config | ||
| 86 | .map(|s| s.parse::<syn::Expr>().unwrap()) | ||
| 87 | .unwrap_or_else(|| { | ||
| 88 | syn::Expr::Verbatim(quote! { | ||
| 89 | Default::default() | ||
| 90 | }) | ||
| 91 | }); | ||
| 92 | |||
| 93 | let (hal_setup, peris_arg) = match HAL { | ||
| 94 | Some(hal) => { | ||
| 95 | let embassy_hal_path = embassy_prefix.append(hal).path(); | ||
| 96 | ( | ||
| 97 | quote!( | ||
| 98 | let p = #embassy_hal_path::init(#config); | ||
| 99 | ), | ||
| 100 | quote!(p), | ||
| 101 | ) | ||
| 102 | } | ||
| 103 | None => (quote!(), quote!()), | ||
| 104 | }; | ||
| 105 | |||
| 106 | quote! { | ||
| 107 | #[cortex_m_rt::entry] | ||
| 108 | fn main() -> ! { | ||
| 109 | #hal_setup | ||
| 110 | |||
| 111 | let mut executor = #embassy_path::executor::Executor::new(); | ||
| 112 | let executor = unsafe { __make_static(&mut executor) }; | ||
| 113 | |||
| 114 | executor.run(|spawner| { | ||
| 115 | spawner.must_spawn(__embassy_main(spawner, #peris_arg)); | ||
| 116 | }) | ||
| 117 | } | ||
| 118 | } | ||
| 119 | }; | ||
| 120 | |||
| 121 | let result = quote! { | ||
| 122 | #[#embassy_path::task(embassy_prefix = #embassy_prefix_lit)] | ||
| 123 | async fn __embassy_main(#fargs) { | ||
| 124 | #f_body | ||
| 125 | } | ||
| 126 | |||
| 127 | unsafe fn __make_static<T>(t: &mut T) -> &'static mut T { | ||
| 128 | ::core::mem::transmute(t) | ||
| 129 | } | ||
| 130 | |||
| 131 | #main | ||
| 132 | }; | ||
| 133 | |||
| 134 | Ok(result) | ||
| 135 | } | ||
diff --git a/embassy-macros/src/macros/mod.rs b/embassy-macros/src/macros/mod.rs new file mode 100644 index 000000000..4350f229f --- /dev/null +++ b/embassy-macros/src/macros/mod.rs | |||
| @@ -0,0 +1,5 @@ | |||
| 1 | pub mod interrupt; | ||
| 2 | pub mod interrupt_declare; | ||
| 3 | pub mod interrupt_take; | ||
| 4 | pub mod main; | ||
| 5 | pub mod task; | ||
diff --git a/embassy-macros/src/macros/task.rs b/embassy-macros/src/macros/task.rs new file mode 100644 index 000000000..f0c78c596 --- /dev/null +++ b/embassy-macros/src/macros/task.rs | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | use darling::FromMeta; | ||
| 2 | use proc_macro2::TokenStream; | ||
| 3 | use quote::{format_ident, quote}; | ||
| 4 | |||
| 5 | use crate::util::ctxt::Ctxt; | ||
| 6 | use crate::util::path::ModulePrefix; | ||
| 7 | |||
| 8 | #[derive(Debug, FromMeta)] | ||
| 9 | struct Args { | ||
| 10 | #[darling(default)] | ||
| 11 | pool_size: Option<usize>, | ||
| 12 | #[darling(default)] | ||
| 13 | send: bool, | ||
| 14 | #[darling(default)] | ||
| 15 | embassy_prefix: ModulePrefix, | ||
| 16 | } | ||
| 17 | |||
| 18 | pub fn run(args: syn::AttributeArgs, mut f: syn::ItemFn) -> Result<TokenStream, TokenStream> { | ||
| 19 | let args = Args::from_list(&args).map_err(|e| e.write_errors())?; | ||
| 20 | |||
| 21 | let embassy_prefix = args.embassy_prefix.append("embassy"); | ||
| 22 | let embassy_path = embassy_prefix.path(); | ||
| 23 | |||
| 24 | let pool_size: usize = args.pool_size.unwrap_or(1); | ||
| 25 | |||
| 26 | let ctxt = Ctxt::new(); | ||
| 27 | |||
| 28 | if f.sig.asyncness.is_none() { | ||
| 29 | ctxt.error_spanned_by(&f.sig, "task functions must be async"); | ||
| 30 | } | ||
| 31 | if !f.sig.generics.params.is_empty() { | ||
| 32 | ctxt.error_spanned_by(&f.sig, "task functions must not be generic"); | ||
| 33 | } | ||
| 34 | if pool_size < 1 { | ||
| 35 | ctxt.error_spanned_by(&f.sig, "pool_size must be 1 or greater"); | ||
| 36 | } | ||
| 37 | |||
| 38 | let mut arg_names: syn::punctuated::Punctuated<syn::Ident, syn::Token![,]> = | ||
| 39 | syn::punctuated::Punctuated::new(); | ||
| 40 | let mut fargs = f.sig.inputs.clone(); | ||
| 41 | |||
| 42 | for arg in fargs.iter_mut() { | ||
| 43 | match arg { | ||
| 44 | syn::FnArg::Receiver(_) => { | ||
| 45 | ctxt.error_spanned_by(arg, "task functions must not have receiver arguments"); | ||
| 46 | } | ||
| 47 | syn::FnArg::Typed(t) => match t.pat.as_mut() { | ||
| 48 | syn::Pat::Ident(i) => { | ||
| 49 | arg_names.push(i.ident.clone()); | ||
| 50 | i.mutability = None; | ||
| 51 | } | ||
| 52 | _ => { | ||
| 53 | ctxt.error_spanned_by( | ||
| 54 | arg, | ||
| 55 | "pattern matching in task arguments is not yet supporteds", | ||
| 56 | ); | ||
| 57 | } | ||
| 58 | }, | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | ctxt.check()?; | ||
| 63 | |||
| 64 | let name = f.sig.ident.clone(); | ||
| 65 | |||
| 66 | let visibility = &f.vis; | ||
| 67 | f.sig.ident = format_ident!("task"); | ||
| 68 | let impl_ty = if args.send { | ||
| 69 | quote!(impl ::core::future::Future + Send + 'static) | ||
| 70 | } else { | ||
| 71 | quote!(impl ::core::future::Future + 'static) | ||
| 72 | }; | ||
| 73 | |||
| 74 | let attrs = &f.attrs; | ||
| 75 | |||
| 76 | let result = quote! { | ||
| 77 | #(#attrs)* | ||
| 78 | #visibility fn #name(#fargs) -> #embassy_path::executor::SpawnToken<#impl_ty> { | ||
| 79 | use #embassy_path::executor::raw::TaskStorage; | ||
| 80 | #f | ||
| 81 | type F = #impl_ty; | ||
| 82 | #[allow(clippy::declare_interior_mutable_const)] | ||
| 83 | const NEW_TASK: TaskStorage<F> = TaskStorage::new(); | ||
| 84 | static POOL: [TaskStorage<F>; #pool_size] = [NEW_TASK; #pool_size]; | ||
| 85 | unsafe { TaskStorage::spawn_pool(&POOL, move || task(#arg_names)) } | ||
| 86 | } | ||
| 87 | }; | ||
| 88 | |||
| 89 | Ok(result) | ||
| 90 | } | ||
diff --git a/embassy-macros/src/util/ctxt.rs b/embassy-macros/src/util/ctxt.rs new file mode 100644 index 000000000..d668ae780 --- /dev/null +++ b/embassy-macros/src/util/ctxt.rs | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | // nifty utility borrowed from serde :) | ||
| 2 | // https://github.com/serde-rs/serde/blob/master/serde_derive/src/internals/ctxt.rs | ||
| 3 | |||
| 4 | use proc_macro2::TokenStream; | ||
| 5 | use quote::{quote, ToTokens}; | ||
| 6 | use std::cell::RefCell; | ||
| 7 | use std::fmt::Display; | ||
| 8 | use std::thread; | ||
| 9 | use syn; | ||
| 10 | |||
| 11 | /// A type to collect errors together and format them. | ||
| 12 | /// | ||
| 13 | /// Dropping this object will cause a panic. It must be consumed using `check`. | ||
| 14 | /// | ||
| 15 | /// References can be shared since this type uses run-time exclusive mut checking. | ||
| 16 | #[derive(Default)] | ||
| 17 | pub struct Ctxt { | ||
| 18 | // The contents will be set to `None` during checking. This is so that checking can be | ||
| 19 | // enforced. | ||
| 20 | errors: RefCell<Option<Vec<syn::Error>>>, | ||
| 21 | } | ||
| 22 | |||
| 23 | impl Ctxt { | ||
| 24 | /// Create a new context object. | ||
| 25 | /// | ||
| 26 | /// This object contains no errors, but will still trigger a panic if it is not `check`ed. | ||
| 27 | pub fn new() -> Self { | ||
| 28 | Ctxt { | ||
| 29 | errors: RefCell::new(Some(Vec::new())), | ||
| 30 | } | ||
| 31 | } | ||
| 32 | |||
| 33 | /// Add an error to the context object with a tokenenizable object. | ||
| 34 | /// | ||
| 35 | /// The object is used for spanning in error messages. | ||
| 36 | pub fn error_spanned_by<A: ToTokens, T: Display>(&self, obj: A, msg: T) { | ||
| 37 | self.errors | ||
| 38 | .borrow_mut() | ||
| 39 | .as_mut() | ||
| 40 | .unwrap() | ||
| 41 | // Curb monomorphization from generating too many identical methods. | ||
| 42 | .push(syn::Error::new_spanned(obj.into_token_stream(), msg)); | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Add one of Syn's parse errors. | ||
| 46 | #[allow(unused)] | ||
| 47 | pub fn syn_error(&self, err: syn::Error) { | ||
| 48 | self.errors.borrow_mut().as_mut().unwrap().push(err); | ||
| 49 | } | ||
| 50 | |||
| 51 | /// Consume this object, producing a formatted error string if there are errors. | ||
| 52 | pub fn check(self) -> Result<(), TokenStream> { | ||
| 53 | let errors = self.errors.borrow_mut().take().unwrap(); | ||
| 54 | match errors.len() { | ||
| 55 | 0 => Ok(()), | ||
| 56 | _ => Err(to_compile_errors(errors)), | ||
| 57 | } | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream { | ||
| 62 | let compile_errors = errors.iter().map(syn::Error::to_compile_error); | ||
| 63 | quote!(#(#compile_errors)*) | ||
| 64 | } | ||
| 65 | |||
| 66 | impl Drop for Ctxt { | ||
| 67 | fn drop(&mut self) { | ||
| 68 | if !thread::panicking() && self.errors.borrow().is_some() { | ||
| 69 | panic!("forgot to check for errors"); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
diff --git a/embassy-macros/src/util/mod.rs b/embassy-macros/src/util/mod.rs new file mode 100644 index 000000000..c2f2dfd65 --- /dev/null +++ b/embassy-macros/src/util/mod.rs | |||
| @@ -0,0 +1,2 @@ | |||
| 1 | pub mod ctxt; | ||
| 2 | pub mod path; | ||
diff --git a/embassy-macros/src/path.rs b/embassy-macros/src/util/path.rs index 00fca7bdb..00fca7bdb 100644 --- a/embassy-macros/src/path.rs +++ b/embassy-macros/src/util/path.rs | |||
