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