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