sway_core/abi_generation/
abi_str.rs

1use crate::{
2    ast_elements::type_argument::GenericTypeArgument, language::CallPath, transform, Engines,
3    TypeId, TypeInfo,
4};
5use sway_error::handler::{ErrorEmitted, Handler};
6use sway_types::{integer_bits::IntegerBits, Ident, Named};
7
8#[derive(Clone)]
9pub struct AbiStrContext {
10    pub program_name: String,
11    pub abi_with_callpaths: bool,
12    pub abi_with_fully_specified_types: bool,
13    pub abi_root_type_without_generic_type_parameters: bool,
14}
15
16impl TypeId {
17    /// Gives back a string that represents the type, considering what it resolves to
18    pub fn get_abi_type_str(
19        &self,
20        handler: &Handler,
21        ctx: &AbiStrContext,
22        engines: &Engines,
23        resolved_type_id: TypeId,
24    ) -> Result<String, ErrorEmitted> {
25        let type_engine = engines.te();
26        let self_abi_str = type_engine
27            .get(*self)
28            .abi_str(handler, ctx, engines, true)?;
29        if self.is_generic_parameter(engines, resolved_type_id) {
30            Ok(format!("generic {self_abi_str}"))
31        } else {
32            match (
33                &*type_engine.get(*self),
34                &*type_engine.get(resolved_type_id),
35            ) {
36                (TypeInfo::Custom { .. }, TypeInfo::Struct { .. })
37                | (TypeInfo::Custom { .. }, TypeInfo::Enum { .. }) => type_engine
38                    .get(resolved_type_id)
39                    .abi_str(handler, ctx, engines, true),
40                (_, TypeInfo::Alias { ty, .. }) => ty
41                    .type_id
42                    .get_abi_type_str(handler, ctx, engines, ty.type_id),
43                (TypeInfo::Tuple(fields), TypeInfo::Tuple(resolved_fields)) => {
44                    assert_eq!(fields.len(), resolved_fields.len());
45                    let field_strs = resolved_fields
46                        .iter()
47                        .map(|f| {
48                            if ctx.abi_with_fully_specified_types {
49                                type_engine
50                                    .get(f.type_id)
51                                    .abi_str(handler, ctx, engines, false)
52                            } else {
53                                Ok("_".to_string())
54                            }
55                        })
56                        .collect::<Result<Vec<String>, _>>()?;
57                    Ok(format!("({})", field_strs.join(", ")))
58                }
59                (TypeInfo::Array(_, length), TypeInfo::Array(type_arg, resolved_length)) => {
60                    assert_eq!(
61                        length.expr().as_literal_val(),
62                        resolved_length.expr().as_literal_val(),
63                        "{:?} {:?}",
64                        length.expr().as_literal_val(),
65                        resolved_length.expr().as_literal_val()
66                    );
67                    let inner_type = if ctx.abi_with_fully_specified_types {
68                        type_engine
69                            .get(type_arg.type_id)
70                            .abi_str(handler, ctx, engines, false)?
71                    } else {
72                        "_".to_string()
73                    };
74                    Ok(format!(
75                        "[{}; {:?}]",
76                        inner_type,
77                        engines.help_out(length.expr())
78                    ))
79                }
80                (TypeInfo::Slice(type_arg), TypeInfo::Slice(_)) => {
81                    let inner_type = if ctx.abi_with_fully_specified_types {
82                        type_engine
83                            .get(type_arg.type_id)
84                            .abi_str(handler, ctx, engines, false)?
85                    } else {
86                        "_".to_string()
87                    };
88                    Ok(format!("[{inner_type}]"))
89                }
90                (TypeInfo::Custom { .. }, _) => Ok(format!("generic {self_abi_str}")),
91                _ => type_engine
92                    .get(resolved_type_id)
93                    .abi_str(handler, ctx, engines, true),
94            }
95        }
96    }
97}
98
99impl TypeInfo {
100    pub fn abi_str(
101        &self,
102        handler: &Handler,
103        ctx: &AbiStrContext,
104        engines: &Engines,
105        is_root: bool,
106    ) -> Result<String, ErrorEmitted> {
107        use TypeInfo::*;
108        let decl_engine = engines.de();
109        match self {
110            Unknown => Ok("unknown".into()),
111            Never => Ok("never".into()),
112            UnknownGeneric { name, .. } => Ok(name.to_string()),
113            Placeholder(_) => Ok("_".to_string()),
114            TypeParam(param) => Ok(format!("typeparam({})", param.name())),
115            StringSlice => Ok("str".into()),
116            StringArray(length) => Ok(format!("str[{:?}]", engines.help_out(length.expr()))),
117            UnsignedInteger(x) => Ok(match x {
118                IntegerBits::Eight => "u8",
119                IntegerBits::Sixteen => "u16",
120                IntegerBits::ThirtyTwo => "u32",
121                IntegerBits::SixtyFour => "u64",
122                IntegerBits::V256 => "u256",
123            }
124            .into()),
125            Boolean => Ok("bool".into()),
126            Custom {
127                qualified_call_path: call_path,
128                ..
129            } => Ok(call_path.call_path.suffix.to_string()),
130            Tuple(fields) => {
131                let field_strs = fields
132                    .iter()
133                    .map(|field| field.abi_str(handler, ctx, engines, false))
134                    .collect::<Result<Vec<String>, ErrorEmitted>>()?;
135                Ok(format!("({})", field_strs.join(", ")))
136            }
137            B256 => Ok("b256".into()),
138            Numeric => Ok("u64".into()), // u64 is the default
139            Contract => Ok("contract".into()),
140            ErrorRecovery(_) => Ok("unknown due to error".into()),
141            UntypedEnum(decl_id) => {
142                let decl = engines.pe().get_enum(decl_id);
143                Ok(format!("untyped enum {}", decl.name))
144            }
145            UntypedStruct(decl_id) => {
146                let decl = engines.pe().get_struct(decl_id);
147                Ok(format!("untyped struct {}", decl.name))
148            }
149            Enum(decl_ref) => {
150                let decl = decl_engine.get_enum(decl_ref);
151                let type_params = if (ctx.abi_root_type_without_generic_type_parameters && is_root)
152                    || decl.generic_parameters.is_empty()
153                {
154                    ""
155                } else {
156                    let params = decl
157                        .generic_parameters
158                        .iter()
159                        .map(|p| p.abi_str(handler, engines, ctx, false))
160                        .collect::<Result<Vec<_>, _>>()?;
161                    &format!("<{}>", params.join(","))
162                };
163                let abi_call_path = get_abi_call_path(handler, &decl.call_path, &decl.attributes)?;
164                Ok(format!(
165                    "enum {}{}",
166                    call_path_display(ctx, &abi_call_path),
167                    type_params
168                ))
169            }
170            Struct(decl_ref) => {
171                let decl = decl_engine.get_struct(decl_ref);
172                let type_params = if (ctx.abi_root_type_without_generic_type_parameters && is_root)
173                    || decl.generic_parameters.is_empty()
174                {
175                    "".into()
176                } else {
177                    let params = decl
178                        .generic_parameters
179                        .iter()
180                        .map(|p| p.abi_str(handler, engines, ctx, false))
181                        .collect::<Result<Vec<_>, _>>()?;
182                    format!("<{}>", params.join(","))
183                };
184                let abi_call_path = get_abi_call_path(handler, &decl.call_path, &decl.attributes)?;
185                Ok(format!(
186                    "struct {}{}",
187                    call_path_display(ctx, &abi_call_path),
188                    type_params
189                ))
190            }
191            ContractCaller { abi_name, .. } => Ok(format!("contract caller {abi_name}")),
192            Array(elem_ty, length) => Ok(format!(
193                "[{}; {:?}]",
194                elem_ty.abi_str(handler, ctx, engines, false)?,
195                engines.help_out(length.expr())
196            )),
197            RawUntypedPtr => Ok("raw untyped ptr".into()),
198            RawUntypedSlice => Ok("raw untyped slice".into()),
199            Ptr(ty) => Ok(format!(
200                "__ptr {}",
201                ty.abi_str(handler, ctx, engines, false)?
202            )),
203            Slice(ty) => Ok(format!(
204                "__slice {}",
205                ty.abi_str(handler, ctx, engines, false)?
206            )),
207            Alias { ty, .. } => Ok(ty.abi_str(handler, ctx, engines, false)?),
208            TraitType {
209                name,
210                implemented_in: _,
211            } => Ok(format!("trait type {name}")),
212            Ref {
213                to_mutable_value,
214                referenced_type,
215            } => {
216                Ok(format!(
217                    "__ref {}{}", // TODO: (REFERENCES) No references in ABIs according to the RFC. Or we want to have them?
218                    if *to_mutable_value { "mut " } else { "" },
219                    referenced_type.abi_str(handler, ctx, engines, false)?
220                ))
221            }
222        }
223    }
224}
225
226fn get_abi_call_path(
227    handler: &Handler,
228    call_path: &CallPath,
229    attributes: &transform::Attributes,
230) -> Result<CallPath, ErrorEmitted> {
231    let mut abi_call_path = call_path.clone();
232    if let Some(abi_name_attr) = attributes.abi_name() {
233        let name = abi_name_attr.args.first().unwrap();
234        let ident = Ident::new_no_span(name.get_string(handler, abi_name_attr)?.clone());
235        abi_call_path.suffix = ident;
236    }
237    Ok(abi_call_path)
238}
239
240/// `call_path_display`  returns the provided `call_path` without the first prefix in case it is equal to the program name.
241/// If the program name is `my_program` and the `call_path` is `my_program::MyStruct` then this function returns only `MyStruct`.
242fn call_path_display(ctx: &AbiStrContext, call_path: &CallPath) -> String {
243    if !ctx.abi_with_callpaths {
244        return call_path.suffix.as_str().to_string();
245    }
246    let mut buf = String::new();
247    for (index, prefix) in call_path.prefixes.iter().enumerate() {
248        if index == 0 && prefix.as_str() == ctx.program_name {
249            continue;
250        }
251        buf.push_str(prefix.as_str());
252        buf.push_str("::");
253    }
254    buf.push_str(&call_path.suffix.to_string());
255
256    buf
257}
258
259impl GenericTypeArgument {
260    pub(self) fn abi_str(
261        &self,
262        handler: &Handler,
263        ctx: &AbiStrContext,
264        engines: &Engines,
265        is_root: bool,
266    ) -> Result<String, ErrorEmitted> {
267        engines
268            .te()
269            .get(self.type_id)
270            .abi_str(handler, ctx, engines, is_root)
271    }
272}