1use super::{Gcx, Ty, TyKind};
2use crate::hir;
3use alloy_json_abi as json;
4use solar_ast::ElementaryType;
5use std::{fmt, ops::ControlFlow};
6
7impl<'gcx> Gcx<'gcx> {
8 pub(super) fn mk_abi_signature(
10 self,
11 name: &str,
12 tys: impl IntoIterator<Item = Ty<'gcx>>,
13 ) -> String {
14 let mut s = String::with_capacity(64);
15 s.push_str(name);
16 TyAbiPrinter::new(self, &mut s, TyAbiPrinterMode::Signature).print_tuple(tys).unwrap();
17 s
18 }
19
20 pub fn contract_abi<'a>(self, id: hir::ContractId) -> Vec<json::AbiItem<'a>> {
24 let mut items = Vec::<json::AbiItem<'a>>::new();
25
26 let c = self.hir.contract(id);
27 if let Some(ctor) = c.ctor
28 && !c.is_abstract()
29 {
30 let json::Function { inputs, state_mutability, .. } = self.function_abi(ctor);
31 items.push(json::Constructor { inputs, state_mutability }.into());
32 }
33 if let Some(fallback) = c.fallback {
34 let json::Function { state_mutability, .. } = self.function_abi(fallback);
35 items.push(json::Fallback { state_mutability }.into());
36 }
37 if let Some(receive) = c.receive {
38 let json::Function { state_mutability, .. } = self.function_abi(receive);
39 items.push(json::Receive { state_mutability }.into());
40 }
41 for f in self.interface_functions(id) {
42 items.push(self.function_abi(f.id).into());
43 }
44 for item in self.hir.contract_item_ids(id) {
47 match item {
48 hir::ItemId::Event(id) => items.push(self.event_abi(id).into()),
49 hir::ItemId::Error(id) => items.push(self.error_abi(id).into()),
50 _ => {}
51 }
52 }
53
54 fn cmp_key<'a>(item: &'a json::AbiItem<'_>) -> impl Ord + use<'a> {
56 (item.json_type(), item.name())
57 }
58 items.sort_by(|a, b| cmp_key(a).cmp(&cmp_key(b)));
59
60 items
61 }
62
63 fn function_abi(self, id: hir::FunctionId) -> json::Function {
64 let f = self.hir.function(id);
65 json::Function {
66 name: f.name.unwrap_or_default().to_string(),
67 inputs: f.parameters.iter().map(|&p| self.var_param_abi(p)).collect(),
68 outputs: f.returns.iter().map(|&p| self.var_param_abi(p)).collect(),
69 state_mutability: json_state_mutability(f.state_mutability),
70 }
71 }
72
73 fn event_abi(self, id: hir::EventId) -> json::Event {
74 let e = self.hir.event(id);
75 json::Event {
76 name: e.name.to_string(),
77 inputs: e.parameters.iter().map(|&p| self.event_param_abi(p)).collect(),
78 anonymous: e.anonymous,
79 }
80 }
81
82 fn error_abi(self, id: hir::ErrorId) -> json::Error {
83 let e = self.hir.error(id);
84 json::Error {
85 name: e.name.to_string(),
86 inputs: e.parameters.iter().map(|&p| self.var_param_abi(p)).collect(),
87 }
88 }
89
90 fn var_param_abi(self, id: hir::VariableId) -> json::Param {
91 let v = self.hir.variable(id);
92 let ty = self.type_of_item(id.into());
93 self.param_abi(ty, v.name.unwrap_or_default().to_string())
94 }
95
96 fn param_abi(self, ty: Ty<'gcx>, name: String) -> json::Param {
97 let ty = ty.peel_refs();
98 let struct_id = ty.visit(&mut |ty| match ty.kind {
99 TyKind::Struct(id) => ControlFlow::Break(id),
100 _ => ControlFlow::Continue(()),
101 });
102 json::Param {
103 ty: self.print_abi_param_ty(ty),
104 name,
105 components: match struct_id {
106 ControlFlow::Break(id) => self
107 .item_fields(id)
108 .map(|(ty, f)| self.param_abi(ty, self.item_name(f).to_string()))
109 .collect(),
110 ControlFlow::Continue(()) => vec![],
111 },
112 internal_type: Some(json::InternalType::parse(&self.print_solc_param_ty(ty)).unwrap()),
113 }
114 }
115
116 fn event_param_abi(self, id: hir::VariableId) -> json::EventParam {
117 let json::Param { ty, name, components, internal_type } = self.var_param_abi(id);
118 let indexed = self.hir.variable(id).indexed;
119 json::EventParam { ty, name, components, internal_type, indexed }
120 }
121
122 fn print_abi_param_ty(self, ty: Ty<'gcx>) -> String {
123 let mut s = String::new();
124 TyAbiPrinter::new(self, &mut s, TyAbiPrinterMode::Abi).print(ty).unwrap();
125 s
126 }
127
128 fn print_solc_param_ty(self, ty: Ty<'gcx>) -> String {
129 let mut s = String::new();
130 TySolcPrinter::new(self, &mut s).data_locations(false).print(ty).unwrap();
131 s
132 }
133}
134
135fn json_state_mutability(s: hir::StateMutability) -> json::StateMutability {
136 match s {
137 hir::StateMutability::Pure => json::StateMutability::Pure,
138 hir::StateMutability::View => json::StateMutability::View,
139 hir::StateMutability::Payable => json::StateMutability::Payable,
140 hir::StateMutability::NonPayable => json::StateMutability::NonPayable,
141 }
142}
143
144pub struct TyAbiPrinter<'gcx, W> {
148 gcx: Gcx<'gcx>,
149 buf: W,
150 mode: TyAbiPrinterMode,
151}
152
153#[derive(Clone, Copy, Debug, PartialEq, Eq)]
155pub enum TyAbiPrinterMode {
156 Signature,
162 Abi,
166}
167
168impl<'gcx, W: fmt::Write> TyAbiPrinter<'gcx, W> {
169 pub fn new(gcx: Gcx<'gcx>, buf: W, mode: TyAbiPrinterMode) -> Self {
171 Self { gcx, buf, mode }
172 }
173
174 pub fn buf(&mut self) -> &mut W {
176 &mut self.buf
177 }
178
179 pub fn into_buf(self) -> W {
181 self.buf
182 }
183
184 pub fn print(&mut self, ty: Ty<'gcx>) -> fmt::Result {
186 match ty.kind {
187 TyKind::Elementary(ty) => ty.write_abi_str(&mut self.buf),
188 TyKind::Contract(_) => self.buf.write_str("address"),
189 TyKind::FnPtr(_) => self.buf.write_str("function"),
190 TyKind::Struct(id) => match self.mode {
191 TyAbiPrinterMode::Signature => {
192 if self.gcx.struct_recursiveness(id).is_recursive() {
193 assert!(
194 self.gcx.dcx().has_errors().is_err(),
195 "trying to print recursive struct and no error has been emitted"
196 );
197 write!(self.buf, "<recursive struct {}>", self.gcx.item_canonical_name(id))
198 } else {
199 self.print_tuple(self.gcx.struct_field_types(id).iter().copied())
200 }
201 }
202 TyAbiPrinterMode::Abi => self.buf.write_str("tuple"),
203 },
204 TyKind::Enum(_) => self.buf.write_str("uint8"),
205 TyKind::Udvt(ty, _) => self.print(ty),
206 TyKind::Ref(ty, _loc) => self.print(ty),
207 TyKind::DynArray(ty) => {
208 self.print(ty)?;
209 self.buf.write_str("[]")
210 }
211 TyKind::Array(ty, len) => {
212 self.print(ty)?;
213 write!(self.buf, "[{len}]")
214 }
215
216 TyKind::StringLiteral(..)
217 | TyKind::IntLiteral(_)
218 | TyKind::Tuple(_)
219 | TyKind::Mapping(..)
220 | TyKind::Error(..)
221 | TyKind::Event(..)
222 | TyKind::Module(_)
223 | TyKind::BuiltinModule(_)
224 | TyKind::Type(_)
225 | TyKind::Meta(_)
226 | TyKind::Err(_) => panic!("printing unsupported type as ABI: {ty:?}"),
227 }
228 }
229
230 pub fn print_tuple(&mut self, tys: impl IntoIterator<Item = Ty<'gcx>>) -> fmt::Result {
232 self.buf.write_str("(")?;
233 for (i, ty) in tys.into_iter().enumerate() {
234 if i > 0 {
235 self.buf.write_str(",")?;
236 }
237 self.print(ty)?;
238 }
239 self.buf.write_str(")")
240 }
241}
242
243pub(crate) struct TySolcPrinter<'gcx, W> {
249 gcx: Gcx<'gcx>,
250 buf: W,
251 data_locations: bool,
252}
253
254impl<'gcx, W: fmt::Write> TySolcPrinter<'gcx, W> {
255 pub(crate) fn new(gcx: Gcx<'gcx>, buf: W) -> Self {
256 Self { gcx, buf, data_locations: false }
257 }
258
259 pub(crate) fn data_locations(mut self, yes: bool) -> Self {
263 self.data_locations = yes;
264 self
265 }
266
267 pub(crate) fn print(&mut self, ty: Ty<'gcx>) -> fmt::Result {
268 match ty.kind {
269 TyKind::Elementary(ty) => {
270 ty.write_abi_str(&mut self.buf)?;
271 if matches!(ty, ElementaryType::Address(true)) {
272 self.buf.write_str(" payable")?;
273 }
274 Ok(())
275 }
276 TyKind::Contract(id) => {
277 let c = self.gcx.hir.contract(id);
278 self.buf.write_str(if c.kind.is_library() { "library" } else { "contract" })?;
279 write!(self.buf, " {}", c.name)
280 }
281 TyKind::FnPtr(f) => {
282 self.print_function(None, f.parameters, f.returns, f.state_mutability, f.visibility)
283 }
284 TyKind::Struct(id) => {
285 write!(self.buf, "struct {}", self.gcx.item_canonical_name(id))
286 }
287 TyKind::Enum(id) => write!(self.buf, "enum {}", self.gcx.item_canonical_name(id)),
288 TyKind::Udvt(_, id) => write!(self.buf, "{}", self.gcx.item_canonical_name(id)),
289 TyKind::Ref(ty, loc) => {
290 self.print(ty)?;
291 if self.data_locations {
292 write!(self.buf, " {loc}")?;
293 }
294 Ok(())
295 }
296 TyKind::DynArray(ty) => {
297 self.print(ty)?;
298 self.buf.write_str("[]")
299 }
300 TyKind::Array(ty, len) => {
301 self.print(ty)?;
302 write!(self.buf, "[{len}]")
303 }
304
305 TyKind::StringLiteral(utf8, size) => {
307 let kind = if utf8 { "utf8" } else { "bytes" };
308 write!(self.buf, "{kind}_string_literal[{}]", size.bytes())
309 }
310 TyKind::IntLiteral(size) => {
311 write!(self.buf, "int_literal[{}]", size.bytes())
312 }
313 TyKind::Tuple(tys) => {
314 self.buf.write_str("tuple")?;
315 self.print_tuple(tys)
316 }
317 TyKind::Mapping(key, value) => {
318 self.buf.write_str("mapping(")?;
319 self.print(key)?;
320 self.buf.write_str(" => ")?;
321 self.print(value)?;
322 self.buf.write_str(")")
323 }
324 TyKind::Module(id) => {
325 let s = self.gcx.hir.source(id);
326 write!(self.buf, "module {}", s.file.name.display())
327 }
328 TyKind::BuiltinModule(b) => self.buf.write_str(b.name().as_str()),
329 TyKind::Type(ty) | TyKind::Meta(ty) => {
330 self.buf.write_str("type(")?;
331 self.print(ty)?; self.buf.write_str(")")
333 }
334 TyKind::Error(tys, id) => self.print_function_like(tys, id.into()),
335 TyKind::Event(tys, id) => self.print_function_like(tys, id.into()),
336
337 TyKind::Err(_) => self.buf.write_str("<error>"),
338 }
339 }
340
341 fn print_function_like(&mut self, parameters: &[Ty<'gcx>], id: hir::ItemId) -> fmt::Result {
342 self.print_function(
343 Some(id),
344 parameters,
345 &[],
346 hir::StateMutability::NonPayable,
347 solar_ast::Visibility::Internal,
348 )
349 }
350
351 fn print_function(
352 &mut self,
353 def: Option<hir::ItemId>,
354 parameters: &[Ty<'gcx>],
355 returns: &[Ty<'gcx>],
356 state_mutability: hir::StateMutability,
357 visibility: hir::Visibility,
358 ) -> fmt::Result {
359 self.buf.write_str("function ")?;
360 if let Some(def) = def {
361 let name = self.gcx.item_canonical_name(def);
362 write!(self.buf, "{name}")?;
363 }
364 self.print_tuple(parameters)?;
365
366 if state_mutability != hir::StateMutability::NonPayable {
367 write!(self.buf, " {state_mutability}")?;
368 }
369 if visibility == hir::Visibility::External {
370 self.buf.write_str(" external")?;
371 }
372
373 if !returns.is_empty() {
374 self.buf.write_str(" returns ")?;
375 self.print_tuple(returns)?;
376 }
377 Ok(())
378 }
379
380 fn print_tuple(&mut self, tys: &[Ty<'gcx>]) -> fmt::Result {
381 self.buf.write_str("(")?;
382 for (i, &ty) in tys.iter().enumerate() {
383 if i > 0 {
384 self.buf.write_str(",")?;
385 }
386 self.print(ty)?;
387 }
388 self.buf.write_str(")")
389 }
390}