1use super::{Gcx, Ty, TyFnKind, TyKind};
2use crate::hir;
3use alloy_json_abi as json;
4use solar_ast::{DataLocation, 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 in_library: bool,
14 ) -> String {
15 let mut s = String::with_capacity(64);
16 s.push_str(name);
17 TyAbiPrinter::new(self, &mut s, TyAbiPrinterMode::Signature)
18 .with_in_library(in_library)
19 .print_tuple(tys)
20 .unwrap();
21 s
22 }
23
24 pub fn contract_abi<'a>(self, id: hir::ContractId) -> Vec<json::AbiItem<'a>> {
28 let mut items = Vec::<json::AbiItem<'a>>::new();
29
30 let c = self.hir.contract(id);
31 if let Some(ctor) = c.ctor
32 && !c.is_abstract()
33 {
34 let json::Function { inputs, state_mutability, .. } = self.function_abi(ctor);
35 items.push(json::Constructor { inputs, state_mutability }.into());
36 }
37 if let Some(fallback) = c.fallback {
38 let json::Function { state_mutability, .. } = self.function_abi(fallback);
39 items.push(json::Fallback { state_mutability }.into());
40 }
41 if let Some(receive) = c.receive {
42 let json::Function { state_mutability, .. } = self.function_abi(receive);
43 items.push(json::Receive { state_mutability }.into());
44 }
45 for f in self.interface_functions(id) {
46 items.push(self.function_abi(f.id).into());
47 }
48 for item in self.hir.contract_item_ids(id) {
51 match item {
52 hir::ItemId::Event(id) => items.push(self.event_abi(id).into()),
53 hir::ItemId::Error(id) => items.push(self.error_abi(id).into()),
54 _ => {}
55 }
56 }
57
58 fn cmp_key<'a>(item: &'a json::AbiItem<'_>) -> impl Ord + use<'a> {
60 (item.json_type(), item.name())
61 }
62 items.sort_by(|a, b| cmp_key(a).cmp(&cmp_key(b)));
63
64 items
65 }
66
67 fn function_abi(self, id: hir::FunctionId) -> json::Function {
68 let f = self.hir.function(id);
69 json::Function {
70 name: f.name.unwrap_or_default().to_string(),
71 inputs: f.parameters.iter().map(|&p| self.var_param_abi(p)).collect(),
72 outputs: f.returns.iter().map(|&p| self.var_param_abi(p)).collect(),
73 state_mutability: json_state_mutability(f.state_mutability),
74 }
75 }
76
77 fn event_abi(self, id: hir::EventId) -> json::Event {
78 let e = self.hir.event(id);
79 json::Event {
80 name: e.name.to_string(),
81 inputs: e.parameters.iter().map(|&p| self.event_param_abi(p)).collect(),
82 anonymous: e.anonymous,
83 }
84 }
85
86 fn error_abi(self, id: hir::ErrorId) -> json::Error {
87 let e = self.hir.error(id);
88 json::Error {
89 name: e.name.to_string(),
90 inputs: e.parameters.iter().map(|&p| self.var_param_abi(p)).collect(),
91 }
92 }
93
94 fn var_param_abi(self, id: hir::VariableId) -> json::Param {
95 let v = self.hir.variable(id);
96 let ty = self.type_of_item(id.into());
97 self.param_abi(ty, v.name.unwrap_or_default().to_string())
98 }
99
100 fn param_abi(self, ty: Ty<'gcx>, name: String) -> json::Param {
101 let ty = ty.peel_refs();
102 let struct_id = ty.visit(&mut |ty| match ty.kind {
103 TyKind::Struct(id) => ControlFlow::Break(id),
104 _ => ControlFlow::Continue(()),
105 });
106 json::Param {
107 ty: self.print_abi_param_ty(ty),
108 name,
109 components: match struct_id {
110 ControlFlow::Break(id) => self
111 .item_fields(id)
112 .map(|(ty, f)| self.param_abi(ty, self.item_name(f).to_string()))
113 .collect(),
114 ControlFlow::Continue(()) => vec![],
115 },
116 internal_type: Some(json::InternalType::parse(&self.print_solc_param_ty(ty)).unwrap()),
117 }
118 }
119
120 fn event_param_abi(self, id: hir::VariableId) -> json::EventParam {
121 let json::Param { ty, name, components, internal_type } = self.var_param_abi(id);
122 let indexed = self.hir.variable(id).indexed;
123 json::EventParam { ty, name, components, internal_type, indexed }
124 }
125
126 fn print_abi_param_ty(self, ty: Ty<'gcx>) -> String {
127 let mut s = String::new();
128 TyAbiPrinter::new(self, &mut s, TyAbiPrinterMode::Abi).print(ty).unwrap();
129 s
130 }
131
132 fn print_solc_param_ty(self, ty: Ty<'gcx>) -> String {
133 let mut s = String::new();
134 TySolcPrinter::new(self, &mut s).data_locations(false).print(ty).unwrap();
135 s
136 }
137}
138
139fn json_state_mutability(s: hir::StateMutability) -> json::StateMutability {
140 match s {
141 hir::StateMutability::Pure => json::StateMutability::Pure,
142 hir::StateMutability::View => json::StateMutability::View,
143 hir::StateMutability::Payable => json::StateMutability::Payable,
144 hir::StateMutability::NonPayable => json::StateMutability::NonPayable,
145 }
146}
147
148pub struct TyAbiPrinter<'gcx, W> {
152 gcx: Gcx<'gcx>,
153 buf: W,
154 mode: TyAbiPrinterMode,
155 in_library: bool,
161}
162
163#[derive(Clone, Copy, Debug, PartialEq, Eq)]
165pub enum TyAbiPrinterMode {
166 Signature,
172 Abi,
176}
177
178impl<'gcx, W: fmt::Write> TyAbiPrinter<'gcx, W> {
179 pub fn new(gcx: Gcx<'gcx>, buf: W, mode: TyAbiPrinterMode) -> Self {
181 Self { gcx, buf, mode, in_library: false }
182 }
183
184 pub fn with_in_library(mut self, yes: bool) -> Self {
186 self.in_library = yes;
187 self
188 }
189
190 pub fn buf(&mut self) -> &mut W {
192 &mut self.buf
193 }
194
195 pub fn into_buf(self) -> W {
197 self.buf
198 }
199
200 pub fn print(&mut self, ty: Ty<'gcx>) -> fmt::Result {
202 match ty.kind {
203 TyKind::Elementary(ty) => ty.write_abi_str(&mut self.buf),
204 TyKind::Contract(id) if self.mode == TyAbiPrinterMode::Signature && self.in_library => {
205 write!(self.buf, "{}", self.gcx.item_canonical_name(id))
206 }
207 TyKind::Contract(_) => self.buf.write_str("address"),
208 TyKind::Fn(_) => self.buf.write_str("function"),
209 TyKind::Struct(id) => match self.mode {
210 TyAbiPrinterMode::Signature if self.in_library => {
211 write!(self.buf, "{}", self.gcx.item_canonical_name(id))
212 }
213 TyAbiPrinterMode::Signature => {
214 if self.gcx.struct_recursiveness(id).is_recursive() {
215 assert!(
216 self.gcx.dcx().has_errors().is_err(),
217 "trying to print recursive struct and no error has been emitted"
218 );
219 write!(self.buf, "<recursive struct {}>", self.gcx.item_canonical_name(id))
220 } else {
221 self.print_tuple(self.gcx.struct_field_types(id).iter().copied())
222 }
223 }
224 TyAbiPrinterMode::Abi => self.buf.write_str("tuple"),
225 },
226 TyKind::Enum(id) => match self.mode {
227 TyAbiPrinterMode::Signature if self.in_library => {
228 write!(self.buf, "{}", self.gcx.item_canonical_name(id))
229 }
230 _ => self.buf.write_str("uint8"),
231 },
232 TyKind::Udvt(ty, _) => self.print(ty),
233 TyKind::Ref(ty, loc) => {
234 self.print(ty)?;
235 if self.in_library && loc == DataLocation::Storage {
239 self.buf.write_str(" storage")?;
240 }
241 Ok(())
242 }
243 TyKind::Mapping(key, value) if self.mode == TyAbiPrinterMode::Signature => {
246 self.buf.write_str("mapping(")?;
247 self.print(key)?;
248 self.buf.write_str(" => ")?;
249 self.print(value)?;
250 self.buf.write_str(")")
251 }
252 TyKind::DynArray(ty) => {
253 self.print(ty)?;
254 self.buf.write_str("[]")
255 }
256 TyKind::Array(ty, len) => {
257 self.print(ty)?;
258 write!(self.buf, "[{len}]")
259 }
260
261 TyKind::Slice(..)
262 | TyKind::StringLiteral(..)
263 | TyKind::IntLiteral(..)
264 | TyKind::Tuple(_)
265 | TyKind::Mapping(..)
266 | TyKind::Super(_)
267 | TyKind::Error(..)
269 | TyKind::Event(..)
270 | TyKind::Module(_)
271 | TyKind::BuiltinModule(_)
272 | TyKind::Variadic
273 | TyKind::Type(_)
274 | TyKind::Meta(_)
275 | TyKind::Err(_) => {
276 assert!(
277 self.gcx.dcx().has_errors().is_err(),
278 "printing unsupported type as ABI: {ty:?}"
279 );
280 self.buf.write_str("<error>")
281 }
282 }
283 }
284
285 pub fn print_tuple(&mut self, tys: impl IntoIterator<Item = Ty<'gcx>>) -> fmt::Result {
287 self.buf.write_str("(")?;
288 for (i, ty) in tys.into_iter().enumerate() {
289 if i > 0 {
290 self.buf.write_str(",")?;
291 }
292 self.print(ty)?;
293 }
294 self.buf.write_str(")")
295 }
296}
297
298pub(crate) struct TySolcPrinter<'gcx, W> {
304 gcx: Gcx<'gcx>,
305 buf: W,
306 data_locations: bool,
307}
308
309impl<'gcx, W: fmt::Write> TySolcPrinter<'gcx, W> {
310 pub(crate) fn new(gcx: Gcx<'gcx>, buf: W) -> Self {
311 Self { gcx, buf, data_locations: false }
312 }
313
314 pub(crate) fn data_locations(mut self, yes: bool) -> Self {
318 self.data_locations = yes;
319 self
320 }
321
322 pub(crate) fn print(&mut self, ty: Ty<'gcx>) -> fmt::Result {
323 match ty.kind {
324 TyKind::Elementary(ty) => {
325 ty.write_abi_str(&mut self.buf)?;
326 if matches!(ty, ElementaryType::Address(true)) {
327 self.buf.write_str(" payable")?;
328 }
329 Ok(())
330 }
331 TyKind::Contract(id) => {
332 let c = self.gcx.hir.contract(id);
333 self.buf.write_str(if c.kind.is_library() { "library" } else { "contract" })?;
334 write!(self.buf, " {}", c.name)
335 }
336 TyKind::Super(id) => {
337 let c = self.gcx.hir.contract(id);
338 write!(self.buf, "contract super {}", c.name)
339 }
340 TyKind::Fn(f) => {
341 self.buf.write_str("function ")?;
342 if f.is_declaration()
343 && let Some(id) = f.function_id
344 {
345 let name = self.gcx.item_canonical_name(hir::ItemId::from(id));
346 write!(self.buf, "{name}")?;
347 }
348 self.print_tuple(f.parameters)?;
349
350 if f.state_mutability != hir::StateMutability::NonPayable {
351 write!(self.buf, " {}", f.state_mutability)?;
352 }
353 if f.kind == TyFnKind::External {
354 self.buf.write_str(" external")?;
355 }
356
357 if !f.returns.is_empty() {
358 self.buf.write_str(" returns ")?;
359 self.print_tuple(f.returns)?;
360 }
361 Ok(())
362 }
363 TyKind::Struct(id) => {
364 write!(self.buf, "struct {}", self.gcx.item_canonical_name(id))
365 }
366 TyKind::Enum(id) => write!(self.buf, "enum {}", self.gcx.item_canonical_name(id)),
367 TyKind::Udvt(_, id) => write!(self.buf, "{}", self.gcx.item_canonical_name(id)),
368 TyKind::Ref(ty, loc) => {
369 self.print(ty)?;
370 if self.data_locations {
371 write!(self.buf, " {loc}")?;
372 }
373 Ok(())
374 }
375 TyKind::DynArray(ty) => {
376 self.print(ty)?;
377 self.buf.write_str("[]")
378 }
379 TyKind::Array(ty, len) => {
380 self.print(ty)?;
381 write!(self.buf, "[{len}]")
382 }
383
384 TyKind::StringLiteral(utf8, size) => {
386 let kind = if utf8 { "utf8" } else { "bytes" };
387 write!(self.buf, "{kind}_string_literal[{}]", size.bytes())
388 }
389 TyKind::IntLiteral(_, size, _) => {
390 write!(self.buf, "int_literal[{}]", size.bits())
391 }
392 TyKind::Slice(ty) => {
393 self.print(ty)?;
394 self.buf.write_str(" slice")
395 }
396 TyKind::Tuple(tys) => {
397 self.buf.write_str("tuple")?;
398 self.print_tuple(tys)
399 }
400 TyKind::Mapping(key, value) => {
401 self.buf.write_str("mapping(")?;
402 self.print(key)?;
403 self.buf.write_str(" => ")?;
404 self.print(value)?;
405 self.buf.write_str(")")
406 }
407 TyKind::Module(id) => {
408 let s = self.gcx.hir.source(id);
409 write!(self.buf, "module {}", s.file.name.display())
410 }
411 TyKind::BuiltinModule(b) => self.buf.write_str(b.name().as_str()),
412 TyKind::Variadic => self.buf.write_str("..."),
413 TyKind::Type(ty) | TyKind::Meta(ty) => {
414 self.buf.write_str("type(")?;
415 self.print(ty)?; self.buf.write_str(")")
417 }
418 TyKind::Error(tys, id) => {
419 self.buf.write_str("error ")?;
420 write!(self.buf, "{}", self.gcx.item_canonical_name(id))?;
421 self.print_tuple(tys)
422 }
423 TyKind::Event(tys, id) => {
424 self.buf.write_str("event ")?;
425 write!(self.buf, "{}", self.gcx.item_canonical_name(id))?;
426 self.print_tuple(tys)
427 }
428
429 TyKind::Err(_) => self.buf.write_str("<error>"),
430 }
431 }
432
433 fn print_tuple(&mut self, tys: &[Ty<'gcx>]) -> fmt::Result {
434 self.buf.write_str("(")?;
435 for (i, &ty) in tys.iter().enumerate() {
436 if i > 0 {
437 self.buf.write_str(",")?;
438 }
439 self.print(ty)?;
440 }
441 self.buf.write_str(")")
442 }
443}