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 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()), 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 {}{}", 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
240fn 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}