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}