1use crate::contract::common::{derive_ident_metadata, extract_ident_from_type};
2use ident_case::RenameRule;
3use proc_macro2::{Ident, Literal, Span, TokenStream};
4use quote::{format_ident, quote, quote_spanned};
5use syn::spanned::Spanned;
6use syn::{
7 Attribute, Error, GenericArgument, Meta, Pat, PatType, PathArguments, ReturnType, Signature,
8 Token, Type, TypeTuple,
9};
10
11#[derive(Copy, Clone)]
12pub(super) enum MethodType {
13 Init,
14 Update,
15 View,
16}
17
18impl MethodType {
19 fn attr_str(self) -> &'static str {
20 match self {
21 MethodType::Init => "init",
22 MethodType::Update => "update",
23 MethodType::View => "view",
24 }
25 }
26}
27
28#[derive(Clone)]
29struct Env {
30 arg_name: Ident,
31 mutability: Option<Token![mut]>,
32}
33
34#[derive(Clone)]
35struct Tmp {
36 type_name: Type,
37 arg_name: Ident,
38 mutability: Option<Token![mut]>,
39}
40
41#[derive(Clone)]
42struct Slot {
43 with_address_arg: bool,
44 type_name: Type,
45 arg_name: Ident,
46 mutability: Option<Token![mut]>,
47}
48
49#[derive(Clone)]
50struct Input {
51 type_name: Type,
52 arg_name: Ident,
53}
54
55#[derive(Clone)]
56struct Output {
57 type_name: Type,
58 arg_name: Ident,
59 has_self: bool,
60}
61
62enum MethodReturnType {
63 Unit(Type),
65 Regular(Type),
67 ResultUnit(Type),
69 Result(Type),
71}
72
73impl MethodReturnType {
74 fn unit_type() -> Type {
75 Type::Tuple(TypeTuple {
76 paren_token: Default::default(),
77 elems: Default::default(),
78 })
79 }
80
81 fn unit_return_type(&self) -> bool {
82 match self {
83 Self::Unit(_) | Self::ResultUnit(_) => true,
84 Self::Regular(_) | Self::Result(_) => false,
85 }
86 }
87
88 fn return_type(&self) -> &Type {
89 match self {
90 Self::Unit(ty) | Self::Regular(ty) | Self::ResultUnit(ty) | Self::Result(ty) => ty,
91 }
92 }
93}
94
95#[derive(Default)]
96pub(super) struct ExtTraitComponents {
97 pub(super) definition: TokenStream,
98 pub(super) r#impl: TokenStream,
99}
100
101pub(super) struct MethodDetails {
102 method_type: MethodType,
103 self_type: Type,
104 state: Option<Option<Token![mut]>>,
105 env: Option<Env>,
106 tmp: Option<Tmp>,
107 slots: Vec<Slot>,
108 inputs: Vec<Input>,
109 outputs: Vec<Output>,
110 return_type: MethodReturnType,
111}
112
113impl MethodDetails {
114 pub(super) fn new(method_type: MethodType, self_type: Type) -> Self {
115 Self {
116 method_type,
117 self_type,
118 state: None,
119 env: None,
120 tmp: None,
121 slots: Vec::new(),
122 inputs: Vec::new(),
123 outputs: Vec::new(),
124 return_type: MethodReturnType::Unit(MethodReturnType::unit_type()),
125 }
126 }
127
128 pub(super) fn tmp_type<'a, I>(iter: I) -> Option<Type>
130 where
131 I: Iterator<Item = &'a Self> + 'a,
132 {
133 let mut tmp_type = None;
134 for slot in iter.flat_map(|method_details| &method_details.tmp) {
135 match &tmp_type {
136 Some(tmp_type) => {
137 if tmp_type != &slot.type_name {
138 return None;
139 }
140 }
141 None => {
142 tmp_type.replace(slot.type_name.clone());
143 }
144 }
145 }
146
147 Some(tmp_type.unwrap_or_else(MethodReturnType::unit_type))
148 }
149
150 pub(super) fn slot_type<'a, I>(iter: I) -> Option<Type>
152 where
153 I: Iterator<Item = &'a Self> + 'a,
154 {
155 let mut slot_type = None;
156 for slot in iter.flat_map(|method_details| &method_details.slots) {
157 match &slot_type {
158 Some(slot_type) => {
159 if slot_type != &slot.type_name {
160 return None;
161 }
162 }
163 None => {
164 slot_type.replace(slot.type_name.clone());
165 }
166 }
167 }
168
169 Some(slot_type.unwrap_or_else(MethodReturnType::unit_type))
170 }
171
172 pub(super) fn process_env_arg_ro(
173 &mut self,
174 input_span: Span,
175 pat_type: &PatType,
176 ) -> Result<(), Error> {
177 self.process_env_arg(input_span, pat_type, false)
178 }
179
180 pub(super) fn process_env_arg_rw(
181 &mut self,
182 input_span: Span,
183 pat_type: &PatType,
184 ) -> Result<(), Error> {
185 self.process_env_arg(input_span, pat_type, true)
186 }
187
188 fn process_env_arg(
189 &mut self,
190 input_span: Span,
191 pat_type: &PatType,
192 allow_mut: bool,
193 ) -> Result<(), Error> {
194 if self.env.is_some()
195 || self.tmp.is_some()
196 || !(self.inputs.is_empty() && self.outputs.is_empty())
197 {
198 return Err(Error::new(
199 input_span,
200 "`#[env]` must be the first non-Self argument and only appear once",
201 ));
202 }
203
204 if let Type::Reference(type_reference) = &*pat_type.ty
205 && let Type::Path(_type_path) = &*type_reference.elem
206 && let Pat::Ident(pat_ident) = &*pat_type.pat
207 {
208 if type_reference.mutability.is_some() && !allow_mut {
209 return Err(Error::new(
210 input_span,
211 "`#[env]` is not allowed to mutate data here",
212 ));
213 }
214
215 self.env.replace(Env {
216 arg_name: pat_ident.ident.clone(),
217 mutability: type_reference.mutability,
218 });
219 Ok(())
220 } else {
221 Err(Error::new(
222 pat_type.span(),
223 "`#[env]` must be a reference to `Env` type (can be shared or exclusive)",
224 ))
225 }
226 }
227
228 pub(super) fn process_state_arg_ro(
229 &mut self,
230 input_span: Span,
231 ty: &Type,
232 ) -> Result<(), Error> {
233 self.process_state_arg(input_span, ty, false)
234 }
235
236 pub(super) fn process_state_arg_rw(
237 &mut self,
238 input_span: Span,
239 ty: &Type,
240 ) -> Result<(), Error> {
241 self.process_state_arg(input_span, ty, true)
242 }
243
244 fn process_state_arg(
245 &mut self,
246 input_span: Span,
247 ty: &Type,
248 allow_mut: bool,
249 ) -> Result<(), Error> {
250 if let Type::Reference(type_reference) = ty
252 && let Type::Path(type_path) = &*type_reference.elem
253 && type_path.path.is_ident("Self")
254 {
255 if type_reference.mutability.is_some() && !allow_mut {
256 return Err(Error::new(
257 input_span,
258 "`#[arg]` is not allowed to mutate data here",
259 ));
260 }
261
262 self.state.replace(type_reference.mutability);
263 Ok(())
264 } else {
265 Err(Error::new(
266 ty.span(),
267 "Can't consume `Self`, use `&self` or `&mut self` instead",
268 ))
269 }
270 }
271
272 pub(super) fn process_tmp_arg(
273 &mut self,
274 input_span: Span,
275 pat_type: &PatType,
276 ) -> Result<(), Error> {
277 if self.tmp.is_some() || !(self.inputs.is_empty() && self.outputs.is_empty()) {
278 return Err(Error::new(
279 input_span,
280 "`#[tmp]` must appear only once before any `#[input]` or `#[output]`",
281 ));
282 }
283
284 if let Type::Reference(type_reference) = &*pat_type.ty {
286 let Some(arg_name) = extract_arg_name(&pat_type.pat) else {
287 return Err(Error::new(
288 pat_type.span(),
289 "`#[tmp]` argument name must be either a simple variable or a reference",
290 ));
291 };
292
293 self.tmp.replace(Tmp {
294 type_name: type_reference.elem.as_ref().clone(),
295 arg_name,
296 mutability: type_reference.mutability,
297 });
298
299 return Ok(());
300 }
301
302 Err(Error::new(
303 pat_type.span(),
304 "`#[tmp]` must be a reference to a type implementing `IoTypeOptional` (can be \
305 shared or exclusive) like `&MaybeData<Slot>` or `&mut VariableBytes<1024>`",
306 ))
307 }
308
309 pub(super) fn process_slot_arg_ro(
310 &mut self,
311 input_span: Span,
312 pat_type: &PatType,
313 ) -> Result<(), Error> {
314 self.process_slot_arg(input_span, pat_type, false)
315 }
316
317 pub(super) fn process_slot_arg_rw(
318 &mut self,
319 input_span: Span,
320 pat_type: &PatType,
321 ) -> Result<(), Error> {
322 self.process_slot_arg(input_span, pat_type, true)
323 }
324
325 fn process_slot_arg(
326 &mut self,
327 input_span: Span,
328 pat_type: &PatType,
329 allow_mut: bool,
330 ) -> Result<(), Error> {
331 if !(self.inputs.is_empty() && self.outputs.is_empty()) {
332 return Err(Error::new(
333 input_span,
334 "`#[slot]` must appear before any `#[input]` or `#[output]`",
335 ));
336 }
337
338 match &*pat_type.ty {
339 Type::Reference(type_reference) => {
341 if type_reference.mutability.is_some() && !allow_mut {
342 return Err(Error::new(
343 input_span,
344 "`#[slot]` is not allowed to mutate data here",
345 ));
346 }
347
348 let Some(arg_name) = extract_arg_name(&pat_type.pat) else {
349 return Err(Error::new(
350 pat_type.span(),
351 "`#[slot]` argument name must be either a simple variable or a reference",
352 ));
353 };
354
355 self.slots.push(Slot {
356 with_address_arg: false,
357 type_name: type_reference.elem.as_ref().clone(),
358 arg_name,
359 mutability: type_reference.mutability,
360 });
361 return Ok(());
362 }
363 Type::Tuple(type_tuple) => {
365 if type_tuple.elems.len() == 2
366 && let Type::Reference(address_type) =
367 type_tuple.elems.first().expect("Checked above; qed")
368 && address_type.mutability.is_none()
369 && let Type::Reference(outer_slot_type) =
370 type_tuple.elems.last().expect("Checked above; qed")
371 && let Pat::Tuple(pat_tuple) = &*pat_type.pat
372 && pat_tuple.elems.len() == 2
373 && let Some(slot_arg) = extract_arg_name(&pat_tuple.elems[1])
374 {
375 if outer_slot_type.mutability.is_some() && !allow_mut {
376 return Err(Error::new(
377 input_span,
378 "`#[slot]` is not allowed to mutate data here",
379 ));
380 }
381
382 self.slots.push(Slot {
383 with_address_arg: true,
384 type_name: outer_slot_type.elem.as_ref().clone(),
385 arg_name: slot_arg,
386 mutability: outer_slot_type.mutability,
387 });
388 return Ok(());
389 }
390
391 return Err(Error::new(
392 pat_type.span(),
393 "`#[slot]` with address must be a tuple of arguments, each of which is \
394 either a simple variable or a reference",
395 ));
396 }
397 _ => {
398 }
400 }
401
402 Err(Error::new(
403 pat_type.span(),
404 "`#[slot]` must be a reference to a type implementing `IoTypeOptional` (can be \
405 shared or exclusive) like `&MaybeData<Slot>` or a tuple of references to address and \
406 to slot type like `(&Address, &mut VariableBytes<1024>)`",
407 ))
408 }
409
410 pub(super) fn process_input_arg(
411 &mut self,
412 input_span: Span,
413 pat_type: &PatType,
414 ) -> Result<(), Error> {
415 if !self.outputs.is_empty() {
416 return Err(Error::new(
417 input_span,
418 "`#[input]` must appear before any `#[output]`",
419 ));
420 }
421
422 if let Type::Reference(type_reference) = &*pat_type.ty {
424 let Some(arg_name) = extract_arg_name(&pat_type.pat) else {
425 return Err(Error::new(
426 pat_type.span(),
427 "`#[input]` argument name must be either a simple variable or a reference",
428 ));
429 };
430 if type_reference.mutability.is_some() {
431 return Err(Error::new(
432 input_span,
433 "`#[input]` must be a shared reference",
434 ));
435 }
436
437 self.inputs.push(Input {
438 type_name: type_reference.elem.as_ref().clone(),
439 arg_name,
440 });
441
442 Ok(())
443 } else {
444 Err(Error::new(
445 pat_type.span(),
446 "`#[input]` must be a shared reference to a type",
447 ))
448 }
449 }
450
451 pub(super) fn process_output_arg(
452 &mut self,
453 _input_span: Span,
454 pat_type: &PatType,
455 ) -> Result<(), Error> {
456 if let Type::Reference(type_reference) = &*pat_type.ty
458 && type_reference.mutability.is_some()
459 {
460 let Pat::Ident(pat_ident) = &*pat_type.pat else {
461 return Err(Error::new(
462 pat_type.span(),
463 "`#[output]` argument name must be an exclusive reference",
464 ));
465 };
466
467 let mut type_name = type_reference.elem.as_ref().clone();
468 let mut has_self = false;
469
470 if let Type::Path(type_path) = &mut type_name
472 && let Some(path_segment) = type_path.path.segments.first_mut()
473 && let PathArguments::AngleBracketed(generic_arguments) =
474 &mut path_segment.arguments
475 && let Some(GenericArgument::Type(first_generic_argument)) =
476 generic_arguments.args.first_mut()
477 && let Type::Path(type_path) = &first_generic_argument
478 && type_path.path.is_ident("Self")
479 {
480 *first_generic_argument = self.self_type.clone();
481 has_self = true;
482 }
483
484 self.outputs.push(Output {
485 type_name,
486 arg_name: pat_ident.ident.clone(),
487 has_self,
488 });
489 Ok(())
490 } else {
491 Err(Error::new(
492 pat_type.span(),
493 "`#[output]` must be an exclusive reference to a type implementing \
494 `IoTypeOptional`, likely `MaybeData` container",
495 ))
496 }
497 }
498
499 pub(super) fn process_return(&mut self, output: &ReturnType) -> Result<(), Error> {
500 let error_message = format!(
502 "`#[{}]` must return `()` or `T` or `Result<T, ContractError>",
503 self.method_type.attr_str()
504 );
505 match output {
506 ReturnType::Default => {
507 self.set_return_type(MethodReturnType::Unit(MethodReturnType::unit_type()));
508 }
509 ReturnType::Type(_r_arrow, return_type) => match return_type.as_ref() {
510 Type::Array(_type_array) => {
511 self.set_return_type(MethodReturnType::Regular(return_type.as_ref().clone()));
512 }
513 Type::Path(type_path) => {
514 let Some(last_path_segment) = type_path.path.segments.last() else {
516 self.set_return_type(MethodReturnType::Regular(
517 return_type.as_ref().clone(),
518 ));
519 return Ok(());
520 };
521
522 if last_path_segment.ident == "Result" {
524 if let PathArguments::AngleBracketed(result_arguments) =
525 &last_path_segment.arguments
526 && result_arguments.args.len() == 2
527 && let GenericArgument::Type(ok_type) = &result_arguments.args[0]
528 && let GenericArgument::Type(error_type) = &result_arguments.args[1]
529 && let Type::Path(error_path) = error_type
530 && error_path
531 .path
532 .segments
533 .last()
534 .is_some_and(|s| s.ident == "ContractError")
535 {
536 if let Type::Path(ok_path) = ok_type
537 && ok_path
538 .path
539 .segments
540 .first()
541 .is_some_and(|s| s.ident == "Self")
542 {
543 self.set_return_type(MethodReturnType::Result(
545 self.self_type.clone(),
546 ));
547 } else {
548 self.set_return_type(MethodReturnType::Result(ok_type.clone()));
549 }
550 } else {
551 return Err(Error::new(return_type.span(), error_message));
552 }
553 } else if last_path_segment.ident == "Self" {
554 self.set_return_type(MethodReturnType::Regular(self.self_type.clone()));
556 } else {
557 self.set_return_type(MethodReturnType::Regular(
558 return_type.as_ref().clone(),
559 ));
560 }
561 }
562 return_type => {
563 return Err(Error::new(return_type.span(), error_message));
564 }
565 },
566 }
567
568 Ok(())
569 }
570
571 fn set_return_type(&mut self, return_type: MethodReturnType) {
572 let unit_type = MethodReturnType::unit_type();
573 self.return_type = match return_type {
574 MethodReturnType::Unit(ty) => MethodReturnType::Unit(ty),
575 MethodReturnType::Regular(ty) => {
576 if ty == unit_type {
577 MethodReturnType::Unit(ty)
578 } else {
579 MethodReturnType::Regular(ty)
580 }
581 }
582 MethodReturnType::ResultUnit(ty) => MethodReturnType::ResultUnit(ty),
583 MethodReturnType::Result(ty) => {
584 if ty == unit_type {
585 MethodReturnType::ResultUnit(ty)
586 } else {
587 MethodReturnType::Result(ty)
588 }
589 }
590 };
591 }
592
593 pub(super) fn generate_guest_ffi(
594 &self,
595 fn_sig: &Signature,
596 trait_name: Option<&Ident>,
597 ) -> Result<TokenStream, Error> {
598 let self_type = &self.self_type;
599 if matches!(self.method_type, MethodType::Init) {
600 let self_return_type = self.return_type.return_type() == self_type;
601 let self_last_output_type = self.outputs.last().is_some_and(|output| output.has_self);
602
603 if !(self_return_type || self_last_output_type) {
604 return Err(Error::new(
605 fn_sig.span(),
606 "`#[init]` must have `Self` as either return type or last `#[output]` \
607 argument",
608 ));
609 }
610 }
611
612 let original_method_name = &fn_sig.ident;
613
614 let guest_fn = self.generate_guest_fn(fn_sig, trait_name)?;
615 let external_args_struct = self.generate_external_args_struct(fn_sig, trait_name)?;
616 let metadata = self.generate_metadata(fn_sig, trait_name)?;
617
618 Ok(quote_spanned! {fn_sig.span() =>
619 pub mod #original_method_name {
620 use super::*;
621
622 #guest_fn
623 #external_args_struct
624 #metadata
625 }
626 })
627 }
628
629 pub(super) fn generate_guest_trait_ffi(
630 &self,
631 fn_sig: &Signature,
632 trait_name: Option<&Ident>,
633 ) -> Result<TokenStream, Error> {
634 let original_method_name = &fn_sig.ident;
635
636 let external_args_struct = self.generate_external_args_struct(fn_sig, trait_name)?;
637 let metadata = self.generate_metadata(fn_sig, trait_name)?;
638
639 Ok(quote_spanned! {fn_sig.span() =>
640 pub mod #original_method_name {
641 use super::*;
642
643 #external_args_struct
644 #metadata
645 }
646 })
647 }
648
649 pub(super) fn generate_guest_fn(
650 &self,
651 fn_sig: &Signature,
652 trait_name: Option<&Ident>,
653 ) -> Result<TokenStream, Error> {
654 let self_type = &self.self_type;
655
656 let mut internal_args_pointers = Vec::new();
658 let mut preparation = Vec::new();
660 let mut original_fn_args = Vec::new();
662
663 if let Some(mutability) = self.state {
665 internal_args_pointers.push(quote! {
666 pub self_ptr: ::core::ptr::NonNull<
667 <#self_type as ::ab_contracts_macros::__private::IoType>::PointerType,
668 >,
669 });
670
671 if mutability.is_some() {
672 internal_args_pointers.push(quote! {
673 pub self_size: *mut ::core::primitive::u32,
675 pub self_capacity: ::core::ptr::NonNull<::core::primitive::u32>,
677 });
678
679 original_fn_args.push(quote! {&mut *{
680 const _: () = {
683 const fn assert_impl_io_type<T>()
684 where
685 T: ::ab_contracts_macros::__private::IoType,
686 {}
687 assert_impl_io_type::<#self_type>();
688 };
689
690 <#self_type as ::ab_contracts_macros::__private::IoType>::from_mut_ptr(
691 &mut args.self_ptr,
692 &mut args.self_size,
693 args.self_capacity.read(),
694 )
695 }});
696 } else {
697 internal_args_pointers.push(quote! {
698 pub self_size: ::core::ptr::NonNull<::core::primitive::u32>,
700 });
701
702 original_fn_args.push(quote! {&*{
703 const _: () = {
706 const fn assert_impl_io_type<T>()
707 where
708 T: ::ab_contracts_macros::__private::IoType,
709 {}
710 assert_impl_io_type::<#self_type>();
711 };
712
713 <#self_type as ::ab_contracts_macros::__private::IoType>::from_ptr(
714 &args.self_ptr,
715 args.self_size.as_ref(),
716 args.self_size.read(),
718 )
719 }});
720 }
721 }
722
723 if let Some(env) = &self.env {
725 let ptr_field = format_ident!("{}_ptr", env.arg_name);
726 let assert_msg = format!("`{ptr_field}` pointer is misaligned");
727 let mutability = env.mutability;
728
729 internal_args_pointers.push(quote! {
730 pub #ptr_field: ::core::ptr::NonNull<::ab_contracts_macros::__private::Env<'internal_args>>,
732 });
733
734 if mutability.is_some() {
735 original_fn_args.push(quote! {{
736 debug_assert!(args.#ptr_field.is_aligned(), #assert_msg);
737 args.#ptr_field.as_mut()
738 }});
739 } else {
740 original_fn_args.push(quote! {{
741 debug_assert!(args.#ptr_field.is_aligned(), #assert_msg);
742 args.#ptr_field.as_ref()
743 }});
744 }
745 }
746
747 if let Some(tmp) = &self.tmp {
751 let type_name = &tmp.type_name;
752 let mutability = tmp.mutability;
753 let ptr_field = format_ident!("{}_ptr", tmp.arg_name);
754 let size_field = format_ident!("{}_size", tmp.arg_name);
755 let size_doc = format!("Size of the contents `{ptr_field}` points to");
756 let capacity_field = format_ident!("{}_capacity", tmp.arg_name);
757 let capacity_doc = format!("Capacity of the allocated memory `{ptr_field}` points to");
758
759 internal_args_pointers.push(quote! {
760 pub #ptr_field: ::core::ptr::NonNull<
761 <
762 <#self_type as ::ab_contracts_macros::__private::Contract>::Tmp as ::ab_contracts_macros::__private::IoType
764 >::PointerType,
765 >,
766 });
767
768 if mutability.is_some() {
769 internal_args_pointers.push(quote! {
770 #[doc = #size_doc]
771 pub #size_field: *mut ::core::primitive::u32,
772 #[doc = #capacity_doc]
773 pub #capacity_field: ::core::ptr::NonNull<::core::primitive::u32>,
774 });
775
776 original_fn_args.push(quote! {&mut *{
777 const _: () = {
781 const fn assert_impl_io_type_optional<T>()
782 where
783 T: ::ab_contracts_macros::__private::IoTypeOptional,
784 {}
785 assert_impl_io_type_optional::<#type_name>();
786 };
787
788 <#type_name as ::ab_contracts_macros::__private::IoType>::from_mut_ptr(
789 &mut args.#ptr_field,
790 &mut args.#size_field,
791 args.#capacity_field.read(),
792 )
793 }});
794 } else {
795 internal_args_pointers.push(quote! {
796 #[doc = #size_doc]
797 pub #size_field: ::core::ptr::NonNull<::core::primitive::u32>,
798 });
799
800 original_fn_args.push(quote! {&*{
801 const _: () = {
805 const fn assert_impl_io_type_optional<T>()
806 where
807 T: ::ab_contracts_macros::__private::IoTypeOptional,
808 {}
809 assert_impl_io_type_optional::<#type_name>();
810 };
811
812 <#type_name as ::ab_contracts_macros::__private::IoType>::from_ptr(
813 &args.#ptr_field,
814 args.#size_field.as_ref(),
815 args.#size_field.read(),
817 )
818 }});
819 }
820 }
821
822 for slot in &self.slots {
829 let type_name = &slot.type_name;
830 let mutability = slot.mutability;
831 let address_ptr_field = format_ident!("{}_address_ptr", slot.arg_name);
832 let ptr_field = format_ident!("{}_ptr", slot.arg_name);
833 let size_field = format_ident!("{}_size", slot.arg_name);
834 let size_doc = format!("Size of the contents `{ptr_field}` points to");
835 let capacity_field = format_ident!("{}_capacity", slot.arg_name);
836 let capacity_doc = format!("Capacity of the allocated memory `{ptr_field}` points to");
837
838 internal_args_pointers.push(quote! {
839 pub #address_ptr_field: ::core::ptr::NonNull<::ab_contracts_macros::__private::Address>,
841 pub #ptr_field: ::core::ptr::NonNull<
842 <
843 <#self_type as ::ab_contracts_macros::__private::Contract>::Slot as ::ab_contracts_macros::__private::IoType
845 >::PointerType,
846 >,
847 });
848
849 let arg_extraction = if mutability.is_some() {
850 internal_args_pointers.push(quote! {
851 #[doc = #size_doc]
852 pub #size_field: *mut ::core::primitive::u32,
853 #[doc = #capacity_doc]
854 pub #capacity_field: ::core::ptr::NonNull<::core::primitive::u32>,
855 });
856
857 quote! {&mut *{
858 const _: () = {
862 const fn assert_impl_io_type_optional<T>()
863 where
864 T: ::ab_contracts_macros::__private::IoTypeOptional,
865 {}
866 assert_impl_io_type_optional::<#type_name>();
867 };
868
869 <#type_name as ::ab_contracts_macros::__private::IoType>::from_mut_ptr(
870 &mut args.#ptr_field,
871 &mut args.#size_field,
872 args.#capacity_field.read(),
873 )
874 }}
875 } else {
876 internal_args_pointers.push(quote! {
877 #[doc = #size_doc]
878 pub #size_field: ::core::ptr::NonNull<::core::primitive::u32>,
879 });
880
881 quote! {&*{
882 const _: () = {
886 const fn assert_impl_io_type_optional<T>()
887 where
888 T: ::ab_contracts_macros::__private::IoTypeOptional,
889 {}
890 assert_impl_io_type_optional::<#type_name>();
891 };
892
893 <#type_name as ::ab_contracts_macros::__private::IoType>::from_ptr(
894 &args.#ptr_field,
895 args.#size_field.as_ref(),
896 args.#size_field.read(),
898 )
899 }}
900 };
901
902 if slot.with_address_arg {
903 original_fn_args.push(quote! {
904 (
905 &<::ab_contracts_macros::__private::Address as ::ab_contracts_macros::__private::IoType>::from_ptr(
906 &args.#address_ptr_field,
907 &<::ab_contracts_macros::__private::Address as ::ab_contracts_macros::__private::TrivialType>::SIZE,
908 <::ab_contracts_macros::__private::Address as ::ab_contracts_macros::__private::TrivialType>::SIZE,
909 ),
910 #arg_extraction,
911 )
912 });
913 } else {
914 original_fn_args.push(arg_extraction);
915 }
916 }
917
918 for input in &self.inputs {
921 let type_name = &input.type_name;
922 let arg_name = &input.arg_name;
923 let ptr_field = format_ident!("{arg_name}_ptr");
924 let size_field = format_ident!("{arg_name}_size");
925 let size_doc = format!("Size of the contents `{ptr_field}` points to");
926
927 internal_args_pointers.push(quote! {
928 pub #ptr_field: ::core::ptr::NonNull<
929 <#type_name as ::ab_contracts_macros::__private::IoType>::PointerType,
930 >,
931 #[doc = #size_doc]
932 pub #size_field: ::core::ptr::NonNull<::core::primitive::u32>,
933 });
934
935 original_fn_args.push(quote! {&*{
936 const _: () = {
939 const fn assert_impl_io_type<T>()
940 where
941 T: ::ab_contracts_macros::__private::IoType,
942 {}
943 assert_impl_io_type::<#type_name>();
944 };
945
946 <#type_name as ::ab_contracts_macros::__private::IoType>::from_ptr(
947 &args.#ptr_field,
948 args.#size_field.as_ref(),
949 args.#size_field.read(),
951 )
952 }});
953 }
954
955 for output in &self.outputs {
958 let type_name = &output.type_name;
959 let arg_name = &output.arg_name;
960 let ptr_field = format_ident!("{arg_name}_ptr");
961 let size_field = format_ident!("{arg_name}_size");
962 let size_doc = format!("Size of the contents `{ptr_field}` points to");
963 let capacity_field = format_ident!("{arg_name}_capacity");
964 let capacity_doc = format!("Capacity of the allocated memory `{ptr_field}` points to");
965
966 internal_args_pointers.push(quote! {
967 pub #ptr_field: ::core::ptr::NonNull<
968 <#type_name as ::ab_contracts_macros::__private::IoType>::PointerType,
969 >,
970 #[doc = #size_doc]
971 pub #size_field: *mut ::core::primitive::u32,
972 #[doc = #capacity_doc]
973 pub #capacity_field: ::core::ptr::NonNull<::core::primitive::u32>,
974 });
975
976 original_fn_args.push(quote! {&mut *{
977 const _: () = {
981 const fn assert_impl_io_type_optional<T>()
982 where
983 T: ::ab_contracts_macros::__private::IoTypeOptional,
984 {}
985 assert_impl_io_type_optional::<#type_name>();
986 };
987
988 <#type_name as ::ab_contracts_macros::__private::IoType>::from_mut_ptr(
989 &mut args.#ptr_field,
990 &mut args.#size_field,
991 args.#capacity_field.read(),
992 )
993 }});
994 }
995
996 let original_method_name = &fn_sig.ident;
997 let ffi_fn_name = derive_ffi_fn_name(self_type, trait_name, original_method_name)?;
998 let return_type = self.return_type.return_type();
999
1000 let internal_args_struct = {
1001 if !self.return_type.unit_return_type() {
1004 internal_args_pointers.push(quote! {
1005 pub ok_result_ptr: ::core::ptr::NonNull<#return_type>,
1006 pub ok_result_size: *mut ::core::primitive::u32,
1008 pub ok_result_capacity: ::core::ptr::NonNull<::core::primitive::u32>,
1010 });
1011
1012 preparation.push(quote! {
1016 debug_assert!(
1017 args.ok_result_ptr.is_aligned(),
1018 "`ok_result_ptr` pointer is misaligned"
1019 );
1020 if !args.ok_result_size.is_null() {
1021 debug_assert_eq!(
1022 args.ok_result_size.read(),
1023 0,
1024 "`ok_result_size` must be zero initially",
1025 );
1026 }
1027 debug_assert!(
1028 args.ok_result_capacity.read() >=
1029 <#return_type as ::ab_contracts_macros::__private::TrivialType>::SIZE,
1030 "`ok_result_capacity` specified is invalid",
1031 );
1032 });
1033 }
1034 let args_struct_doc = format!(
1035 "Data structure containing expected input to [`{ffi_fn_name}()`], it is used \
1036 internally by the contract, there should be no need to construct it explicitly \
1037 except maybe in contract's own tests"
1038 );
1039 quote_spanned! {fn_sig.span() =>
1040 #[doc = #args_struct_doc]
1041 #[derive(::core::fmt::Debug)]
1042 #[repr(C)]
1043 pub struct InternalArgs<'internal_args>
1044 {
1045 #( #internal_args_pointers )*
1046 _phantom: ::core::marker::PhantomData<&'internal_args ()>,
1047 }
1048 }
1049 };
1050
1051 let result_var_name = format_ident!("result");
1052 let guest_fn = {
1053 let result_handling = match &self.return_type {
1056 MethodReturnType::Unit(_) => {
1057 quote! {
1058 ::ab_contracts_macros::__private::ExitCode::ok()
1060 }
1061 }
1062 MethodReturnType::Regular(_) => {
1063 quote! {
1064 if !args.ok_result_size.is_null() {
1066 args.ok_result_size.write(
1067 <#return_type as ::ab_contracts_macros::__private::TrivialType>::SIZE,
1068 );
1069 }
1070 args.ok_result_ptr.write(#result_var_name);
1071 ::ab_contracts_macros::__private::ExitCode::ok()
1073 }
1074 }
1075 MethodReturnType::ResultUnit(_) => {
1076 quote! {
1077 match #result_var_name {
1079 Ok(()) => ::ab_contracts_macros::__private::ExitCode::ok(),
1080 Err(error) => error.exit_code(),
1081 }
1082 }
1083 }
1084 MethodReturnType::Result(_) => {
1085 quote! {
1086 match #result_var_name {
1088 Ok(result) => {
1089 if !args.ok_result_size.is_null() {
1091 args.ok_result_size.write(
1092 <#return_type as ::ab_contracts_macros::__private::TrivialType>::SIZE,
1093 );
1094 }
1095 args.ok_result_ptr.write(result);
1096 ::ab_contracts_macros::__private::ExitCode::ok()
1098 }
1099 Err(error) => error.exit_code(),
1100 }
1101 }
1102 }
1103 };
1104
1105 let full_struct_name = if let Some(trait_name) = trait_name {
1106 quote! { <#self_type as #trait_name> }
1107 } else {
1108 quote! { #self_type }
1109 };
1110
1111 quote_spanned! {fn_sig.span() =>
1114 #[cfg_attr(feature = "guest", unsafe(no_mangle))]
1123 #[allow(clippy::new_ret_no_self, reason = "Method was re-written for FFI purposes without `Self`")]
1124 #[allow(clippy::absurd_extreme_comparisons, reason = "Macro-generated code doesn't know the size upfront")]
1125 pub unsafe extern "C" fn #ffi_fn_name(
1126 mut args: ::core::ptr::NonNull<InternalArgs<'_>>,
1127 ) -> ::ab_contracts_macros::__private::ExitCode {
1128 unsafe {
1130 debug_assert!(args.is_aligned(), "`args` pointer is misaligned");
1131 let args = args.as_mut();
1132
1133 #( #preparation )*
1134
1135 #[allow(
1137 unused_variables,
1138 reason = "Sometimes result is `()`"
1139 )]
1140 #[allow(
1141 clippy::let_unit_value,
1142 reason = "Sometimes result is `()`"
1143 )]
1144 let #result_var_name = #full_struct_name::#original_method_name(
1145 #( #original_fn_args, )*
1146 );
1147
1148 #result_handling
1149 }
1150 }
1151 }
1152 };
1153
1154 let fn_pointer_static = {
1155 let adapter_ffi_fn_name = format_ident!("{ffi_fn_name}_adapter");
1156 let args_struct_name =
1157 derive_external_args_struct_name(self_type, trait_name, original_method_name)?;
1158
1159 quote! {
1160 #[doc(hidden)]
1161 pub mod fn_pointer {
1162 use super::*;
1163
1164 unsafe extern "C" fn #adapter_ffi_fn_name(
1165 ptr: ::core::ptr::NonNull<::core::ptr::NonNull<::core::ffi::c_void>>,
1166 ) -> ::ab_contracts_macros::__private::ExitCode {
1167 unsafe { #ffi_fn_name(ptr.cast::<InternalArgs<'_>>()) }
1170 }
1171
1172 pub const METHOD_FN_POINTER: ::ab_contracts_macros::__private::NativeExecutorContactMethod =
1173 ::ab_contracts_macros::__private::NativeExecutorContactMethod {
1174 method_fingerprint: &<#args_struct_name as ::ab_contracts_macros::__private::ExternalArgs>::FINGERPRINT,
1175 method_metadata: METADATA,
1176 ffi_fn: #adapter_ffi_fn_name,
1177 };
1178 }
1179 }
1180 };
1181
1182 Ok(quote! {
1183 #internal_args_struct
1184 #guest_fn
1185 #fn_pointer_static
1186 })
1187 }
1188
1189 fn generate_external_args_struct(
1190 &self,
1191 fn_sig: &Signature,
1192 trait_name: Option<&Ident>,
1193 ) -> Result<TokenStream, Error> {
1194 let self_type = &self.self_type;
1195 let original_method_name = &fn_sig.ident;
1196
1197 let args_struct_name =
1198 derive_external_args_struct_name(self_type, trait_name, original_method_name)?;
1199 let mut external_args_fields = Vec::new();
1201 let mut method_args = Vec::new();
1203 let mut method_args_fields = Vec::new();
1205
1206 for slot in &self.slots {
1208 let arg_name = &slot.arg_name;
1209 let ptr_field = format_ident!("{arg_name}_ptr");
1210
1211 external_args_fields.push(quote! {
1212 pub #ptr_field: ::core::ptr::NonNull<::ab_contracts_macros::__private::Address>,
1213 });
1214
1215 method_args.push(quote! {
1216 #arg_name: &::ab_contracts_macros::__private::Address,
1217 });
1218 method_args_fields.push(quote! {
1219 #ptr_field: ::core::ptr::NonNull::from(#arg_name),
1221 });
1222 }
1223
1224 for input in &self.inputs {
1226 let type_name = &input.type_name;
1227 let arg_name = &input.arg_name;
1228 let ptr_field = format_ident!("{arg_name}_ptr");
1229 let size_field = format_ident!("{arg_name}_size");
1230 let size_doc = format!("Size of the contents `{ptr_field}` points to");
1231
1232 external_args_fields.push(quote! {
1233 pub #ptr_field: ::core::ptr::NonNull<
1234 <#type_name as ::ab_contracts_macros::__private::IoType>::PointerType,
1235 >,
1236 #[doc = #size_doc]
1237 pub #size_field: ::core::ptr::NonNull<::core::primitive::u32>,
1238 });
1239
1240 method_args.push(quote! {
1241 #arg_name: &#type_name,
1242 });
1243 method_args_fields.push(quote! {
1244 #ptr_field: unsafe {
1248 *::ab_contracts_macros::__private::IoType::as_ptr(#arg_name)
1249 },
1250 #size_field: unsafe {
1254 *::ab_contracts_macros::__private::IoType::size_ptr(#arg_name)
1255 },
1256 });
1257 }
1258
1259 let mut outputs_iter = self.outputs.iter().peekable();
1261 while let Some(output) = outputs_iter.next() {
1262 let type_name = &output.type_name;
1263 let arg_name = &output.arg_name;
1264 let ptr_field = format_ident!("{arg_name}_ptr");
1265 let size_field = format_ident!("{arg_name}_size");
1266 let size_doc = format!("Size of the contents `{ptr_field}` points to");
1267 let capacity_field = format_ident!("{arg_name}_capacity");
1268 let capacity_doc = format!("Capacity of the allocated memory `{ptr_field}` points to");
1269
1270 if outputs_iter.is_empty()
1273 && self.return_type.unit_return_type()
1274 && matches!(self.method_type, MethodType::Init)
1275 {
1276 continue;
1277 }
1278
1279 external_args_fields.push(quote! {
1280 pub #ptr_field: ::core::ptr::NonNull<
1281 <#type_name as ::ab_contracts_macros::__private::IoType>::PointerType,
1282 >,
1283 #[doc = #size_doc]
1284 pub #size_field: *mut ::core::primitive::u32,
1285 #[doc = #capacity_doc]
1286 pub #capacity_field: ::core::ptr::NonNull<::core::primitive::u32>,
1287 });
1288
1289 method_args.push(quote! {
1290 #arg_name: &mut #type_name,
1291 });
1292 method_args_fields.push(quote! {
1293 #ptr_field: unsafe {
1297 *::ab_contracts_macros::__private::IoType::as_mut_ptr(#arg_name)
1298 },
1299 #size_field: unsafe {
1303 *::ab_contracts_macros::__private::IoType::size_mut_ptr(#arg_name)
1304 },
1305 #capacity_field: unsafe {
1309 *::ab_contracts_macros::__private::IoType::capacity_ptr(#arg_name)
1310 },
1311 });
1312 }
1313
1314 let ffi_fn_name = derive_ffi_fn_name(self_type, trait_name, original_method_name)?;
1315
1316 if !(matches!(self.method_type, MethodType::Init) || self.return_type.unit_return_type()) {
1320 let return_type = &self.return_type.return_type();
1321
1322 external_args_fields.push(quote! {
1323 pub ok_result_ptr: ::core::ptr::NonNull<#return_type>,
1324 pub ok_result_size: *mut ::core::primitive::u32,
1326 pub ok_result_capacity: ::core::ptr::NonNull<::core::primitive::u32>,
1328 });
1329
1330 method_args.push(quote! {
1331 ok_result: &mut ::core::mem::MaybeUninit<#return_type>,
1332 ok_result_size: &mut ::core::primitive::u32,
1333 });
1334 method_args_fields.push(quote! {
1335 ok_result_ptr: unsafe {
1337 ::core::ptr::NonNull::new_unchecked(ok_result.as_mut_ptr())
1338 },
1339 ok_result_size: ::core::ptr::from_mut(ok_result_size),
1340 ok_result_capacity: ::core::ptr::NonNull::from(
1343 &<#return_type as ::ab_contracts_macros::__private::TrivialType>::SIZE,
1344 ),
1345 });
1346 }
1347 let args_struct_doc = format!(
1348 "Data structure containing expected input for external method invocation, eventually \
1349 calling `{ffi_fn_name}()` on the other side by the host.\n\n\
1350 This can be used with [`Env`](::ab_contracts_macros::__private::Env), though there are \
1351 helper methods on this provided by extension trait that allow not dealing with this \
1352 struct directly in simpler cases."
1353 );
1354
1355 Ok(quote_spanned! {fn_sig.span() =>
1356 #[doc = #args_struct_doc]
1357 #[derive(::core::fmt::Debug)]
1358 #[repr(C)]
1359 pub struct #args_struct_name {
1360 #( #external_args_fields )*
1361 }
1362
1363 #[automatically_derived]
1364 unsafe impl ::ab_contracts_macros::__private::ExternalArgs for #args_struct_name {
1365 const FINGERPRINT: ::ab_contracts_macros::__private::MethodFingerprint =
1366 ::ab_contracts_macros::__private::MethodFingerprint::new(METADATA)
1367 .expect("Metadata is statically correct; qed");
1368 const METADATA: &[::core::primitive::u8] = METADATA;
1369 }
1370
1371 impl #args_struct_name {
1372 #[allow(
1374 clippy::new_without_default,
1375 reason = "Do not want `Default` in auto-generated code"
1376 )]
1377 pub fn new(
1378 #( #method_args )*
1379 ) -> Self {
1380 Self {
1381 #( #method_args_fields )*
1382 }
1383 }
1384 }
1385 })
1386 }
1387
1388 fn generate_metadata(
1389 &self,
1390 fn_sig: &Signature,
1391 trait_name: Option<&Ident>,
1392 ) -> Result<TokenStream, Error> {
1393 let self_type = &self.self_type;
1394 let mut method_metadata = Vec::new();
1397
1398 if let Some(env) = &self.env {
1399 let env_metadata_type = if env.mutability.is_some() {
1400 "EnvRw"
1401 } else {
1402 "EnvRo"
1403 };
1404
1405 let env_metadata_type = format_ident!("{env_metadata_type}");
1406 method_metadata.push(quote! {
1407 &[::ab_contracts_macros::__private::ContractMetadataKind::#env_metadata_type as ::core::primitive::u8],
1408 });
1409 }
1410
1411 if let Some(tmp) = &self.tmp {
1412 let tmp_metadata_type = if tmp.mutability.is_some() {
1413 "TmpRw"
1414 } else {
1415 "TmpRo"
1416 };
1417
1418 let tmp_metadata_type = format_ident!("{tmp_metadata_type}");
1419 let arg_name_metadata = derive_ident_metadata(&tmp.arg_name)?;
1420 method_metadata.push(quote! {
1421 &[::ab_contracts_macros::__private::ContractMetadataKind::#tmp_metadata_type as ::core::primitive::u8],
1422 #arg_name_metadata,
1423 });
1424 }
1425
1426 for slot in &self.slots {
1427 let slot_metadata_type = if slot.mutability.is_some() {
1428 "SlotRw"
1429 } else {
1430 "SlotRo"
1431 };
1432
1433 let slot_metadata_type = format_ident!("{slot_metadata_type}");
1434 let arg_name_metadata = derive_ident_metadata(&slot.arg_name)?;
1435 method_metadata.push(quote! {
1436 &[::ab_contracts_macros::__private::ContractMetadataKind::#slot_metadata_type as ::core::primitive::u8],
1437 #arg_name_metadata,
1438 });
1439 }
1440
1441 for input in &self.inputs {
1442 let io_metadata_type = format_ident!("Input");
1443 let arg_name_metadata = derive_ident_metadata(&input.arg_name)?;
1444 let type_name = &input.type_name;
1445
1446 method_metadata.push(quote! {
1447 &[::ab_contracts_macros::__private::ContractMetadataKind::#io_metadata_type as ::core::primitive::u8],
1448 #arg_name_metadata,
1449 <#type_name as ::ab_contracts_macros::__private::IoType>::METADATA,
1450 });
1451 }
1452
1453 let mut outputs_iter = self.outputs.iter().peekable();
1454 while let Some(output) = outputs_iter.next() {
1455 let io_metadata_type = "Output";
1456
1457 let io_metadata_type = format_ident!("{io_metadata_type}");
1458 let arg_name_metadata = derive_ident_metadata(&output.arg_name)?;
1459 let with_type_metadata = if outputs_iter.is_empty()
1461 && self.return_type.unit_return_type()
1462 && matches!(self.method_type, MethodType::Init)
1463 {
1464 None
1465 } else {
1466 let type_name = &output.type_name;
1467 Some(quote! {
1468 <#type_name as ::ab_contracts_macros::__private::IoType>::METADATA,
1469 })
1470 };
1471 method_metadata.push(quote! {
1472 &[::ab_contracts_macros::__private::ContractMetadataKind::#io_metadata_type as ::core::primitive::u8],
1473 #arg_name_metadata,
1474 #with_type_metadata
1475 });
1476 }
1477
1478 if !self.return_type.unit_return_type() {
1480 let arg_name_metadata = Literal::u8_unsuffixed(0);
1482 let with_type_metadata = if matches!(self.method_type, MethodType::Init) {
1484 None
1485 } else {
1486 let return_type = self.return_type.return_type();
1487 Some(quote! {
1488 <#return_type as ::ab_contracts_macros::__private::IoType>::METADATA,
1489 })
1490 };
1491 method_metadata.push(quote! {
1492 &[
1493 ::ab_contracts_macros::__private::ContractMetadataKind::Output as ::core::primitive::u8,
1494 #arg_name_metadata,
1495 ],
1496 #with_type_metadata
1497 });
1498 }
1499
1500 let method_type = match self.method_type {
1501 MethodType::Init => "Init",
1502 MethodType::Update => {
1503 if let Some(mutable) = &self.state {
1504 if mutable.is_some() {
1505 "UpdateStatefulRw"
1506 } else {
1507 "UpdateStatefulRo"
1508 }
1509 } else {
1510 "UpdateStateless"
1511 }
1512 }
1513 MethodType::View => {
1514 if let Some(mutable) = &self.state {
1515 if mutable.is_some() {
1516 return Err(Error::new(
1517 fn_sig.span(),
1518 "Stateful view methods are not supported",
1519 ));
1520 }
1521
1522 "ViewStateful"
1523 } else {
1524 "ViewStateless"
1525 }
1526 }
1527 };
1528
1529 let method_type = format_ident!("{method_type}");
1530 let number_of_arguments = u8::try_from(method_metadata.len()).map_err(|_error| {
1531 Error::new(
1532 fn_sig.span(),
1533 format!("Number of arguments must not be more than {}", u8::MAX),
1534 )
1535 })?;
1536 let total_number_of_arguments =
1537 Literal::u8_unsuffixed(number_of_arguments.saturating_add(1));
1538 let number_of_arguments = Literal::u8_unsuffixed(number_of_arguments);
1539
1540 let original_method_name = &fn_sig.ident;
1541 let ffi_fn_name = derive_ffi_fn_name(self_type, trait_name, original_method_name)?;
1542 let method_name_metadata = derive_ident_metadata(&ffi_fn_name)?;
1543 Ok(quote_spanned! {fn_sig.span() =>
1544 #[expect(
1545 clippy::assertions_on_constants,
1546 reason = "Auto-generated compile-time check"
1547 )]
1548 const fn metadata()
1549 -> ([::core::primitive::u8; ::ab_contracts_macros::__private::MAX_METADATA_CAPACITY], usize)
1550 {
1551 assert!(
1552 #total_number_of_arguments <= ::ab_contracts_macros::__private::MAX_TOTAL_METHOD_ARGS,
1553 "Too many arguments"
1554 );
1555 ::ab_contracts_macros::__private::concat_metadata_sources(&[
1556 &[::ab_contracts_macros::__private::ContractMetadataKind::#method_type as ::core::primitive::u8],
1557 #method_name_metadata,
1558 &[#number_of_arguments],
1559 #( #method_metadata )*
1560 ])
1561 }
1562
1563 pub const METADATA: &[::core::primitive::u8] =
1568 metadata()
1569 .0
1570 .split_at(metadata().1)
1571 .0;
1572 })
1573 }
1574
1575 pub(super) fn generate_trait_ext_components(
1576 &self,
1577 fn_sig: &Signature,
1578 fn_attrs: &[Attribute],
1579 trait_name: Option<&Ident>,
1580 ) -> Result<ExtTraitComponents, Error> {
1581 let self_type = &self.self_type;
1582
1583 let mut preparation = Vec::new();
1584 let mut method_args = Vec::new();
1585 let mut external_args_args = Vec::new();
1586 let mut result_processing = Vec::new();
1587
1588 method_args.push(quote_spanned! {fn_sig.span() =>
1590 contract: ::ab_contracts_macros::__private::Address,
1591 });
1592
1593 for slot in &self.slots {
1595 let arg_name = &slot.arg_name;
1596
1597 method_args.push(quote_spanned! {fn_sig.span() =>
1598 #arg_name: &::ab_contracts_macros::__private::Address,
1599 });
1600 external_args_args.push(quote_spanned! {fn_sig.span() => #arg_name });
1601 }
1602
1603 for input in &self.inputs {
1605 let type_name = &input.type_name;
1606 let arg_name = &input.arg_name;
1607
1608 method_args.push(quote_spanned! {fn_sig.span() =>
1609 #arg_name: &#type_name,
1610 });
1611 external_args_args.push(quote_spanned! {fn_sig.span() => #arg_name });
1612 }
1613
1614 let mut outputs_iter = self.outputs.iter().peekable();
1616 while let Some(output) = outputs_iter.next() {
1617 let type_name = &output.type_name;
1618 let arg_name = &output.arg_name;
1619
1620 if outputs_iter.is_empty()
1623 && self.return_type.unit_return_type()
1624 && matches!(self.method_type, MethodType::Init)
1625 {
1626 continue;
1627 }
1628
1629 method_args.push(quote_spanned! {fn_sig.span() =>
1630 #arg_name: &mut #type_name,
1631 });
1632 external_args_args.push(quote_spanned! {fn_sig.span() => #arg_name });
1633 }
1634
1635 let original_method_name = &fn_sig.ident;
1636 let ext_method_name = derive_ffi_fn_name(self_type, trait_name, original_method_name)?;
1637 let env_self = if matches!(self.method_type, MethodType::View) {
1639 quote_spanned! {fn_sig.span() => &self }
1640 } else {
1641 quote_spanned! {fn_sig.span() => &mut self }
1642 };
1643 let method_context_arg = (!matches!(self.method_type, MethodType::View)).then(|| {
1645 quote_spanned! {fn_sig.span() =>
1646 method_context: ::ab_contracts_macros::__private::MethodContext,
1647 }
1648 });
1649 let method_signature = if matches!(self.method_type, MethodType::Init)
1653 || self.return_type.unit_return_type()
1654 {
1655 quote_spanned! {fn_sig.span() =>
1656 #[allow(dead_code, reason = "Macro-generated")]
1657 fn #ext_method_name(
1658 #env_self,
1659 #method_context_arg
1660 #( #method_args )*
1661 ) -> ::core::result::Result<(), ::ab_contracts_macros::__private::ContractError>
1662 }
1663 } else {
1664 let return_type = self.return_type.return_type();
1665
1666 preparation.push(quote_spanned! {fn_sig.span() =>
1667 let mut ok_result = ::core::mem::MaybeUninit::uninit();
1668 let mut ok_result_size =
1671 <#return_type as ::ab_contracts_macros::__private::TrivialType>::SIZE;
1672 });
1673 external_args_args.push(quote_spanned! {fn_sig.span() =>
1674 &mut ok_result,
1675 &mut ok_result_size
1676 });
1677 result_processing.push(quote_spanned! {fn_sig.span() =>
1678 ok_result.assume_init()
1680 });
1681
1682 quote_spanned! {fn_sig.span() =>
1683 #[allow(dead_code, reason = "Macro-generated")]
1684 fn #ext_method_name(
1685 #env_self,
1686 #method_context_arg
1687 #( #method_args )*
1688 ) -> ::core::result::Result<
1689 #return_type,
1690 ::ab_contracts_macros::__private::ContractError,
1691 >
1692 }
1693 };
1694
1695 let attrs = fn_attrs.iter().filter(|attr| {
1696 let path = match &attr.meta {
1697 Meta::Path(path) => path,
1698 Meta::List(list) => &list.path,
1699 Meta::NameValue(name_value) => &name_value.path,
1700 };
1701
1702 if let Some(ident) = path.get_ident() {
1703 ident == "doc" || ident == "allow" || ident == "expect"
1704 } else {
1705 false
1706 }
1707 });
1708 let definition = quote_spanned! {fn_sig.span() =>
1709 #[allow(
1710 clippy::too_many_arguments,
1711 reason = "Generated code may have more arguments that source code"
1712 )]
1713 #( #attrs )*
1714 #method_signature;
1715 };
1716
1717 let args_struct_name =
1718 derive_external_args_struct_name(self_type, trait_name, original_method_name)?;
1719 let method_context_value = if matches!(self.method_type, MethodType::View) {
1721 quote_spanned! {fn_sig.span() =>
1722 ::ab_contracts_macros::__private::MethodContext::Reset
1723 }
1724 } else {
1725 quote_spanned! {fn_sig.span() =>
1726 method_context
1727 }
1728 };
1729 let r#impl = quote_spanned! {fn_sig.span() =>
1730 #[inline]
1731 #method_signature {
1732 #( #preparation )*
1733
1734 let mut args = #original_method_name::#args_struct_name::new(
1735 #( #external_args_args, )*
1736 );
1737
1738 self.call(contract, &mut args, #method_context_value)?;
1739
1740 #[allow(
1742 unused_unsafe,
1743 reason = "Sometimes there is no result to process and block is empty"
1744 )]
1745 #[allow(
1746 clippy::let_unit_value,
1747 reason = "Sometimes there is no result to process and block is empty"
1748 )]
1749 let result = unsafe {
1750 #( #result_processing )*
1751 };
1752
1753 Ok(result)
1754 }
1755 };
1756
1757 Ok(ExtTraitComponents { definition, r#impl })
1758 }
1759}
1760
1761fn extract_arg_name(mut pat: &Pat) -> Option<Ident> {
1762 loop {
1763 match pat {
1764 Pat::Ident(pat_ident) => {
1765 return Some(pat_ident.ident.clone());
1766 }
1767 Pat::Reference(pat_reference) => {
1768 pat = &pat_reference.pat;
1769 }
1770 _ => {
1771 return None;
1772 }
1773 }
1774 }
1775}
1776
1777fn derive_ffi_fn_name(
1778 type_name: &Type,
1779 trait_name: Option<&Ident>,
1780 method_name: &Ident,
1781) -> Result<Ident, Error> {
1782 let type_name = extract_ident_from_type(type_name).ok_or_else(|| {
1783 Error::new(
1784 type_name.span(),
1785 "`#[contract]` must be applied to a simple struct without generics",
1786 )
1787 })?;
1788 let ffi_fn_prefix =
1789 RenameRule::SnakeCase.apply_to_variant(trait_name.unwrap_or(type_name).to_string());
1790
1791 Ok(format_ident!("{ffi_fn_prefix}_{method_name}"))
1792}
1793
1794fn derive_external_args_struct_name(
1795 type_name: &Type,
1796 trait_name: Option<&Ident>,
1797 method_name: &Ident,
1798) -> Result<Ident, Error> {
1799 let type_name = extract_ident_from_type(type_name).ok_or_else(|| {
1800 Error::new(
1801 type_name.span(),
1802 "`#[contract]` must be applied to a simple struct without generics",
1803 )
1804 })?;
1805 Ok(format_ident!(
1806 "{}{}Args",
1807 trait_name.unwrap_or(type_name),
1808 RenameRule::PascalCase.apply_to_field(method_name.to_string())
1809 ))
1810}