1use std::collections::HashSet;
16
17use proc_macro::TokenStream;
18use proc_macro2::{Ident, Span};
19use quote::{quote, ToTokens};
20use syn::{parse::Parser, DeriveInput, TypeParamBound};
21
22#[allow(dead_code)]
23pub(crate) fn logfile(logfile: std::path::PathBuf) -> impl std::io::Write {
24 use std::{fs::OpenOptions, io::BufWriter};
25 let logfile = BufWriter::new(
26 OpenOptions::new()
27 .append(true)
28 .create(true)
29 .open(logfile)
30 .unwrap(),
31 );
32 logfile
33}
34
35#[allow(unused_macros)]
36macro_rules! log {
37 ($path: literal, $pat: literal, $e: expr) => {{
38 let logfile = std::path::PathBuf::from($path);
39 use std::io::Write;
40 let e = $e;
41 writeln!(crate::logfile(logfile), $pat, e);
42 e
43 }};
44 ($pat: literal, $e: expr) => {
45 log!("logfile.txt", $pat, $e)
46 };
47 ($e: expr) => {
48 log!("{}", $e)
49 };
50}
51
52pub(crate) fn tl_mod() -> proc_macro2::TokenStream {
53 match proc_macro_crate::crate_name("stabby-abi") {
54 Ok(proc_macro_crate::FoundCrate::Itself) => return quote!(crate),
55 Ok(proc_macro_crate::FoundCrate::Name(crate_name)) => {
56 let crate_name = Ident::new(&crate_name, Span::call_site());
57 return quote!(#crate_name);
58 }
59 _ => {}
60 }
61 match proc_macro_crate::crate_name("stabby")
62 .expect("Couldn't find `stabby` in your dependencies")
63 {
64 proc_macro_crate::FoundCrate::Itself => quote!(crate::abi),
65 proc_macro_crate::FoundCrate::Name(crate_name) => {
66 let crate_name = Ident::new(&crate_name, Span::call_site());
67 quote!(#crate_name::abi)
68 }
69 }
70}
71
72#[proc_macro_attribute]
74pub fn stabby(stabby_attrs: TokenStream, tokens: TokenStream) -> TokenStream {
75 if let Ok(DeriveInput {
76 attrs,
77 vis,
78 ident,
79 generics,
80 data,
81 }) = syn::parse(tokens.clone())
82 {
83 match data {
84 syn::Data::Struct(data) => {
85 structs::stabby(attrs, vis, ident, generics, data, &stabby_attrs)
86 }
87 syn::Data::Enum(data) => {
88 enums::stabby(attrs, vis, ident, generics, data, &stabby_attrs)
89 }
90 syn::Data::Union(data) => {
91 unions::stabby(attrs, vis, ident, generics, data, &stabby_attrs)
92 }
93 }
94 } else if let Ok(fn_spec) = syn::parse(tokens.clone()) {
95 functions::stabby(syn::parse(stabby_attrs).unwrap(), fn_spec)
96 } else if let Ok(trait_spec) = syn::parse(tokens.clone()) {
97 traits::stabby(trait_spec, &stabby_attrs)
98 } else if let Ok(async_block) = syn::parse::<syn::ExprAsync>(tokens) {
99 quote!(Box::new(#async_block).into())
100 } else {
101 panic!("Expected a type declaration, a trait declaration or a function declaration")
102 }
103 .into()
104}
105
106#[proc_macro]
111pub fn vtable(tokens: TokenStream) -> TokenStream {
112 let st = tl_mod();
113 let bounds =
114 syn::punctuated::Punctuated::<TypeParamBound, syn::token::Add>::parse_separated_nonempty
115 .parse(tokens)
116 .unwrap();
117 let mut vt = quote!(#st::vtable::VtDrop);
118 for bound in bounds {
119 match &bound {
120 TypeParamBound::Trait(t) => vt = quote!(< dyn #t as #st::vtable::CompoundVt >::Vt<#vt>),
121 TypeParamBound::Lifetime(lt) => panic!("Cannot give lifetimes to vtables, use `Dyn<{lt}, P, Vt>` or `DynRef<{lt}, Vt> instead`"),
122 }
123 }
124 vt.into()
125}
126
127enum PtrType {
128 Path(proc_macro2::TokenStream),
129 Ref,
130 RefMut,
131}
132struct DynPtr {
133 ptr: PtrType,
134 bounds: Vec<syn::TraitBound>,
135 lifetime: Option<syn::Lifetime>,
136}
137impl syn::parse::Parse for DynPtr {
138 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
139 let (mut this, elem) = match input.parse::<syn::Type>()? {
140 syn::Type::Path(syn::TypePath {
141 path:
142 syn::Path {
143 leading_colon,
144 mut segments,
145 },
146 ..
147 }) => {
148 let syn::PathSegment {
149 ident,
150 arguments:
151 syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
152 colon2_token: None,
153 mut args,
154 ..
155 }),
156 } = segments.pop().unwrap().into_value()
157 else {
158 panic!()
159 };
160 if args.len() != 1 {
161 panic!("Pointer-type must have exactly one generic argument containing `dyn Bounds`")
162 }
163 let arg = args.pop().unwrap().into_value();
164 let syn::GenericArgument::Type(ty) = arg else {
165 panic!()
166 };
167 (
168 DynPtr {
169 ptr: PtrType::Path(quote!(#leading_colon #segments #ident)),
170 lifetime: None,
171 bounds: Vec::new(),
172 },
173 ty,
174 )
175 }
176 syn::Type::Reference(syn::TypeReference {
177 lifetime,
178 mutability,
179 elem,
180 ..
181 }) => (
182 DynPtr {
183 ptr: if mutability.is_some() {
184 PtrType::RefMut
185 } else {
186 PtrType::Ref
187 },
188 lifetime,
189 bounds: Vec::new(),
190 },
191 *elem,
192 ),
193 _ => panic!("Only references and paths are supported by this macro"),
194 };
195 let syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) = elem else {
196 panic!("expected `dyn` not found")
197 };
198 for bound in bounds {
199 match bound {
200 TypeParamBound::Trait(t) => this.bounds.push(t),
201 TypeParamBound::Lifetime(lt) => {
202 if this.lifetime.is_some() {
203 panic!("Only a single lifetime is supported in this macro")
204 } else {
205 this.lifetime = Some(lt)
206 }
207 }
208 }
209 }
210 Ok(this)
211 }
212}
213
214#[proc_macro]
219pub fn dynptr(tokens: TokenStream) -> TokenStream {
220 let st = tl_mod();
221 let DynPtr {
222 ptr,
223 bounds,
224 lifetime,
225 } = syn::parse(tokens).unwrap();
226 let mut vt = quote!(#st::vtable::VtDrop);
227 let lifetime = lifetime.unwrap_or(syn::Lifetime::new("'static", Span::call_site()));
228 for bound in bounds {
229 vt = quote!(< dyn #bound as #st::vtable::CompoundVt<#lifetime> >::Vt<#vt>);
230 }
231 match ptr {
232 PtrType::Path(path) => quote!(#st::Dyn<#lifetime, #path<()>, #vt>),
233 PtrType::RefMut => quote!(#st::Dyn<#lifetime, &#lifetime mut (), #vt>),
234 PtrType::Ref => quote!(#st::DynRef<#lifetime, #vt>),
235 }
236 .into()
237}
238
239mod enums;
240mod functions;
241mod structs;
242mod traits;
243mod unions;
244pub(crate) mod utils;
245
246mod tyops;
247#[proc_macro]
248pub fn tyeval(tokens: TokenStream) -> TokenStream {
249 tyops::tyeval(&tokens.into()).into()
250}
251
252mod gen_closures;
253#[proc_macro]
254pub fn gen_closures_impl(_: TokenStream) -> TokenStream {
255 gen_closures::gen_closures().into()
256}
257
258#[derive(Clone)]
259enum Type {
260 Syn(syn::Type),
261 Report(Report),
262}
263impl From<syn::Type> for Type {
264 fn from(value: syn::Type) -> Self {
265 Self::Syn(value)
266 }
267}
268impl From<Report> for Type {
269 fn from(value: Report) -> Self {
270 Self::Report(value)
271 }
272}
273
274#[derive(Debug, Clone, Copy)]
275pub(crate) enum Tyty {
276 Struct,
277 Union,
278 Enum(enums::Repr),
279}
280impl ToTokens for Tyty {
281 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
282 let st = crate::tl_mod();
283 let tyty = match self {
284 Tyty::Struct => quote!(#st::report::TyTy::Struct),
285 Tyty::Union => quote!(#st::report::TyTy::Union),
286 Tyty::Enum(r) => {
287 let s = format!("{r:?}");
288 quote!(#st::report::TyTy::Enum(#st::str::Str::new(#s)))
289 }
290 };
291 tokens.extend(tyty);
292 }
293}
294#[derive(Clone)]
295pub(crate) struct Report {
296 name: String,
297 fields: Vec<(String, Type)>,
298 version: u32,
299 module: proc_macro2::TokenStream,
300 pub tyty: Tyty,
301}
302impl Report {
303 pub fn r#struct(
304 name: impl Into<String>,
305 version: u32,
306 module: proc_macro2::TokenStream,
307 ) -> Self {
308 Self {
309 name: name.into(),
310 fields: Vec::new(),
311 version,
312 module: if module.is_empty() {
313 quote!(::core::module_path!())
314 } else {
315 module
316 },
317 tyty: Tyty::Struct,
318 }
319 }
320 pub fn r#enum(name: impl Into<String>, version: u32, module: proc_macro2::TokenStream) -> Self {
321 Self {
322 name: name.into(),
323 fields: Vec::new(),
324 version,
325 module: if module.is_empty() {
326 quote!(::core::module_path!())
327 } else {
328 module
329 },
330 tyty: Tyty::Enum(enums::Repr::Stabby),
331 }
332 }
333 pub fn r#union(
334 name: impl Into<String>,
335 version: u32,
336 module: proc_macro2::TokenStream,
337 ) -> Self {
338 Self {
339 name: name.into(),
340 fields: Vec::new(),
341 version,
342 module: if module.is_empty() {
343 quote!(::core::module_path!())
344 } else {
345 module
346 },
347 tyty: Tyty::Union,
348 }
349 }
350 pub fn add_field(&mut self, name: String, ty: impl Into<Type>) {
351 self.fields.push((name, ty.into()));
352 }
353 fn __bounds(
354 &self,
355 bounded_types: &mut HashSet<syn::Type>,
356 mut report_bounds: proc_macro2::TokenStream,
357 st: &proc_macro2::TokenStream,
358 ) -> proc_macro2::TokenStream {
359 for (_, ty) in self.fields.iter() {
360 match ty {
361 Type::Syn(ty) => {
362 if bounded_types.insert(ty.clone()) {
363 report_bounds = quote!(#ty: #st::IStable, #report_bounds);
364 }
365 }
366 Type::Report(report) => {
367 report_bounds = report.__bounds(bounded_types, report_bounds, st)
368 }
369 }
370 }
371 report_bounds
372 }
373 pub fn bounds(&self) -> proc_macro2::TokenStream {
374 let st = crate::tl_mod();
375 let mut bounded_types = HashSet::new();
376 self.__bounds(&mut bounded_types, quote!(), &st)
377 }
378
379 pub fn crepr(&self) -> proc_macro2::TokenStream {
380 let st = crate::tl_mod();
381 match self.tyty {
382 Tyty::Struct => {
383 let tuple = quote::format_ident!("Tuple{}", self.fields.len());
392 let fields = self.fields.iter().map(|f| match &f.1 {
393 Type::Syn(ty) => quote! (<#ty as #st::IStable>::CType),
394 Type::Report(r) => r.crepr(),
395 });
396 quote! {
397 #st::tuple::#tuple <#(#fields,)*>
398 }
399 }
400 Tyty::Union => {
401 let mut crepr = quote!(());
402 for f in &self.fields {
403 let ty = match &f.1 {
404 Type::Syn(ty) => quote! (#ty),
405 Type::Report(r) => r.crepr(),
406 };
407 crepr = quote!(#st::Union<#ty, #crepr>);
408 }
409 quote! {<#crepr as #st::IStable>::CType}
410 }
411 Tyty::Enum(r) => {
412 let mut clone = self.clone();
413 clone.tyty = Tyty::Union;
414 let crepr = clone.crepr();
415 let determinant = quote::format_ident!("{r:?}");
416 quote! {
417 #st::tuple::Tuple2<#determinant, #crepr>
418 }
419 }
420 }
421 }
422}
423impl ToTokens for Report {
424 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
425 let st = crate::tl_mod();
426 let mut fields = quote!(None);
427
428 for (name, ty) in &self.fields {
429 fields = match ty {
430 Type::Syn(ty) => quote! {
431 Some(& #st::report::FieldReport {
432 name: #st::str::Str::new(#name),
433 ty: <#ty as #st::IStable>::REPORT,
434 next_field: #st::StableLike::new(#fields)
435 })
436 },
437 Type::Report(re) => quote! {
438 Some(& #st::report::FieldReport {
439 name: #st::str::Str::new(#name),
440 ty: &#re,
441 next_field: #st::StableLike::new(#fields)
442 })
443 },
444 }
445 }
446 let Self {
447 name,
448 version,
449 tyty,
450 module,
451 ..
452 } = self;
453 tokens.extend(quote!(#st::report::TypeReport {
454 name: #st::str::Str::new(#name),
455 module: #st::str::Str::new(#module),
456 fields: unsafe{#st::StableLike::new(#fields)},
457 version: #version,
458 tyty: #tyty,
459 }));
460 }
461}
462
463#[proc_macro_attribute]
464pub fn export(attrs: TokenStream, fn_spec: TokenStream) -> TokenStream {
465 crate::functions::export(attrs, syn::parse(fn_spec).unwrap()).into()
466}
467
468#[proc_macro_attribute]
469pub fn import(attrs: TokenStream, fn_spec: TokenStream) -> TokenStream {
470 crate::functions::import(attrs, syn::parse(fn_spec).unwrap()).into()
471}
472
473#[proc_macro]
474pub fn canary_suffixes(_: TokenStream) -> TokenStream {
475 let mut stream = quote::quote!();
476 for (name, spec) in functions::CanarySpec::ARRAY.iter().skip(2) {
477 let id = quote::format_ident!("CANARY_{}", name.to_ascii_uppercase());
478 let suffix = spec.to_string();
479 stream.extend(quote::quote!(pub const #id: &'static str = #suffix;));
480 }
481 stream.into()
482}
483
484trait Unself {
485 fn unself(&self, this: &syn::Ident) -> Self;
486}
487impl Unself for syn::Path {
488 fn unself(&self, this: &syn::Ident) -> Self {
489 let syn::Path {
490 leading_colon,
491 segments,
492 } = self;
493 if self.is_ident("Self") || self.is_ident(this) {
494 let st = crate::tl_mod();
495 return syn::parse2(quote! {#st::istable::_Self}).unwrap();
496 }
497 syn::Path {
498 leading_colon: *leading_colon,
499 segments: segments
500 .iter()
501 .map(|syn::PathSegment { ident, arguments }| syn::PathSegment {
502 ident: ident.clone(),
503 arguments: match arguments {
504 syn::PathArguments::None => syn::PathArguments::None,
505 syn::PathArguments::AngleBracketed(
506 syn::AngleBracketedGenericArguments {
507 colon2_token,
508 lt_token,
509 args,
510 gt_token,
511 },
512 ) => syn::PathArguments::AngleBracketed(
513 syn::AngleBracketedGenericArguments {
514 colon2_token: *colon2_token,
515 lt_token: *lt_token,
516 args: args
517 .iter()
518 .map(|arg| match arg {
519 syn::GenericArgument::Type(ty) => {
520 syn::GenericArgument::Type(ty.unself(this))
521 }
522 syn::GenericArgument::Binding(syn::Binding {
523 ident,
524 eq_token,
525 ty,
526 }) => syn::GenericArgument::Binding(syn::Binding {
527 ident: ident.clone(),
528 eq_token: *eq_token,
529 ty: ty.unself(this),
530 }),
531 syn::GenericArgument::Constraint(syn::Constraint {
532 ident,
533 colon_token,
534 bounds,
535 }) => syn::GenericArgument::Constraint(syn::Constraint {
536 ident: ident.clone(),
537 colon_token: *colon_token,
538 bounds: bounds.iter().map(|b| b.unself(this)).collect(),
539 }),
540 other => other.clone(),
541 })
542 .collect(),
543 gt_token: *gt_token,
544 },
545 ),
546 syn::PathArguments::Parenthesized(syn::ParenthesizedGenericArguments {
547 paren_token,
548 inputs,
549 output,
550 }) => {
551 syn::PathArguments::Parenthesized(syn::ParenthesizedGenericArguments {
552 paren_token: *paren_token,
553 inputs: inputs.iter().map(|t| t.unself(this)).collect(),
554 output: match output {
555 syn::ReturnType::Default => syn::ReturnType::Default,
556 syn::ReturnType::Type(arrow, ty) => {
557 syn::ReturnType::Type(*arrow, Box::new(ty.unself(this)))
558 }
559 },
560 })
561 }
562 },
563 })
564 .collect(),
565 }
566 }
567}
568impl Unself for syn::TypeParamBound {
569 fn unself(&self, this: &syn::Ident) -> Self {
570 match self {
571 TypeParamBound::Trait(syn::TraitBound {
572 paren_token,
573 modifier,
574 lifetimes,
575 path,
576 }) => TypeParamBound::Trait(syn::TraitBound {
577 paren_token: *paren_token,
578 modifier: *modifier,
579 lifetimes: lifetimes.clone(),
580 path: path.unself(this),
581 }),
582 TypeParamBound::Lifetime(l) => TypeParamBound::Lifetime(l.clone()),
583 }
584 }
585}
586impl Unself for syn::Type {
587 fn unself(&self, this: &syn::Ident) -> syn::Type {
588 match self {
589 syn::Type::Array(syn::TypeArray {
590 elem,
591 len,
592 bracket_token,
593 semi_token,
594 }) => syn::Type::Array(syn::TypeArray {
595 elem: Box::new(elem.unself(this)),
596 len: len.clone(),
597 bracket_token: *bracket_token,
598 semi_token: *semi_token,
599 }),
600 syn::Type::BareFn(syn::TypeBareFn {
601 lifetimes,
602 unsafety,
603 abi,
604 inputs,
605 output,
606 fn_token,
607 paren_token,
608 variadic,
609 }) => syn::Type::BareFn(syn::TypeBareFn {
610 lifetimes: lifetimes.clone(),
611 unsafety: *unsafety,
612 abi: abi.clone(),
613 inputs: inputs
614 .iter()
615 .map(|syn::BareFnArg { attrs, name, ty }| syn::BareFnArg {
616 attrs: attrs.clone(),
617 name: name.clone(),
618 ty: ty.unself(this),
619 })
620 .collect(),
621 output: match output {
622 syn::ReturnType::Default => syn::ReturnType::Default,
623 syn::ReturnType::Type(arrow, ret) => {
624 syn::ReturnType::Type(*arrow, Box::new(ret.unself(this)))
625 }
626 },
627 fn_token: *fn_token,
628 paren_token: *paren_token,
629 variadic: variadic.clone(),
630 }),
631 syn::Type::Group(syn::TypeGroup { group_token, elem }) => {
632 syn::Type::Group(syn::TypeGroup {
633 group_token: *group_token,
634 elem: Box::new(elem.unself(this)),
635 })
636 }
637 syn::Type::ImplTrait(syn::TypeImplTrait { impl_token, bounds }) => {
638 syn::Type::ImplTrait(syn::TypeImplTrait {
639 impl_token: *impl_token,
640 bounds: bounds.into_iter().map(|p| p.unself(this)).collect(),
641 })
642 }
643 syn::Type::Paren(syn::TypeParen { paren_token, elem }) => {
644 syn::Type::Paren(syn::TypeParen {
645 paren_token: *paren_token,
646 elem: Box::new(elem.unself(this)),
647 })
648 }
649 syn::Type::Path(syn::TypePath { qself, path }) => syn::Type::Path(syn::TypePath {
650 qself: qself.as_ref().map(
651 |syn::QSelf {
652 lt_token,
653 ty,
654 position,
655 as_token,
656 gt_token,
657 }| syn::QSelf {
658 lt_token: *lt_token,
659 ty: Box::new(ty.unself(this)),
660 position: *position,
661 as_token: *as_token,
662 gt_token: *gt_token,
663 },
664 ),
665 path: path.unself(this),
666 }),
667 syn::Type::Ptr(syn::TypePtr {
668 star_token,
669 const_token,
670 mutability,
671 elem,
672 }) => syn::Type::Ptr(syn::TypePtr {
673 star_token: *star_token,
674 const_token: *const_token,
675 mutability: *mutability,
676 elem: Box::new(elem.unself(this)),
677 }),
678 syn::Type::Reference(syn::TypeReference {
679 and_token,
680 lifetime,
681 mutability,
682 elem,
683 }) => syn::Type::Reference(syn::TypeReference {
684 and_token: *and_token,
685 lifetime: lifetime.clone(),
686 mutability: *mutability,
687 elem: Box::new(elem.unself(this)),
688 }),
689 syn::Type::Slice(syn::TypeSlice {
690 bracket_token,
691 elem,
692 }) => syn::Type::Slice(syn::TypeSlice {
693 bracket_token: *bracket_token,
694 elem: Box::new(elem.unself(this)),
695 }),
696 syn::Type::TraitObject(syn::TypeTraitObject { dyn_token, bounds }) => {
697 syn::Type::TraitObject(syn::TypeTraitObject {
698 dyn_token: *dyn_token,
699 bounds: bounds.iter().map(|b| b.unself(this)).collect(),
700 })
701 }
702 syn::Type::Tuple(syn::TypeTuple { paren_token, elems }) => {
703 syn::Type::Tuple(syn::TypeTuple {
704 paren_token: *paren_token,
705 elems: elems.iter().map(|ty| ty.unself(this)).collect(),
706 })
707 }
708 o => o.clone(),
709 }
710 }
711}