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