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