basic_oop_macro/
lib.rs

1extern crate proc_macro;
2
3use anycase::{to_pascal, to_snake, to_screaming_snake};
4use macro_magic::import_tokens_attr;
5use proc_macro2::{TokenStream, Span};
6use proc_macro2_diagnostics::{Diagnostic, SpanDiagnosticExt};
7use quote::{quote, TokenStreamExt, ToTokens};
8use syn::{parse_macro_input, parse_quote, ItemStruct, Path, Type, Fields, Meta, Attribute, Visibility, Ident};
9use syn::{Field, FieldMutability, TypePath, Token, PathSegment, PathArguments, LitInt, TypeBareFn, Expr};
10use syn::{BareFnArg, Generics, Signature, Pat, PatType, PatIdent, FnArg, ImplItemFn, Stmt, ExprPath};
11use syn::{Receiver, TypeReference, AttrStyle};
12use syn::punctuated::Punctuated;
13use syn::spanned::Spanned;
14use syn::token::Bracket;
15
16fn parse_base_field_meta(meta: &Meta) -> bool {
17    match meta {
18        Meta::Path(path) if
19               path.leading_colon.is_none()
20            && path.segments.len() == 1
21            && path.segments[0].arguments.is_none()
22        => match path.segments[0].ident.to_string().as_ref() {
23            "non_virt" => false,
24            _ => true
25        },
26        _ => true
27    }
28}
29
30fn parse_base_field_attrs(attrs: &[Attribute]) -> Result<bool, Diagnostic> {
31    let mut is_virtual = true;
32    for attr in attrs {
33        if !parse_base_field_meta(&attr.meta) {
34            if !is_virtual {
35                return Err(
36                     attr.span()
37                    .error("invalid base class")
38                );
39            }
40            is_virtual = false;
41        }
42    }
43    Ok(is_virtual)
44}
45
46struct Base {
47    ty: Path,
48    non_virt_methods: Vec<(Ident, TypeBareFn)>,
49    virt_methods: Vec<(Ident, TypeBareFn)>,
50}
51
52fn parse_base_types(inherits: ItemStruct) -> Result<Vec<Base>, Diagnostic> {
53    let Fields::Named(fields) = inherits.fields else {
54        return Err(inherits.fields.span().error("invalid base class"));
55    };
56    let mut res = Vec::new();
57    let mut base = None;
58    for field in fields.named {
59        if field.ident.as_ref().unwrap().to_string() == "__class__" {
60            if let Some(base) = base.take() {
61                res.push(base);
62            }
63            let Type::Path(type_path) = field.ty else {
64                return Err(field.ty.span().error("invalid base class"));
65            };
66            if type_path.path.segments.len() != 1 {
67                return Err(type_path.span().error("invalid base class"));
68            }
69            base = Some(Base {
70                ty: type_path.path,
71                non_virt_methods: Vec::new(),
72                virt_methods: Vec::new()
73            });
74        } else {
75            let name = field.ident.as_ref().unwrap().clone();
76            let Type::BareFn(type_fn) = field.ty else {
77                return Err(field.ty.span().error("invalid base class"));
78            };
79            let Some(base) = base.as_mut() else {
80                return Err(type_fn.span().error("invalid base class"));
81            };
82            if parse_base_field_attrs(&field.attrs)? {
83                base.virt_methods.push((name, type_fn));
84            } else {
85                base.non_virt_methods.push((name, type_fn));
86            }
87        }
88    }
89    if let Some(base) = base.take() {
90        res.push(base);
91    }
92    Ok(res)
93}
94
95#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
96enum FieldKind {
97    NonVirtMethod,
98    VirtMethod,
99    Override,
100    Data,
101}
102
103fn parse_field_meta(meta: &Meta) -> FieldKind {
104    match meta {
105        Meta::Path(path) if
106               path.leading_colon.is_none()
107            && path.segments.len() == 1
108            && path.segments[0].arguments.is_none()
109        => match path.segments[0].ident.to_string().as_ref() {
110            "non_virt" => FieldKind::NonVirtMethod,
111            "virt" => FieldKind::VirtMethod,
112            "over" => FieldKind::Override,
113            _ => FieldKind::Data,
114        },
115        _ => FieldKind::Data,
116    }
117}
118
119fn parse_field_attrs(attrs: &[Attribute]) -> Result<FieldKind, Diagnostic> {
120    let mut kind = FieldKind::Data;
121    for attr in attrs {
122        match parse_field_meta(&attr.meta) {
123            FieldKind::Data => { },
124            FieldKind::NonVirtMethod => {
125                if kind != FieldKind::Data {
126                    return Err(
127                         attr.span()
128                        .error("only one of 'non_virt'/'virt'/'over' attributes can be specified for a field")
129                    );
130                }
131                kind = FieldKind::NonVirtMethod;
132            },
133            FieldKind::VirtMethod => {
134                if kind != FieldKind::Data {
135                    return Err(
136                         attr.span()
137                        .error("only one of 'non_virt'/'virt'/'over' attributes can be specified for a field")
138                    );
139                }
140                kind = FieldKind::VirtMethod;
141            },
142            FieldKind::Override => {
143                if kind != FieldKind::Data {
144                    return Err(
145                         attr.span()
146                        .error("only one of 'non_virt'/'virt'/'over' attributes can be specified for a field")
147                    );
148                }
149                kind = FieldKind::Override;
150            },
151        }
152    }
153    Ok(kind)
154}
155
156struct Class {
157    attrs: Vec<Attribute>,
158    vis: Visibility,
159    name: Ident,
160    fields: Vec<Field>,
161    non_virt_methods: Vec<(Ident, TypeBareFn)>,
162    virt_methods: Vec<(Ident, TypeBareFn)>,
163    overrides: Vec<Ident>,
164}
165
166impl Class {
167    fn parse(descr: ItemStruct) -> Result<Class, Diagnostic> {
168        if descr.generics.lt_token.is_some() || descr.generics.where_clause.is_some() {
169            return Err(descr.generics.span().error("basic-oop does not support generics"));
170        }
171        let mut fields = Vec::new();
172        let mut non_virt_methods = Vec::new();
173        let mut virt_methods = Vec::new();
174        let mut overrides = Vec::new();
175        let Fields::Named(descr_fields) = descr.fields else {
176            return Err(descr.fields.span().error("class should be described as struct with named fields"));
177        };
178        for field in descr_fields.named.iter() {
179            match parse_field_attrs(&field.attrs)? {
180                FieldKind::Data => fields.push(field.clone()),
181                FieldKind::NonVirtMethod => {
182                    let name = field.ident.clone().unwrap();
183                    if name.to_string() == "__class__" {
184                        return Err(name.span().error("this name is reserved"));
185                    }
186                    let Type::BareFn(type_fn) = &field.ty else {
187                        return Err(field.ty.span().error("invalid non-virtual method type"));
188                    };
189                    if
190                           type_fn.unsafety.is_some()
191                        || type_fn.abi.is_some()
192                        || type_fn.variadic.is_some()
193                        || type_fn.inputs.iter().any(|x| !x.attrs.is_empty())
194                    {
195                        return Err(field.ty.span().error("invalid non-virtual method type"));
196                    }
197                    if let Some(arg) = type_fn.inputs.iter().find(|x| x.name.is_none()) {
198                        return Err(arg.span().error("argument name required"));
199                    }
200                    non_virt_methods.push((name, type_fn.clone()));
201                },
202                FieldKind::VirtMethod => {
203                    let name = field.ident.clone().unwrap();
204                    if name.to_string() == "__class__" {
205                        return Err(name.span().error("this name is reserved"));
206                    }
207                    let Type::BareFn(type_fn) = &field.ty else {
208                        return Err(field.ty.span().error("invalid virtual method type"));
209                    };
210                    if
211                           type_fn.unsafety.is_some()
212                        || type_fn.abi.is_some()
213                        || type_fn.variadic.is_some()
214                        || type_fn.inputs.iter().any(|x| !x.attrs.is_empty())
215                    {
216                        return Err(field.ty.span().error("invalid virtual method type"));
217                    }
218                    if let Some(arg) = type_fn.inputs.iter().find(|x| x.name.is_none()) {
219                        return Err(arg.span().error("argument name required"));
220                    }
221                    virt_methods.push((name, type_fn.clone()));
222                },
223                FieldKind::Override => {
224                    let name = field.ident.clone().unwrap();
225                    let Type::Tuple(type_tuple) = &field.ty else {
226                        return Err(field.ty.span().error("invalid override method type"));
227                    };
228                    if !type_tuple.elems.is_empty() {
229                        return Err(field.ty.span().error("invalid override method type"));
230                    }
231                    overrides.push(name);
232                },
233            }
234        }
235        Ok(Class {
236            attrs: descr.attrs,
237            vis: descr.vis,
238            name: descr.ident,
239            fields,
240            non_virt_methods,
241            virt_methods,
242            overrides,
243        })
244    }
245}
246
247fn build_inherits(
248    base_types: &[Base],
249    class_name: &Ident,
250    non_virt_methods: &[(Ident, TypeBareFn)],
251    virt_methods: &[(Ident, TypeBareFn)]
252) -> ItemStruct {
253    let name = Ident::new(&("inherits_".to_string() + &class_name.to_string()), Span::call_site());
254    let mut struct_: ItemStruct = parse_quote! {
255        #[::basic_oop::macro_magic::export_tokens_no_emit]
256        struct #name {
257            __class__: #class_name
258        }
259    };
260    let Fields::Named(fields) = &mut struct_.fields else { panic!() };
261    for (method_name, method_ty) in non_virt_methods {
262        let mut segments = Punctuated::new();
263        segments.push(PathSegment {
264            ident: Ident::new("non_virt", Span::call_site()),
265            arguments: PathArguments::None
266        });
267        fields.named.push(Field {
268            attrs: vec![Attribute {
269                pound_token: <Token![#]>::default(),
270                style: AttrStyle::Outer,
271                bracket_token: Bracket::default(),
272                meta: Meta::Path(Path { leading_colon: None, segments }),
273            }],
274            vis: Visibility::Inherited,
275            mutability: FieldMutability::None,
276            ident: Some(method_name.clone()),
277            colon_token: Some(<Token![:]>::default()),
278            ty: Type::BareFn(method_ty.clone()),
279        });
280    }
281    for (method_name, method_ty) in virt_methods {
282        fields.named.push(Field {
283            attrs: Vec::new(),
284            vis: Visibility::Inherited,
285            mutability: FieldMutability::None,
286            ident: Some(method_name.clone()),
287            colon_token: Some(<Token![:]>::default()),
288            ty: Type::BareFn(method_ty.clone()),
289        });
290    }
291    for base_type in base_types {
292        fields.named.push(Field {
293            attrs: Vec::new(),
294            vis: Visibility::Inherited,
295            mutability: FieldMutability::None,
296            ident: Some(Ident::new("__class__", Span::call_site())),
297            colon_token: Some(<Token![:]>::default()),
298            ty: Type::Path(TypePath {
299                qself: None,
300                path: base_type.ty.clone()
301            }),
302        });
303        for (method_name, method_ty) in &base_type.non_virt_methods {
304            let mut segments = Punctuated::new();
305            segments.push(PathSegment {
306                ident: Ident::new("non_virt", Span::call_site()),
307                arguments: PathArguments::None
308            });
309            fields.named.push(Field {
310                attrs: vec![Attribute {
311                    pound_token: <Token![#]>::default(),
312                    style: AttrStyle::Outer,
313                    bracket_token: Bracket::default(),
314                    meta: Meta::Path(Path { leading_colon: None, segments }),
315                }],
316                vis: Visibility::Inherited,
317                mutability: FieldMutability::None,
318                ident: Some(method_name.clone()),
319                colon_token: Some(<Token![:]>::default()),
320                ty: Type::BareFn(method_ty.clone()),
321            });
322        }
323        for (method_name, method_ty) in &base_type.virt_methods {
324            fields.named.push(Field {
325                attrs: Vec::new(),
326                vis: Visibility::Inherited,
327                mutability: FieldMutability::None,
328                ident: Some(method_name.clone()),
329                colon_token: Some(<Token![:]>::default()),
330                ty: Type::BareFn(method_ty.clone()),
331            });
332        }
333    }
334    struct_
335}
336
337fn patch_path(path: &mut Path, f: impl FnOnce(String) -> String) {
338    let ident = f(path.segments.last().unwrap().ident.to_string());
339    path.segments.last_mut().unwrap().ident = Ident::new(&ident, Span::call_site());
340}
341
342fn build_attrs(attrs: &[Attribute]) -> TokenStream {
343    let mut tokens = TokenStream::new();
344    tokens.append_all(attrs);
345    tokens
346}
347
348fn build_struct(
349    base_types: &[Base],
350    attrs: &[Attribute],
351    vis: &Visibility,
352    name: &Ident,
353    fields: &[Field]
354) -> ItemStruct {
355    let base_type = base_types[0].ty.clone();
356    let base_field = Ident::new(&to_snake(base_type.segments.last().unwrap().ident.to_string()), Span::call_site());
357    let attrs = build_attrs(attrs);
358    let mut struct_: ItemStruct = parse_quote! {
359        #attrs
360        #vis struct #name {
361            #base_field: #base_type
362        }
363    };
364    let Fields::Named(struct_fields) = &mut struct_.fields else { panic!() };
365    for field in fields {
366        struct_fields.named.push(field.clone());
367    }
368    struct_
369}
370
371fn build_trait(base_types: &[Base], vis: &Visibility, class_name: &Ident) -> TokenStream {
372    let base_type = base_types[0].ty.clone();
373    let base_field = Ident::new(
374        &to_snake(base_type.segments.last().unwrap().ident.to_string()),
375        Span::call_site()
376    );
377    let mut base_trait = base_types[0].ty.clone();
378    patch_path(&mut base_trait, |x| "T".to_string() + &x);
379    let trait_name = Ident::new(&("T".to_string() + &class_name.to_string()), Span::call_site());
380    let method_name = Ident::new(&to_snake(class_name.to_string()), Span::call_site());
381    let mut trait_ = quote! {
382        #vis trait #trait_name: #base_trait {
383            fn #method_name(&self) -> &#class_name;
384        }
385
386        impl #trait_name for #class_name {
387            fn #method_name(&self) -> &#class_name { self }
388        }
389
390        impl #base_trait for #class_name {
391            fn #base_field(&self) -> &#base_type { &self.#base_field }
392        }
393    };
394    for base_base_type in base_types.iter().skip(1) {
395        let method_name = Ident::new(
396            &to_snake(base_base_type.ty.segments.last().unwrap().ident.to_string()),
397            Span::call_site()
398        );
399        let mut base_base_trait = base_base_type.ty.clone();
400        patch_path(&mut base_base_trait, |x| "T".to_string() + &x);
401        let base_base_type_ty = &base_base_type.ty;
402        trait_.extend(quote! {
403            impl #base_base_trait for #class_name {
404                fn #method_name(&self) -> &#base_base_type_ty {
405                    #base_base_trait::#method_name(&self.#base_field)
406                }
407            }
408        });
409    }
410    let mut traits_list: Punctuated<Path, Token![,]> = Punctuated::new();
411    let mut trait_path = Path {
412        leading_colon: None,
413        segments: Punctuated::new()
414    };
415    trait_path.segments.push(PathSegment {
416        ident: trait_name,
417        arguments: PathArguments::None
418    });
419    traits_list.push(trait_path);
420    for base_type in base_types {
421        let mut base_trait = base_type.ty.clone();
422        patch_path(&mut base_trait, |x| "T".to_string() + &x);
423        traits_list.push(base_trait);
424    }
425    trait_.extend(quote! {
426        ::basic_oop::dynamic_cast_impl_supports_interfaces!(#class_name: #traits_list);
427    });
428    trait_
429}
430
431fn build_virt_methods_enum(
432    base_type: &Path,
433    vis: &Visibility,
434    class_name: &Ident,
435    virt_methods: &[(Ident, TypeBareFn)]
436) -> TokenStream {
437    let mut base_methods_enum = base_type.clone();
438    patch_path(&mut base_methods_enum, |x| x + "VirtMethods");
439    let methods_enum = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
440    let mut values = TokenStream::new();
441    values.append_terminated(
442        virt_methods.iter().enumerate().map(|(i, (method_name, _method_ty))| {
443            let name = Ident::new(&to_pascal(method_name.to_string()), Span::call_site());
444            let index = LitInt::new(&(i.to_string() + "usize"), Span::call_site());
445            quote! {
446                #name = (#base_methods_enum::VirtMethodsCount as usize) + #index
447            }
448        }),
449        <Token![,]>::default()
450    );
451    let count = virt_methods.len();
452    quote! {
453        #[derive(Debug, Eq, PartialEq, Clone, Copy, Ord, PartialOrd, Hash)]
454        #[repr(usize)]
455        #vis enum #methods_enum {
456            #values
457            VirtMethodsCount = (#base_methods_enum::VirtMethodsCount as usize) + #count
458        }
459    }
460}
461
462fn build_consts_for_vtable(class_name: &Ident, base_types: &[Base]) -> TokenStream {
463    let enum_name = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
464    let mut tokens = TokenStream::new();
465    for base_type in base_types {
466        let mut base_methods_enum = base_type.ty.clone();
467        patch_path(&mut base_methods_enum, |x| x + "VirtMethods");
468        let base_const_name = Ident::new(
469            &(
470                to_screaming_snake(
471                    class_name.to_string() + &base_type.ty.segments.last().unwrap().ident.to_string()
472                ) + "_VIRT_METHODS_COUNT"
473            ),
474            Span::call_site()
475        );
476        let complement_const_name = Ident::new(&(base_const_name.to_string() + "_COMPL"), Span::call_site());
477        tokens.extend(quote! {
478            const #base_const_name: usize = #base_methods_enum::VirtMethodsCount as usize;
479            const #complement_const_name: usize = (#enum_name::VirtMethodsCount as usize) - #base_const_name;
480        });
481    }
482    tokens
483}
484
485fn actual_base_method_ty(mut ty: TypeBareFn, base_type: &Path, sync: bool) -> TypeBareFn {
486    let rc = if sync {
487        quote! { ::basic_oop::alloc_sync_Arc }
488    } else {
489        quote! { ::basic_oop::alloc_rc_Rc }
490    };
491    let mut base_trait = base_type.clone();
492    patch_path(&mut base_trait, |x| "T".to_string() + &x);
493    let this_arg = BareFnArg {
494        attrs: Vec::new(),
495        name: Some((Ident::new("this", Span::call_site()), <Token![:]>::default())),
496        ty: parse_quote! { &#rc<dyn #base_trait> },
497    };
498    ty.inputs.insert(0, this_arg);
499    ty
500}
501
502fn actual_method_ty(mut ty: TypeBareFn, class_name: &Ident, sync: bool) -> TypeBareFn {
503    let rc = if sync {
504        quote! { ::basic_oop::alloc_sync_Arc }
505    } else {
506        quote! { ::basic_oop::alloc_rc_Rc }
507    };
508    let trait_name = Ident::new(&("T".to_string() + &class_name.to_string()), Span::call_site());
509    let this_arg = BareFnArg {
510        attrs: Vec::new(),
511        name: Some((Ident::new("this", Span::call_site()), <Token![:]>::default())),
512        ty: parse_quote! { &#rc<dyn #trait_name> },
513    };
514    ty.inputs.insert(0, this_arg);
515    ty
516}
517
518fn fn_ty_without_idents(mut ty: TypeBareFn) -> TypeBareFn {
519    for arg in &mut ty.inputs {
520        arg.name = None;
521    }
522    ty
523}
524
525fn bare_fn_arg_to_fn_arg(a: &BareFnArg) -> FnArg {
526    let Some((name, colon_token)) = &a.name else { panic!() }; 
527    FnArg::Typed(PatType {
528        attrs: Vec::new(),
529        pat: Box::new(Pat::Ident(PatIdent {
530            attrs: Vec::new(),
531            by_ref: None,
532            mutability: None,
533            ident: name.clone(),
534            subpat: None
535        })),
536        colon_token: colon_token.clone(),
537        ty: Box::new(a.ty.clone()),
538    })
539}
540
541fn method_signature(ty: &TypeBareFn, name: Ident) -> Signature {
542    let generics = if let Some(lifetimes) = &ty.lifetimes {
543        Generics {
544            lt_token: Some(lifetimes.lt_token),
545            params: lifetimes.lifetimes.clone(),
546            gt_token: Some(lifetimes.gt_token),
547            where_clause: None,
548        }
549    } else {
550        Generics {
551            lt_token: None,
552            params: Punctuated::new(),
553            gt_token: None,
554            where_clause: None,
555        }
556    };
557    let mut s = Signature {
558        constness: None,
559        asyncness: None,
560        unsafety: None,
561        abi: None,
562        fn_token: ty.fn_token.clone(),
563        ident: name,
564        generics,
565        paren_token: ty.paren_token.clone(),
566        inputs: Punctuated::new(),
567        variadic: None,
568        output: ty.output.clone(),
569    };
570    let mut segments = Punctuated::new();
571    segments.push(PathSegment {
572        ident: Ident::new("Self", Span::call_site()),
573        arguments: PathArguments::None
574    });
575    s.inputs.push(FnArg::Receiver(Receiver {
576        attrs: Vec::new(),
577        reference: Some((<Token![&]>::default(), None)),
578        mutability: None,
579        self_token: <Token![self]>::default(),
580        colon_token: None,
581        ty: Box::new(Type::Reference(TypeReference {
582            and_token: <Token![&]>::default(),
583            lifetime: None,
584            mutability: None,
585            elem: Box::new(Type::Path(TypePath {
586                qself: None,
587                path: Path {
588                    leading_colon: None,
589                    segments
590                }
591            }))
592        }))
593    }));
594    for arg in ty.inputs.iter().skip(1) {
595        s.inputs.push(bare_fn_arg_to_fn_arg(arg));
596    }
597    s
598}
599
600fn build_vtable(
601    base_types: &[Base],
602    class_name: &Ident,
603    sync: bool,
604    vis: &Visibility,
605    virt_methods: &[(Ident, TypeBareFn)],
606    overrides: &[Ident],
607) -> TokenStream {
608    let vtable_name = Ident::new(&(class_name.to_string() + "Vtable"), Span::call_site());
609    let methods_enum_name = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
610    let struct_ = quote! {
611        #vis struct #vtable_name(pub [*const (); #methods_enum_name::VirtMethodsCount as usize]);
612    };
613    let mut methods_impl = TokenStream::new();
614    methods_impl.append_separated(virt_methods.iter().map(|(m, ty)| {
615        let ty = actual_method_ty(ty.clone(), class_name, sync);
616        let ty_without_idents = fn_ty_without_idents(ty);
617        let impl_name = Ident::new(&(m.to_string() + "_impl"), Span::call_site());
618        quote! { { let f: #ty_without_idents = #class_name::#impl_name; f as *const () } }
619    }), <Token![,]>::default());
620    let mut base_vtable = base_types[0].ty.clone();
621    patch_path(&mut base_vtable, |x| x + "Vtable");
622    let base_vtable_new: Expr = parse_quote! { #base_vtable::new() };
623    let mut base_vtable_with_overrides = base_vtable_new;
624    for override_name in overrides {
625        let impl_name = Ident::new(&(override_name.to_string() + "_impl"), Span::call_site());
626        base_vtable_with_overrides = parse_quote! {
627            #base_vtable_with_overrides.#override_name(#class_name::#impl_name)
628        };
629    }
630    let base_const_name = Ident::new(
631        &(
632            to_screaming_snake(
633                class_name.to_string() + &base_types[0].ty.segments.last().unwrap().ident.to_string()
634            ) + "_VIRT_METHODS_COUNT"
635        ),
636        Span::call_site()
637    );
638    let complement_const_name = Ident::new(&(base_const_name.to_string() + "_COMPL"), Span::call_site());
639    let mut base_methods = TokenStream::new();
640    for base_type in base_types {
641        let mut base_vtable = base_type.ty.clone();
642        patch_path(&mut base_vtable, |x| x + "Vtable");
643        let base_const_name = Ident::new(
644            &(
645                to_screaming_snake(
646                    class_name.to_string() + &base_type.ty.segments.last().unwrap().ident.to_string()
647                ) + "_VIRT_METHODS_COUNT"
648            ),
649            Span::call_site()
650        );
651        let complement_const_name = Ident::new(&(base_const_name.to_string() + "_COMPL"), Span::call_site());
652        for (base_method, base_method_ty) in &base_type.virt_methods {
653            let ty = actual_base_method_ty(base_method_ty.clone(), &base_type.ty, sync);
654            let ty_without_idents = fn_ty_without_idents(ty);
655            base_methods.extend(quote! {
656                pub const fn #base_method(
657                    self,
658                    f: #ty_without_idents
659                ) -> Self {
660                    let vtable = unsafe { ::basic_oop::core_mem_transmute::<
661                        [*const (); #methods_enum_name::VirtMethodsCount as usize],
662                        ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>
663                    >(self.0) };
664                    let vtable: ::basic_oop::VtableJoin<
665                        #base_const_name,
666                        #complement_const_name
667                    > = ::basic_oop::VtableJoin {
668                        a: #base_vtable(vtable.a).#base_method(f).0,
669                        b: vtable.b
670                    };
671                    #vtable_name(unsafe { ::basic_oop::core_mem_transmute::<
672                        ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>,
673                        [*const (); #methods_enum_name::VirtMethodsCount as usize]
674                    >(vtable) })
675                }
676            });
677        }
678    }
679    let mut methods_tokens = TokenStream::new();
680    for (method_index, (method_name, method_ty)) in virt_methods.iter().enumerate() {
681        let ty = actual_method_ty(method_ty.clone(), class_name, sync);
682        let ty_without_idents = fn_ty_without_idents(ty);
683        let mut list: Punctuated<Expr, Token![,]> = Punctuated::new();
684        for i in 0 .. method_index {
685            let index = LitInt::new(&(i.to_string() + "usize"), Span::call_site());
686            list.push(parse_quote! { vtable.b[#index] });
687        }
688        list.push(parse_quote! { f as *const () });
689        for i in method_index + 1 .. virt_methods.len() {
690            let index = LitInt::new(&(i.to_string() + "usize"), Span::call_site());
691            list.push(parse_quote! { vtable.b[#index] });
692        }
693        methods_tokens.extend(quote! {
694            pub const fn #method_name(
695                self,
696                f: #ty_without_idents
697            ) -> Self {
698                let vtable = unsafe { ::basic_oop::core_mem_transmute::<
699                    [*const (); #methods_enum_name::VirtMethodsCount as usize],
700                    ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>
701                >(self.0) };
702                let vtable: ::basic_oop::VtableJoin<
703                    #base_const_name,
704                    #complement_const_name
705                > = ::basic_oop::VtableJoin {
706                    a: vtable.a,
707                    b: [#list]
708                };
709                #vtable_name(unsafe { ::basic_oop::core_mem_transmute::<
710                    ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>,
711                    [*const (); #methods_enum_name::VirtMethodsCount as usize]
712                >(vtable) })
713            }
714        });
715    }
716    quote! {
717        #struct_
718
719        impl #vtable_name {
720            pub const fn new() -> Self {
721                let vtable: ::basic_oop::VtableJoin<
722                    #base_const_name,
723                    #complement_const_name
724                > = ::basic_oop::VtableJoin {
725                    a: #base_vtable_with_overrides.0,
726                    b: [#methods_impl]
727                };
728                #vtable_name(unsafe { ::basic_oop::core_mem_transmute::<
729                    ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>,
730                    [*const (); #methods_enum_name::VirtMethodsCount as usize]
731                >(vtable) })
732            }
733
734            #base_methods
735            #methods_tokens
736        }
737    }
738}
739
740fn build_methods(
741    base_types: &[Base],
742    class_name: &Ident,
743    sync: bool,
744    non_virt_methods: &[(Ident, TypeBareFn)],
745    virt_methods: &[(Ident, TypeBareFn)]
746) -> TokenStream {
747    let (rc, cast) = if sync {
748        (quote! { ::basic_oop::alloc_sync_Arc }, quote! { ::basic_oop::dynamic_cast_dyn_cast_arc })
749    } else {
750        (quote! { ::basic_oop::alloc_rc_Rc }, quote! { ::basic_oop::dynamic_cast_dyn_cast_rc })
751    };
752    let methods_enum_name = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
753    let mut methods_tokens = TokenStream::new();
754    for base_type in base_types {
755        let base_type_ty = base_type.ty.clone();
756        let mut base_trait = base_type_ty.clone();
757        patch_path(&mut base_trait, |x| "T".to_string() + &x);
758        let mut base_trait_ext = base_type_ty.clone();
759        patch_path(&mut base_trait_ext, |x| x + "Ext");
760        for (method_name, method_ty) in base_type.non_virt_methods.iter().chain(base_type.virt_methods.iter()) {
761            let ty = actual_method_ty(method_ty.clone(), class_name, sync);
762            let signature = method_signature(&ty, method_name.clone());
763            let mut item: ImplItemFn = parse_quote! {
764                #signature {
765                    let this: #rc<dyn #base_trait> = #cast(self.clone()).unwrap();
766                    #base_trait_ext::#method_name(&this)
767                }
768            };
769            let Stmt::Expr(Expr::Call(call), _) = item.block.stmts.last_mut().unwrap() else { panic!() };
770            for arg in ty.inputs.iter().skip(1) {
771                let mut segments = Punctuated::new();
772                segments.push(PathSegment {
773                    ident: arg.name.clone().unwrap().0,
774                    arguments: PathArguments::None,
775                });
776                call.args.push(Expr::Path(ExprPath {
777                    attrs: Vec::new(),
778                    qself: None,
779                    path: Path { leading_colon: None, segments },
780                }));
781            }
782            item.to_tokens(&mut methods_tokens);
783        }
784    }
785    for (method_name, method_ty) in non_virt_methods {
786        let ty = actual_method_ty(method_ty.clone(), class_name, sync);
787        let signature = method_signature(&ty, method_name.clone());
788        let name = Ident::new(&(method_name.to_string() + "_impl"), Span::call_site());
789        let mut item: ImplItemFn = parse_quote! {
790            #signature {
791                #class_name::#name(self)
792            }
793        };
794        let Stmt::Expr(Expr::Call(call), _) = item.block.stmts.last_mut().unwrap() else { panic!() };
795        for arg in ty.inputs.into_iter().skip(1) {
796            let mut segments = Punctuated::new();
797            segments.push(PathSegment {
798                ident: arg.name.unwrap().0,
799                arguments: PathArguments::None,
800            });
801            call.args.push(Expr::Path(ExprPath {
802                attrs: Vec::new(),
803                qself: None,
804                path: Path { leading_colon: None, segments },
805            }));
806        }
807        item.to_tokens(&mut methods_tokens);
808    }
809    for (method_name, method_ty) in virt_methods {
810        let ty = actual_method_ty(method_ty.clone(), class_name, sync);
811        let signature = method_signature(&ty, method_name.clone());
812        let ty_without_idents = fn_ty_without_idents(ty.clone());
813        let name = Ident::new(&to_pascal(method_name.to_string()), Span::call_site());
814        let mut item: ImplItemFn = parse_quote! {
815            #signature {
816                let vtable = ::basic_oop::obj::TObj::obj(self.as_ref()).vtable();
817                let method = unsafe { ::basic_oop::core_mem_transmute::<*const (), #ty_without_idents>(
818                    *vtable.add(#methods_enum_name::#name as usize)
819                ) };
820                method(self)
821            }
822        };
823        let Stmt::Expr(Expr::Call(call), _) = item.block.stmts.last_mut().unwrap() else { panic!() };
824        for arg in ty.inputs.into_iter().skip(1) {
825            let mut segments = Punctuated::new();
826            segments.push(PathSegment {
827                ident: arg.name.unwrap().0,
828                arguments: PathArguments::None,
829            });
830            call.args.push(Expr::Path(ExprPath {
831                attrs: Vec::new(),
832                qself: None,
833                path: Path { leading_colon: None, segments },
834            }));
835        }
836        item.to_tokens(&mut methods_tokens);
837    }
838    let trait_name = Ident::new(&(class_name.to_string() + "Ext"), Span::call_site());
839    let t = Ident::new(&("T".to_string() + &class_name.to_string()), Span::call_site());
840    quote! {
841        impl #trait_name for #rc<dyn #t> {
842            #methods_tokens
843        }
844    }
845}
846
847fn build_vtable_const(class_name: &Ident) -> TokenStream {
848    let methods_enum_name = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
849    let vtable_name = Ident::new(&(class_name.to_string() + "Vtable"), Span::call_site());
850    let const_name = Ident::new(&to_screaming_snake(vtable_name.to_string()), Span::call_site());
851    quote! {
852        const #const_name: [*const (); #methods_enum_name::VirtMethodsCount as usize] = #vtable_name::new().0;
853    }
854}
855
856fn build_call_trait(
857    base_types: &[Base],
858    vis: &Visibility,
859    class_name: &Ident,
860    sync: bool,
861    non_virt_methods: &[(Ident, TypeBareFn)],
862    virt_methods: &[(Ident, TypeBareFn)]
863) -> TokenStream {
864    let mut methods_tokens = TokenStream::new();
865    for base_type in base_types {
866        for (method_name, method_ty) in base_type.non_virt_methods.iter().chain(base_type.virt_methods.iter()) {
867            let ty = actual_method_ty(method_ty.clone(), class_name, sync);
868            let signature = method_signature(&ty, method_name.clone());
869            signature.to_tokens(&mut methods_tokens);
870            <Token![;]>::default().to_tokens(&mut methods_tokens);
871        }
872    }
873    for (method_name, method_ty) in non_virt_methods.iter().chain(virt_methods.iter()) {
874        let ty = actual_method_ty(method_ty.clone(), class_name, sync);
875        let signature = method_signature(&ty, method_name.clone());
876        signature.to_tokens(&mut methods_tokens);
877        <Token![;]>::default().to_tokens(&mut methods_tokens);
878    }
879    let trait_name = Ident::new(&(class_name.to_string() + "Ext"), Span::call_site());
880    quote! {
881        #vis trait #trait_name {
882            #methods_tokens
883        }
884    }
885}
886
887fn build(inherits: ItemStruct, class: ItemStruct, sync: bool) -> Result<TokenStream, Diagnostic> {
888    let base_types = parse_base_types(inherits)?;
889    let class = Class::parse(class)?;
890    let new_inherits = build_inherits(
891        &base_types, &class.name, &class.non_virt_methods, &class.virt_methods
892    );
893    let struct_ = build_struct(&base_types, &class.attrs, &class.vis, &class.name, &class.fields);
894    let trait_ = build_trait(&base_types, &class.vis, &class.name);
895    let methods_enum = build_virt_methods_enum(
896        &base_types[0].ty, &class.vis, &class.name, &class.virt_methods
897    );
898    let consts_for_vtable = build_consts_for_vtable(&class.name, &base_types);
899    let vtable = build_vtable(
900        &base_types, &class.name, sync, &class.vis, &class.virt_methods, &class.overrides
901    );
902    let vtable_const = build_vtable_const(&class.name);
903    let call_trait = build_call_trait(
904        &base_types, &class.vis, &class.name, sync, &class.non_virt_methods, &class.virt_methods
905    );
906    let methods = build_methods(
907        &base_types, &class.name, sync, &class.non_virt_methods, &class.virt_methods
908    );
909    Ok(quote! {
910        #new_inherits
911        #struct_
912        #trait_
913        #methods_enum
914        #consts_for_vtable
915        #vtable
916        #vtable_const
917        #call_trait
918        #methods
919    })
920}
921
922#[import_tokens_attr(::basic_oop::macro_magic)]
923#[proc_macro_attribute]
924pub fn class_sync_unsafe(attr: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
925    let inherits = parse_macro_input!(attr as ItemStruct);
926    let class = parse_macro_input!(input as ItemStruct);
927    match build(inherits, class, true) {
928        Ok(tokens) => tokens.into(),
929        Err(diag) => diag.emit_as_expr_tokens().into(),
930    }
931}
932
933#[import_tokens_attr(::basic_oop::macro_magic)]
934#[proc_macro_attribute]
935pub fn class_unsafe(attr: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
936    let inherits = parse_macro_input!(attr as ItemStruct);
937    let class = parse_macro_input!(input as ItemStruct);
938    match build(inherits, class, false) {
939        Ok(tokens) => tokens.into(),
940        Err(diag) => diag.emit_as_expr_tokens().into(),
941    }
942}