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(inherited_from: ItemStruct) -> Result<Vec<Base>, Diagnostic> {
53    let Fields::Named(fields) = inherited_from.fields else {
54        return Err(inherited_from.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_inherited_from(
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(&("inherited_from_".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, _)| {
615        let impl_name = Ident::new(&(m.to_string() + "_impl"), Span::call_site());
616        quote! { #class_name::#impl_name as *const () }
617    }), <Token![,]>::default());
618    let mut base_vtable = base_types[0].ty.clone();
619    patch_path(&mut base_vtable, |x| x + "Vtable");
620    let base_vtable_new: Expr = parse_quote! { #base_vtable::new() };
621    let mut base_vtable_with_overrides = base_vtable_new;
622    for override_name in overrides {
623        let impl_name = Ident::new(&(override_name.to_string() + "_impl"), Span::call_site());
624        base_vtable_with_overrides = parse_quote! {
625            #base_vtable_with_overrides.#override_name(#class_name::#impl_name)
626        };
627    }
628    let base_const_name = Ident::new(
629        &(
630            to_screaming_snake(
631                class_name.to_string() + &base_types[0].ty.segments.last().unwrap().ident.to_string()
632            ) + "_VIRT_METHODS_COUNT"
633        ),
634        Span::call_site()
635    );
636    let complement_const_name = Ident::new(&(base_const_name.to_string() + "_COMPL"), Span::call_site());
637    let mut base_methods = TokenStream::new();
638    for base_type in base_types {
639        let mut base_vtable = base_type.ty.clone();
640        patch_path(&mut base_vtable, |x| x + "Vtable");
641        let base_const_name = Ident::new(
642            &(
643                to_screaming_snake(
644                    class_name.to_string() + &base_type.ty.segments.last().unwrap().ident.to_string()
645                ) + "_VIRT_METHODS_COUNT"
646            ),
647            Span::call_site()
648        );
649        let complement_const_name = Ident::new(&(base_const_name.to_string() + "_COMPL"), Span::call_site());
650        for (base_method, base_method_ty) in &base_type.virt_methods {
651            let ty = actual_base_method_ty(base_method_ty.clone(), &base_type.ty, sync);
652            let ty_without_idents = fn_ty_without_idents(ty);
653            base_methods.extend(quote! {
654                pub const fn #base_method(
655                    self,
656                    f: #ty_without_idents
657                ) -> Self {
658                    let vtable = unsafe { ::basic_oop::core_mem_transmute::<
659                        [*const (); #methods_enum_name::VirtMethodsCount as usize],
660                        ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>
661                    >(self.0) };
662                    let vtable: ::basic_oop::VtableJoin<
663                        #base_const_name,
664                        #complement_const_name
665                    > = ::basic_oop::VtableJoin {
666                        a: #base_vtable(vtable.a).#base_method(f).0,
667                        b: vtable.b
668                    };
669                    #vtable_name(unsafe { ::basic_oop::core_mem_transmute::<
670                        ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>,
671                        [*const (); #methods_enum_name::VirtMethodsCount as usize]
672                    >(vtable) })
673                }
674            });
675        }
676    }
677    let mut methods_tokens = TokenStream::new();
678    for (method_index, (method_name, method_ty)) in virt_methods.iter().enumerate() {
679        let ty = actual_method_ty(method_ty.clone(), class_name, sync);
680        let ty_without_idents = fn_ty_without_idents(ty);
681        let mut list: Punctuated<Expr, Token![,]> = Punctuated::new();
682        for i in 0 .. method_index {
683            let index = LitInt::new(&(i.to_string() + "usize"), Span::call_site());
684            list.push(parse_quote! { vtable.b[#index] });
685        }
686        list.push(parse_quote! { f as *const () });
687        for i in method_index + 1 .. virt_methods.len() {
688            let index = LitInt::new(&(i.to_string() + "usize"), Span::call_site());
689            list.push(parse_quote! { vtable.b[#index] });
690        }
691        methods_tokens.extend(quote! {
692            pub const fn #method_name(
693                self,
694                f: #ty_without_idents
695            ) -> Self {
696                let vtable = unsafe { ::basic_oop::core_mem_transmute::<
697                    [*const (); #methods_enum_name::VirtMethodsCount as usize],
698                    ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>
699                >(self.0) };
700                let vtable: ::basic_oop::VtableJoin<
701                    #base_const_name,
702                    #complement_const_name
703                > = ::basic_oop::VtableJoin {
704                    a: vtable.a,
705                    b: [#list]
706                };
707                #vtable_name(unsafe { ::basic_oop::core_mem_transmute::<
708                    ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>,
709                    [*const (); #methods_enum_name::VirtMethodsCount as usize]
710                >(vtable) })
711            }
712        });
713    }
714    quote! {
715        #struct_
716
717        impl #vtable_name {
718            pub const fn new() -> Self {
719                let vtable: ::basic_oop::VtableJoin<
720                    #base_const_name,
721                    #complement_const_name
722                > = ::basic_oop::VtableJoin {
723                    a: #base_vtable_with_overrides.0,
724                    b: [#methods_impl]
725                };
726                #vtable_name(unsafe { ::basic_oop::core_mem_transmute::<
727                    ::basic_oop::VtableJoin<#base_const_name, #complement_const_name>,
728                    [*const (); #methods_enum_name::VirtMethodsCount as usize]
729                >(vtable) })
730            }
731
732            #base_methods
733            #methods_tokens
734        }
735    }
736}
737
738fn build_methods(
739    base_types: &[Base],
740    class_name: &Ident,
741    sync: bool,
742    non_virt_methods: &[(Ident, TypeBareFn)],
743    virt_methods: &[(Ident, TypeBareFn)]
744) -> TokenStream {
745    let (rc, cast) = if sync {
746        (quote! { ::basic_oop::alloc_sync_Arc }, quote! { ::basic_oop::dynamic_cast_dyn_cast_arc })
747    } else {
748        (quote! { ::basic_oop::alloc_rc_Rc }, quote! { ::basic_oop::dynamic_cast_dyn_cast_rc })
749    };
750    let methods_enum_name = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
751    let mut methods_tokens = TokenStream::new();
752    for base_type in base_types {
753        let base_type_ty = base_type.ty.clone();
754        let mut base_trait = base_type_ty.clone();
755        patch_path(&mut base_trait, |x| "T".to_string() + &x);
756        let mut base_trait_ext = base_type_ty.clone();
757        patch_path(&mut base_trait_ext, |x| x + "Ext");
758        for (method_name, method_ty) in base_type.non_virt_methods.iter().chain(base_type.virt_methods.iter()) {
759            let ty = actual_method_ty(method_ty.clone(), class_name, sync);
760            let signature = method_signature(&ty, method_name.clone());
761            let mut item: ImplItemFn = parse_quote! {
762                #signature {
763                    let this: #rc<dyn #base_trait> = #cast(self.clone()).unwrap();
764                    #base_trait_ext::#method_name(&this)
765                }
766            };
767            let Stmt::Expr(Expr::Call(call), _) = item.block.stmts.last_mut().unwrap() else { panic!() };
768            for arg in ty.inputs.iter().skip(1) {
769                let mut segments = Punctuated::new();
770                segments.push(PathSegment {
771                    ident: arg.name.clone().unwrap().0,
772                    arguments: PathArguments::None,
773                });
774                call.args.push(Expr::Path(ExprPath {
775                    attrs: Vec::new(),
776                    qself: None,
777                    path: Path { leading_colon: None, segments },
778                }));
779            }
780            item.to_tokens(&mut methods_tokens);
781        }
782    }
783    for (method_name, method_ty) in non_virt_methods {
784        let ty = actual_method_ty(method_ty.clone(), class_name, sync);
785        let signature = method_signature(&ty, method_name.clone());
786        let name = Ident::new(&(method_name.to_string() + "_impl"), Span::call_site());
787        let mut item: ImplItemFn = parse_quote! {
788            #signature {
789                #class_name::#name(self)
790            }
791        };
792        let Stmt::Expr(Expr::Call(call), _) = item.block.stmts.last_mut().unwrap() else { panic!() };
793        for arg in ty.inputs.into_iter().skip(1) {
794            let mut segments = Punctuated::new();
795            segments.push(PathSegment {
796                ident: arg.name.unwrap().0,
797                arguments: PathArguments::None,
798            });
799            call.args.push(Expr::Path(ExprPath {
800                attrs: Vec::new(),
801                qself: None,
802                path: Path { leading_colon: None, segments },
803            }));
804        }
805        item.to_tokens(&mut methods_tokens);
806    }
807    for (method_name, method_ty) in virt_methods {
808        let ty = actual_method_ty(method_ty.clone(), class_name, sync);
809        let signature = method_signature(&ty, method_name.clone());
810        let ty_without_idents = fn_ty_without_idents(ty.clone());
811        let name = Ident::new(&to_pascal(method_name.to_string()), Span::call_site());
812        let mut item: ImplItemFn = parse_quote! {
813            #signature {
814                let vtable = ::basic_oop::TObj::obj(self.as_ref()).vtable();
815                let method = unsafe { ::basic_oop::core_mem_transmute::<*const (), #ty_without_idents>(
816                    *vtable.add(#methods_enum_name::#name as usize)
817                ) };
818                method(self)
819            }
820        };
821        let Stmt::Expr(Expr::Call(call), _) = item.block.stmts.last_mut().unwrap() else { panic!() };
822        for arg in ty.inputs.into_iter().skip(1) {
823            let mut segments = Punctuated::new();
824            segments.push(PathSegment {
825                ident: arg.name.unwrap().0,
826                arguments: PathArguments::None,
827            });
828            call.args.push(Expr::Path(ExprPath {
829                attrs: Vec::new(),
830                qself: None,
831                path: Path { leading_colon: None, segments },
832            }));
833        }
834        item.to_tokens(&mut methods_tokens);
835    }
836    let trait_name = Ident::new(&(class_name.to_string() + "Ext"), Span::call_site());
837    let t = Ident::new(&("T".to_string() + &class_name.to_string()), Span::call_site());
838    quote! {
839        impl #trait_name for #rc<dyn #t> {
840            #methods_tokens
841        }
842    }
843}
844
845fn build_vtable_const(class_name: &Ident) -> TokenStream {
846    let methods_enum_name = Ident::new(&(class_name.to_string() + "VirtMethods"), Span::call_site());
847    let vtable_name = Ident::new(&(class_name.to_string() + "Vtable"), Span::call_site());
848    let const_name = Ident::new(&to_screaming_snake(vtable_name.to_string()), Span::call_site());
849    quote! {
850        const #const_name: [*const (); #methods_enum_name::VirtMethodsCount as usize] = #vtable_name::new().0;
851    }
852}
853
854fn build_call_trait(
855    base_types: &[Base],
856    vis: &Visibility,
857    class_name: &Ident,
858    sync: bool,
859    non_virt_methods: &[(Ident, TypeBareFn)],
860    virt_methods: &[(Ident, TypeBareFn)]
861) -> TokenStream {
862    let mut methods_tokens = TokenStream::new();
863    for base_type in base_types {
864        for (method_name, method_ty) in base_type.non_virt_methods.iter().chain(base_type.virt_methods.iter()) {
865            let ty = actual_method_ty(method_ty.clone(), class_name, sync);
866            let signature = method_signature(&ty, method_name.clone());
867            signature.to_tokens(&mut methods_tokens);
868            <Token![;]>::default().to_tokens(&mut methods_tokens);
869        }
870    }
871    for (method_name, method_ty) in non_virt_methods.iter().chain(virt_methods.iter()) {
872        let ty = actual_method_ty(method_ty.clone(), class_name, sync);
873        let signature = method_signature(&ty, method_name.clone());
874        signature.to_tokens(&mut methods_tokens);
875        <Token![;]>::default().to_tokens(&mut methods_tokens);
876    }
877    let trait_name = Ident::new(&(class_name.to_string() + "Ext"), Span::call_site());
878    quote! {
879        #vis trait #trait_name {
880            #methods_tokens
881        }
882    }
883}
884
885fn build(inherited_from: ItemStruct, class: ItemStruct, sync: bool) -> Result<TokenStream, Diagnostic> {
886    let base_types = parse_base_types(inherited_from)?;
887    let class = Class::parse(class)?;
888    let new_inherited_from = build_inherited_from(
889        &base_types, &class.name, &class.non_virt_methods, &class.virt_methods
890    );
891    let struct_ = build_struct(&base_types, &class.attrs, &class.vis, &class.name, &class.fields);
892    let trait_ = build_trait(&base_types, &class.vis, &class.name);
893    let methods_enum = build_virt_methods_enum(
894        &base_types[0].ty, &class.vis, &class.name, &class.virt_methods
895    );
896    let consts_for_vtable = build_consts_for_vtable(&class.name, &base_types);
897    let vtable = build_vtable(
898        &base_types, &class.name, sync, &class.vis, &class.virt_methods, &class.overrides
899    );
900    let vtable_const = build_vtable_const(&class.name);
901    let call_trait = build_call_trait(
902        &base_types, &class.vis, &class.name, sync, &class.non_virt_methods, &class.virt_methods
903    );
904    let methods = build_methods(
905        &base_types, &class.name, sync, &class.non_virt_methods, &class.virt_methods
906    );
907    Ok(quote! {
908        #new_inherited_from
909        #struct_
910        #trait_
911        #methods_enum
912        #consts_for_vtable
913        #vtable
914        #vtable_const
915        #call_trait
916        #methods
917    })
918}
919
920#[import_tokens_attr(::basic_oop::macro_magic)]
921#[proc_macro_attribute]
922pub fn class_sync_unsafe(attr: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
923    let inherited_from = parse_macro_input!(attr as ItemStruct);
924    let class = parse_macro_input!(input as ItemStruct);
925    match build(inherited_from, class, true) {
926        Ok(tokens) => tokens.into(),
927        Err(diag) => diag.emit_as_expr_tokens().into(),
928    }
929}
930
931#[import_tokens_attr(::basic_oop::macro_magic)]
932#[proc_macro_attribute]
933pub fn class_unsafe(attr: proc_macro::TokenStream, input: proc_macro::TokenStream) -> proc_macro::TokenStream {
934    let inherited_from = parse_macro_input!(attr as ItemStruct);
935    let class = parse_macro_input!(input as ItemStruct);
936    match build(inherited_from, class, false) {
937        Ok(tokens) => tokens.into(),
938        Err(diag) => diag.emit_as_expr_tokens().into(),
939    }
940}