1use crate::{
6 prelude::{ChiselDispatcher, ChiselResult, ChiselRunner, SessionSource, SolidityHelper},
7 source::IntermediateOutput,
8};
9use alloy_dyn_abi::{DynSolType, DynSolValue};
10use alloy_json_abi::EventParam;
11use alloy_primitives::{Address, B256, U256, hex};
12use eyre::{Result, WrapErr};
13use foundry_compilers::Artifact;
14use foundry_evm::{
15 backend::Backend, decode::decode_console_logs, executors::ExecutorBuilder,
16 inspectors::CheatsConfig, traces::TraceMode,
17};
18use solang_parser::pt;
19use std::ops::ControlFlow;
20use yansi::Paint;
21
22impl SessionSource {
24 pub async fn execute(&mut self) -> Result<ChiselResult> {
26 let output = self.build()?;
28
29 let (bytecode, final_pc) = output.enter(|output| -> Result<_> {
30 let contract = output
31 .repl_contract()
32 .ok_or_else(|| eyre::eyre!("failed to find REPL contract"))?;
33 trace!(?contract, "REPL contract");
34 let bytecode = contract
35 .get_bytecode_bytes()
36 .ok_or_else(|| eyre::eyre!("No bytecode found for `REPL` contract"))?;
37 Ok((bytecode.into_owned(), output.final_pc(contract)?))
38 })?;
39 let final_pc = final_pc.unwrap_or_default();
40 let mut runner = self.build_runner(final_pc).await?;
41 runner.run(bytecode)
42 }
43
44 pub async fn inspect(&self, input: &str) -> Result<(ControlFlow<()>, Option<String>)> {
56 let line = format!("bytes memory inspectoor = abi.encode({input});");
57 let mut source = match self.clone_with_new_line(line) {
58 Ok((source, _)) => source,
59 Err(err) => {
60 debug!(%err, "failed to build new source for inspection");
61 return Ok((ControlFlow::Continue(()), None));
62 }
63 };
64
65 let mut source_without_inspector = self.clone();
66
67 let (mut res, err) = match source.execute().await {
70 Ok(res) => (res, None),
71 Err(err) => {
72 debug!(?err, %input, "execution failed");
73 match source_without_inspector.execute().await {
74 Ok(res) => (res, Some(err)),
75 Err(_) => {
76 if self.config.foundry_config.verbosity >= 3 {
77 sh_err!("Could not inspect: {err}")?;
78 }
79 return Ok((ControlFlow::Continue(()), None));
80 }
81 }
82 }
83 };
84
85 if let Some(err) = err {
87 let output = source_without_inspector.build()?;
88
89 let formatted_event =
90 output.enter(|output| output.get_event(input).map(format_event_definition));
91 if let Some(formatted_event) = formatted_event {
92 return Ok((ControlFlow::Break(()), Some(formatted_event?)));
93 }
94
95 if self.config.foundry_config.verbosity >= 3 {
97 sh_err!("Failed eval: {err}")?;
98 }
99
100 debug!(%err, %input, "failed abi encode input");
101 return Ok((ControlFlow::Break(()), None));
102 }
103 drop(source_without_inspector);
104
105 let Some((stack, memory)) = &res.state else {
106 if let Ok(decoder) = ChiselDispatcher::decode_traces(&source.config, &mut res).await {
108 ChiselDispatcher::show_traces(&decoder, &mut res).await?;
109 }
110 let decoded_logs = decode_console_logs(&res.logs);
111 if !decoded_logs.is_empty() {
112 sh_println!("{}", "Logs:".green())?;
113 for log in decoded_logs {
114 sh_println!(" {log}")?;
115 }
116 }
117
118 return Err(eyre::eyre!("Failed to inspect expression"));
119 };
120
121 let generated_output = source.build()?;
124
125 let contract_expr = generated_output
128 .intermediate
129 .repl_contract_expressions
130 .get(input)
131 .or_else(|| source.infer_inner_expr_type());
132
133 let function_call_return_type =
136 Type::get_function_return_type(contract_expr, &generated_output.intermediate);
137
138 let (contract_expr, ty) = if let Some(function_call_return_type) = function_call_return_type
139 {
140 (function_call_return_type.0, function_call_return_type.1)
141 } else {
142 match contract_expr.and_then(|e| {
143 Type::ethabi(e, Some(&generated_output.intermediate)).map(|ty| (e, ty))
144 }) {
145 Some(res) => res,
146 None => return Ok((ControlFlow::Continue(()), None)),
148 }
149 };
150
151 let data = (|| -> Option<_> {
154 let mut offset: usize = stack.last()?.try_into().ok()?;
155 debug!("inspect memory @ {offset}: {}", hex::encode(memory));
156 let mem_offset = memory.get(offset..offset + 32)?;
157 let len: usize = U256::try_from_be_slice(mem_offset)?.try_into().ok()?;
158 offset += 32;
159 memory.get(offset..offset + len)
160 })();
161 let Some(data) = data else {
162 eyre::bail!("Failed to inspect last expression: could not retrieve data from memory")
163 };
164 let token = ty.abi_decode(data).wrap_err("Could not decode inspected values")?;
165 let c = if should_continue(contract_expr) {
166 ControlFlow::Continue(())
167 } else {
168 ControlFlow::Break(())
169 };
170 Ok((c, Some(format_token(token))))
171 }
172
173 fn infer_inner_expr_type(&self) -> Option<&pt::Expression> {
184 let out = self.build().ok()?;
185 let run = out.run_func_body().ok()?.last();
186 match run {
187 Some(pt::Statement::VariableDefinition(
188 _,
189 _,
190 Some(pt::Expression::FunctionCall(_, _, args)),
191 )) => {
192 Some(args.first().unwrap())
196 }
197 _ => None,
198 }
199 }
200
201 async fn build_runner(&mut self, final_pc: usize) -> Result<ChiselRunner> {
202 let env = self.config.evm_opts.evm_env().await?;
203
204 let backend = match self.config.backend.clone() {
205 Some(backend) => backend,
206 None => {
207 let fork = self.config.evm_opts.get_fork(&self.config.foundry_config, env.clone());
208 let backend = Backend::spawn(fork)?;
209 self.config.backend = Some(backend.clone());
210 backend
211 }
212 };
213
214 let executor = ExecutorBuilder::new()
215 .inspectors(|stack| {
216 stack
217 .logs(self.config.foundry_config.live_logs)
218 .chisel_state(final_pc)
219 .trace_mode(TraceMode::Call)
220 .cheatcodes(
221 CheatsConfig::new(
222 &self.config.foundry_config,
223 self.config.evm_opts.clone(),
224 None,
225 None,
226 )
227 .into(),
228 )
229 })
230 .gas_limit(self.config.evm_opts.gas_limit())
231 .spec_id(self.config.foundry_config.evm_spec_id())
232 .legacy_assertions(self.config.foundry_config.legacy_assertions)
233 .build(env, backend);
234
235 Ok(ChiselRunner::new(executor, U256::MAX, Address::ZERO, self.config.calldata.clone()))
236 }
237}
238
239fn format_token(token: DynSolValue) -> String {
242 match token {
243 DynSolValue::Address(a) => {
244 format!("Type: {}\n└ Data: {}", "address".red(), a.cyan())
245 }
246 DynSolValue::FixedBytes(b, byte_len) => {
247 format!(
248 "Type: {}\n└ Data: {}",
249 format!("bytes{byte_len}").red(),
250 hex::encode_prefixed(b).cyan()
251 )
252 }
253 DynSolValue::Int(i, bit_len) => {
254 format!(
255 "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
256 format!("int{bit_len}").red(),
257 format!(
258 "0x{}",
259 format!("{i:x}")
260 .chars()
261 .skip(if i.is_negative() { 64 - bit_len / 4 } else { 0 })
262 .collect::<String>()
263 )
264 .cyan(),
265 hex::encode_prefixed(B256::from(i)).cyan(),
266 i.cyan()
267 )
268 }
269 DynSolValue::Uint(i, bit_len) => {
270 format!(
271 "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
272 format!("uint{bit_len}").red(),
273 format!("0x{i:x}").cyan(),
274 hex::encode_prefixed(B256::from(i)).cyan(),
275 i.cyan()
276 )
277 }
278 DynSolValue::Bool(b) => {
279 format!("Type: {}\n└ Value: {}", "bool".red(), b.cyan())
280 }
281 DynSolValue::String(_) | DynSolValue::Bytes(_) => {
282 let hex = hex::encode(token.abi_encode());
283 let s = token.as_str();
284 format!(
285 "Type: {}\n{}├ Hex (Memory):\n├─ Length ({}): {}\n├─ Contents ({}): {}\n├ Hex (Tuple Encoded):\n├─ Pointer ({}): {}\n├─ Length ({}): {}\n└─ Contents ({}): {}",
286 if s.is_some() { "string" } else { "dynamic bytes" }.red(),
287 if let Some(s) = s {
288 format!("├ UTF-8: {}\n", s.cyan())
289 } else {
290 String::default()
291 },
292 "[0x00:0x20]".yellow(),
293 format!("0x{}", &hex[64..128]).cyan(),
294 "[0x20:..]".yellow(),
295 format!("0x{}", &hex[128..]).cyan(),
296 "[0x00:0x20]".yellow(),
297 format!("0x{}", &hex[..64]).cyan(),
298 "[0x20:0x40]".yellow(),
299 format!("0x{}", &hex[64..128]).cyan(),
300 "[0x40:..]".yellow(),
301 format!("0x{}", &hex[128..]).cyan(),
302 )
303 }
304 DynSolValue::FixedArray(tokens) | DynSolValue::Array(tokens) => {
305 let mut out = format!(
306 "{}({}) = {}",
307 "array".red(),
308 format!("{}", tokens.len()).yellow(),
309 '['.red()
310 );
311 for token in tokens {
312 out.push_str("\n ├ ");
313 out.push_str(&format_token(token).replace('\n', "\n "));
314 out.push('\n');
315 }
316 out.push_str(&']'.red().to_string());
317 out
318 }
319 DynSolValue::Tuple(tokens) => {
320 let displayed_types = tokens
321 .iter()
322 .map(|t| t.sol_type_name().unwrap_or_default())
323 .collect::<Vec<_>>()
324 .join(", ");
325 let mut out =
326 format!("{}({}) = {}", "tuple".red(), displayed_types.yellow(), '('.red());
327 for token in tokens {
328 out.push_str("\n ├ ");
329 out.push_str(&format_token(token).replace('\n', "\n "));
330 out.push('\n');
331 }
332 out.push_str(&')'.red().to_string());
333 out
334 }
335 _ => {
336 unimplemented!()
337 }
338 }
339}
340
341fn format_event_definition(event_definition: &pt::EventDefinition) -> Result<String> {
352 let event_name = event_definition.name.as_ref().expect("Event has a name").to_string();
353 let inputs = event_definition
354 .fields
355 .iter()
356 .map(|param| {
357 let name = param
358 .name
359 .as_ref()
360 .map(ToString::to_string)
361 .unwrap_or_else(|| "<anonymous>".to_string());
362 let kind = Type::from_expression(¶m.ty)
363 .and_then(Type::into_builtin)
364 .ok_or_else(|| eyre::eyre!("Invalid type in event {event_name}"))?;
365 Ok(EventParam {
366 name,
367 ty: kind.to_string(),
368 components: vec![],
369 indexed: param.indexed,
370 internal_type: None,
371 })
372 })
373 .collect::<Result<Vec<_>>>()?;
374 let event =
375 alloy_json_abi::Event { name: event_name, inputs, anonymous: event_definition.anonymous };
376
377 Ok(format!(
378 "Type: {}\n├ Name: {}\n├ Signature: {:?}\n└ Selector: {:?}",
379 "event".red(),
380 SolidityHelper::new().highlight(&format!(
381 "{}({})",
382 &event.name,
383 &event
384 .inputs
385 .iter()
386 .map(|param| format!(
387 "{}{}{}",
388 param.ty,
389 if param.indexed { " indexed" } else { "" },
390 if param.name.is_empty() {
391 String::default()
392 } else {
393 format!(" {}", ¶m.name)
394 },
395 ))
396 .collect::<Vec<_>>()
397 .join(", ")
398 )),
399 event.signature().cyan(),
400 event.selector().cyan(),
401 ))
402}
403
404#[derive(Clone, Debug, PartialEq)]
410enum Type {
411 Builtin(DynSolType),
413
414 Array(Box<Self>),
416
417 FixedArray(Box<Self>, usize),
419
420 ArrayIndex(Box<Self>, Option<usize>),
422
423 Tuple(Vec<Option<Self>>),
425
426 Function(Box<Self>, Vec<Option<Self>>, Vec<Option<Self>>),
428
429 Access(Box<Self>, String),
431
432 Custom(Vec<String>),
434}
435
436impl Type {
437 fn from_expression(expr: &pt::Expression) -> Option<Self> {
447 match expr {
448 pt::Expression::Type(_, ty) => Self::from_type(ty),
449
450 pt::Expression::Variable(ident) => Some(Self::Custom(vec![ident.name.clone()])),
451
452 pt::Expression::ArraySubscript(_, expr, num) => {
454 Self::from_expression(expr).and_then(|ty| {
457 let boxed = Box::new(ty);
458 let num = num.as_deref().and_then(parse_number_literal).and_then(|n| {
459 usize::try_from(n).ok()
460 });
461 match expr.as_ref() {
462 pt::Expression::Type(_, _) => {
464 if let Some(num) = num {
465 Some(Self::FixedArray(boxed, num))
466 } else {
467 Some(Self::Array(boxed))
468 }
469 }
470 pt::Expression::Variable(_) => {
472 Some(Self::ArrayIndex(boxed, num))
473 }
474 _ => None
475 }
476 })
477 }
478 pt::Expression::ArrayLiteral(_, values) => {
479 values.first().and_then(Self::from_expression).map(|ty| {
480 Self::FixedArray(Box::new(ty), values.len())
481 })
482 }
483
484 pt::Expression::List(_, params) => Some(Self::Tuple(map_parameters(params))),
486
487 pt::Expression::MemberAccess(_, lhs, rhs) => {
489 Self::from_expression(lhs).map(|lhs| {
490 Self::Access(Box::new(lhs), rhs.name.clone())
491 })
492 }
493
494 pt::Expression::Parenthesis(_, inner) | pt::Expression::New(_, inner) | pt::Expression::UnaryPlus(_, inner) | pt::Expression::BitwiseNot(_, inner) | pt::Expression::ArraySlice(_, inner, _, _) | pt::Expression::PreDecrement(_, inner) | pt::Expression::PostDecrement(_, inner) | pt::Expression::PreIncrement(_, inner) | pt::Expression::PostIncrement(_, inner) | pt::Expression::Assign(_, inner, _) | pt::Expression::AssignAdd(_, inner, _) | pt::Expression::AssignSubtract(_, inner, _) | pt::Expression::AssignMultiply(_, inner, _) | pt::Expression::AssignDivide(_, inner, _) | pt::Expression::AssignModulo(_, inner, _) | pt::Expression::AssignAnd(_, inner, _) | pt::Expression::AssignOr(_, inner, _) | pt::Expression::AssignXor(_, inner, _) | pt::Expression::AssignShiftLeft(_, inner, _) | pt::Expression::AssignShiftRight(_, inner, _) => Self::from_expression(inner),
518
519 pt::Expression::ConditionalOperator(_, _, if_true, if_false) => {
521 Self::from_expression(if_true).or_else(|| Self::from_expression(if_false))
522 }
523
524 pt::Expression::AddressLiteral(_, _) => Some(Self::Builtin(DynSolType::Address)),
526 pt::Expression::HexNumberLiteral(_, s, _) => {
527 match s.parse::<Address>() {
528 Ok(addr) if *s == addr.to_checksum(None) => {
529 Some(Self::Builtin(DynSolType::Address))
530 }
531 _ => Some(Self::Builtin(DynSolType::Uint(256))),
532 }
533 }
534
535 pt::Expression::Negate(_, inner) => Self::from_expression(inner).map(Self::invert_int),
538
539 pt::Expression::Add(_, lhs, rhs) |
543 pt::Expression::Subtract(_, lhs, rhs) |
544 pt::Expression::Multiply(_, lhs, rhs) |
545 pt::Expression::Divide(_, lhs, rhs) => {
546 match (Self::ethabi(lhs, None), Self::ethabi(rhs, None)) {
547 (Some(DynSolType::Int(_)), Some(DynSolType::Int(_))) |
548 (Some(DynSolType::Int(_)), Some(DynSolType::Uint(_))) |
549 (Some(DynSolType::Uint(_)), Some(DynSolType::Int(_))) => {
550 Some(Self::Builtin(DynSolType::Int(256)))
551 }
552 _ => {
553 Some(Self::Builtin(DynSolType::Uint(256)))
554 }
555 }
556 }
557
558 pt::Expression::Modulo(_, _, _) |
560 pt::Expression::Power(_, _, _) |
561 pt::Expression::BitwiseOr(_, _, _) |
562 pt::Expression::BitwiseAnd(_, _, _) |
563 pt::Expression::BitwiseXor(_, _, _) |
564 pt::Expression::ShiftRight(_, _, _) |
565 pt::Expression::ShiftLeft(_, _, _) |
566 pt::Expression::NumberLiteral(_, _, _, _) => Some(Self::Builtin(DynSolType::Uint(256))),
567
568 pt::Expression::RationalNumberLiteral(_, _, _, _, _) => {
570 Some(Self::Builtin(DynSolType::Uint(256)))
571 }
572
573 pt::Expression::BoolLiteral(_, _) |
575 pt::Expression::And(_, _, _) |
576 pt::Expression::Or(_, _, _) |
577 pt::Expression::Equal(_, _, _) |
578 pt::Expression::NotEqual(_, _, _) |
579 pt::Expression::Less(_, _, _) |
580 pt::Expression::LessEqual(_, _, _) |
581 pt::Expression::More(_, _, _) |
582 pt::Expression::MoreEqual(_, _, _) |
583 pt::Expression::Not(_, _) => Some(Self::Builtin(DynSolType::Bool)),
584
585 pt::Expression::StringLiteral(_) => Some(Self::Builtin(DynSolType::String)),
587
588 pt::Expression::HexLiteral(_) => Some(Self::Builtin(DynSolType::Bytes)),
590
591 pt::Expression::FunctionCall(_, name, args) => {
593 Self::from_expression(name).map(|name| {
594 let args = args.iter().map(Self::from_expression).collect();
595 Self::Function(Box::new(name), args, vec![])
596 })
597 }
598 pt::Expression::NamedFunctionCall(_, name, args) => {
599 Self::from_expression(name).map(|name| {
600 let args = args.iter().map(|arg| Self::from_expression(&arg.expr)).collect();
601 Self::Function(Box::new(name), args, vec![])
602 })
603 }
604
605 pt::Expression::Delete(_, _) | pt::Expression::FunctionCallBlock(_, _, _) => None,
607 }
608 }
609
610 fn from_type(ty: &pt::Type) -> Option<Self> {
620 let ty = match ty {
621 pt::Type::Address | pt::Type::AddressPayable | pt::Type::Payable => {
622 Self::Builtin(DynSolType::Address)
623 }
624 pt::Type::Bool => Self::Builtin(DynSolType::Bool),
625 pt::Type::String => Self::Builtin(DynSolType::String),
626 pt::Type::Int(size) => Self::Builtin(DynSolType::Int(*size as usize)),
627 pt::Type::Uint(size) => Self::Builtin(DynSolType::Uint(*size as usize)),
628 pt::Type::Bytes(size) => Self::Builtin(DynSolType::FixedBytes(*size as usize)),
629 pt::Type::DynamicBytes => Self::Builtin(DynSolType::Bytes),
630 pt::Type::Mapping { value, .. } => Self::from_expression(value)?,
631 pt::Type::Function { params, returns, .. } => {
632 let params = map_parameters(params);
633 let returns = returns
634 .as_ref()
635 .map(|(returns, _)| map_parameters(returns))
636 .unwrap_or_default();
637 Self::Function(
638 Box::new(Self::Custom(vec!["__fn_type__".to_string()])),
639 params,
640 returns,
641 )
642 }
643 pt::Type::Rational => return None,
645 };
646 Some(ty)
647 }
648
649 fn map_special(self) -> Self {
653 if !matches!(self, Self::Function(_, _, _) | Self::Access(_, _) | Self::Custom(_)) {
654 return self;
655 }
656
657 let mut types = Vec::with_capacity(5);
658 let mut args = None;
659 self.recurse(&mut types, &mut args);
660
661 let len = types.len();
662 if len == 0 {
663 return self;
664 }
665
666 #[expect(clippy::single_match)]
668 #[allow(clippy::collapsible_match)]
669 match &self {
670 Self::Access(inner, access) => {
671 if let Some(ty) = inner.as_ref().clone().try_as_ethabi(None) {
672 let ty = Self::Builtin(ty);
674 match access.as_str() {
675 "length" if ty.is_dynamic() || ty.is_array() || ty.is_fixed_bytes() => {
676 return Self::Builtin(DynSolType::Uint(256));
677 }
678 "pop" if ty.is_dynamic_array() => return ty,
679 _ => {}
680 }
681 }
682 }
683 _ => {}
684 }
685
686 let this = {
687 let name = types.last().unwrap().as_str();
688 match len {
689 0 => unreachable!(),
690 1 => match name {
691 "gasleft" | "addmod" | "mulmod" => Some(DynSolType::Uint(256)),
692 "keccak256" | "sha256" | "blockhash" => Some(DynSolType::FixedBytes(32)),
693 "ripemd160" => Some(DynSolType::FixedBytes(20)),
694 "ecrecover" => Some(DynSolType::Address),
695 _ => None,
696 },
697 2 => {
698 let access = types.first().unwrap().as_str();
699 match name {
700 "block" => match access {
701 "coinbase" => Some(DynSolType::Address),
702 "timestamp" | "difficulty" | "prevrandao" | "number" | "gaslimit"
703 | "chainid" | "basefee" | "blobbasefee" => Some(DynSolType::Uint(256)),
704 _ => None,
705 },
706 "msg" => match access {
707 "sender" => Some(DynSolType::Address),
708 "gas" => Some(DynSolType::Uint(256)),
709 "value" => Some(DynSolType::Uint(256)),
710 "data" => Some(DynSolType::Bytes),
711 "sig" => Some(DynSolType::FixedBytes(4)),
712 _ => None,
713 },
714 "tx" => match access {
715 "origin" => Some(DynSolType::Address),
716 "gasprice" => Some(DynSolType::Uint(256)),
717 _ => None,
718 },
719 "abi" => match access {
720 "decode" => {
721 let mut args = args.unwrap();
725 let last = args.pop().unwrap();
726 match last {
727 Some(ty) => {
728 return match ty {
729 Self::Tuple(_) => ty,
730 ty => Self::Tuple(vec![Some(ty)]),
731 };
732 }
733 None => None,
734 }
735 }
736 s if s.starts_with("encode") => Some(DynSolType::Bytes),
737 _ => None,
738 },
739 "address" => match access {
740 "balance" => Some(DynSolType::Uint(256)),
741 "code" => Some(DynSolType::Bytes),
742 "codehash" => Some(DynSolType::FixedBytes(32)),
743 "send" => Some(DynSolType::Bool),
744 _ => None,
745 },
746 "type" => match access {
747 "name" => Some(DynSolType::String),
748 "creationCode" | "runtimeCode" => Some(DynSolType::Bytes),
749 "interfaceId" => Some(DynSolType::FixedBytes(4)),
750 "min" | "max" => Some(
751 (|| args?.pop()??.into_builtin())()
753 .unwrap_or(DynSolType::Uint(256)),
754 ),
755 _ => None,
756 },
757 "string" => match access {
758 "concat" => Some(DynSolType::String),
759 _ => None,
760 },
761 "bytes" => match access {
762 "concat" => Some(DynSolType::Bytes),
763 _ => None,
764 },
765 _ => None,
766 }
767 }
768 _ => None,
769 }
770 };
771
772 this.map(Self::Builtin).unwrap_or_else(|| match types.last().unwrap().as_str() {
773 "this" | "super" => Self::Custom(types),
774 _ => match self {
775 Self::Custom(_) | Self::Access(_, _) => Self::Custom(types),
776 Self::Function(_, _, _) => self,
777 _ => unreachable!(),
778 },
779 })
780 }
781
782 fn recurse(&self, types: &mut Vec<String>, args: &mut Option<Vec<Option<Self>>>) {
785 match self {
786 Self::Builtin(ty) => types.push(ty.to_string()),
787 Self::Custom(tys) => types.extend(tys.clone()),
788 Self::Access(expr, name) => {
789 types.push(name.clone());
790 expr.recurse(types, args);
791 }
792 Self::Function(fn_name, fn_args, _fn_ret) => {
793 if args.is_none() && !fn_args.is_empty() {
794 *args = Some(fn_args.clone());
795 }
796 fn_name.recurse(types, args);
797 }
798 _ => {}
799 }
800 }
801
802 fn infer_custom_type(
816 intermediate: &IntermediateOutput,
817 custom_type: &mut Vec<String>,
818 contract_name: Option<String>,
819 ) -> Result<Option<DynSolType>> {
820 if let Some("this") | Some("super") = custom_type.last().map(String::as_str) {
821 custom_type.pop();
822 }
823 if custom_type.is_empty() {
824 return Ok(None);
825 }
826
827 if let Some(contract_name) = contract_name {
830 let intermediate_contract = intermediate
831 .intermediate_contracts
832 .get(&contract_name)
833 .ok_or_else(|| eyre::eyre!("Could not find intermediate contract!"))?;
834
835 let cur_type = custom_type.last().unwrap();
836 if let Some(func) = intermediate_contract.function_definitions.get(cur_type) {
837 if let res @ Some(_) = func_members(func, custom_type) {
839 return Ok(res);
840 }
841
842 if func.returns.is_empty() {
845 eyre::bail!(
846 "This call expression does not return any values to inspect. Insert as statement."
847 )
848 }
849
850 let (_, param) = func.returns.first().unwrap();
852 let return_ty = ¶m.as_ref().unwrap().ty;
854
855 if let pt::Expression::Variable(ident) = return_ty {
859 custom_type.push(ident.name.clone());
860 return Self::infer_custom_type(intermediate, custom_type, Some(contract_name));
861 }
862
863 if let Some(pt::FunctionAttribute::Mutability(_mut)) = func
867 .attributes
868 .iter()
869 .find(|attr| matches!(attr, pt::FunctionAttribute::Mutability(_)))
870 {
871 if let pt::Mutability::Payable(_) = _mut {
872 eyre::bail!("This function mutates state. Insert as a statement.")
873 }
874 } else {
875 eyre::bail!("This function mutates state. Insert as a statement.")
876 }
877
878 Ok(Self::ethabi(return_ty, Some(intermediate)))
879 } else if let Some(var) = intermediate_contract.variable_definitions.get(cur_type) {
880 Self::infer_var_expr(&var.ty, Some(intermediate), custom_type)
881 } else if let Some(strukt) = intermediate_contract.struct_definitions.get(cur_type) {
882 let inner_types = strukt
883 .fields
884 .iter()
885 .map(|var| {
886 Self::ethabi(&var.ty, Some(intermediate))
887 .ok_or_else(|| eyre::eyre!("Struct `{cur_type}` has invalid fields"))
888 })
889 .collect::<Result<Vec<_>>>()?;
890 Ok(Some(DynSolType::Tuple(inner_types)))
891 } else {
892 eyre::bail!(
893 "Could not find any definition in contract \"{contract_name}\" for type: {custom_type:?}"
894 )
895 }
896 } else {
897 if let Ok(res) = Self::infer_custom_type(intermediate, custom_type, Some("REPL".into()))
900 {
901 return Ok(res);
902 }
903
904 let name = custom_type.last().unwrap();
907 let contract = intermediate.intermediate_contracts.get(name);
908 if contract.is_some() {
909 let contract_name = custom_type.pop();
910 return Self::infer_custom_type(intermediate, custom_type, contract_name);
911 }
912
913 let name = custom_type.last().unwrap();
915 if let Some(expr) = intermediate.repl_contract_expressions.get(name) {
916 return Self::infer_var_expr(expr, Some(intermediate), custom_type);
917 }
918
919 Ok(None)
922 }
923 }
924
925 fn infer_var_expr(
927 expr: &pt::Expression,
928 intermediate: Option<&IntermediateOutput>,
929 custom_type: &mut Vec<String>,
930 ) -> Result<Option<DynSolType>> {
931 let res = match &expr {
933 pt::Expression::Variable(ident) => {
935 let name = &ident.name;
936
937 if let Some(intermediate) = intermediate {
938 if let Some(expr) = intermediate.repl_contract_expressions.get(name) {
940 Self::infer_var_expr(expr, Some(intermediate), custom_type)
941 } else if intermediate.intermediate_contracts.contains_key(name) {
942 if custom_type.len() > 1 {
943 custom_type.pop();
945 Self::infer_custom_type(intermediate, custom_type, Some(name.clone()))
946 } else {
947 Ok(Some(DynSolType::Address))
949 }
950 } else {
951 Err(eyre::eyre!("Could not infer variable type"))
952 }
953 } else {
954 Ok(None)
955 }
956 }
957 ty => Ok(Self::ethabi(ty, intermediate)),
958 };
959 match res {
962 Ok(Some(ty)) => {
963 let box_ty = Box::new(Self::Builtin(ty.clone()));
964 let access = Self::Access(box_ty, custom_type.drain(..).next().unwrap_or_default());
965 if let Some(mapped) = access.map_special().try_as_ethabi(intermediate) {
966 Ok(Some(mapped))
967 } else {
968 Ok(Some(ty))
969 }
970 }
971 res => res,
972 }
973 }
974
975 fn try_as_ethabi(self, intermediate: Option<&IntermediateOutput>) -> Option<DynSolType> {
983 match self {
984 Self::Builtin(ty) => Some(ty),
985 Self::Tuple(types) => Some(DynSolType::Tuple(types_to_parameters(types, intermediate))),
986 Self::Array(inner) => match *inner {
987 ty @ Self::Custom(_) => ty.try_as_ethabi(intermediate),
988 _ => inner
989 .try_as_ethabi(intermediate)
990 .map(|inner| DynSolType::Array(Box::new(inner))),
991 },
992 Self::FixedArray(inner, size) => match *inner {
993 ty @ Self::Custom(_) => ty.try_as_ethabi(intermediate),
994 _ => inner
995 .try_as_ethabi(intermediate)
996 .map(|inner| DynSolType::FixedArray(Box::new(inner), size)),
997 },
998 ty @ Self::ArrayIndex(_, _) => ty.into_array_index(intermediate),
999 Self::Function(ty, _, _) => ty.try_as_ethabi(intermediate),
1000 Self::Access(_, _) => None,
1002 Self::Custom(mut types) => {
1003 intermediate.and_then(|intermediate| {
1005 Self::infer_custom_type(intermediate, &mut types, None).ok().flatten()
1006 })
1007 }
1008 }
1009 }
1010
1011 fn ethabi(
1013 expr: &pt::Expression,
1014 intermediate: Option<&IntermediateOutput>,
1015 ) -> Option<DynSolType> {
1016 Self::from_expression(expr)
1017 .map(Self::map_special)
1018 .and_then(|ty| ty.try_as_ethabi(intermediate))
1019 }
1020
1021 fn get_function_return_type<'a>(
1023 contract_expr: Option<&'a pt::Expression>,
1024 intermediate: &IntermediateOutput,
1025 ) -> Option<(&'a pt::Expression, DynSolType)> {
1026 let function_call = match contract_expr? {
1027 pt::Expression::FunctionCall(_, function_call, _) => function_call,
1028 _ => return None,
1029 };
1030 let (contract_name, function_name) = match function_call.as_ref() {
1031 pt::Expression::MemberAccess(_, contract_name, function_name) => {
1032 (contract_name, function_name)
1033 }
1034 _ => return None,
1035 };
1036 let contract_name = match contract_name.as_ref() {
1037 pt::Expression::Variable(contract_name) => contract_name.to_owned(),
1038 _ => return None,
1039 };
1040
1041 let pt::Expression::Variable(contract_name) =
1042 intermediate.repl_contract_expressions.get(&contract_name.name)?
1043 else {
1044 return None;
1045 };
1046
1047 let contract = intermediate
1048 .intermediate_contracts
1049 .get(&contract_name.name)?
1050 .function_definitions
1051 .get(&function_name.name)?;
1052 let return_parameter = contract.as_ref().returns.first()?.to_owned().1?;
1053 Self::ethabi(&return_parameter.ty, Some(intermediate)).map(|p| (contract_expr.unwrap(), p))
1054 }
1055
1056 fn invert_int(self) -> Self {
1058 match self {
1059 Self::Builtin(DynSolType::Uint(n)) => Self::Builtin(DynSolType::Int(n)),
1060 Self::Builtin(DynSolType::Int(n)) => Self::Builtin(DynSolType::Uint(n)),
1061 x => x,
1062 }
1063 }
1064
1065 #[inline]
1067 fn into_builtin(self) -> Option<DynSolType> {
1068 match self {
1069 Self::Builtin(ty) => Some(ty),
1070 _ => None,
1071 }
1072 }
1073
1074 fn into_array_index(self, intermediate: Option<&IntermediateOutput>) -> Option<DynSolType> {
1076 match self {
1077 Self::Array(inner) | Self::FixedArray(inner, _) | Self::ArrayIndex(inner, _) => {
1078 match inner.try_as_ethabi(intermediate) {
1079 Some(DynSolType::Array(inner)) | Some(DynSolType::FixedArray(inner, _)) => {
1080 Some(*inner)
1081 }
1082 Some(DynSolType::Bytes)
1083 | Some(DynSolType::String)
1084 | Some(DynSolType::FixedBytes(_)) => Some(DynSolType::FixedBytes(1)),
1085 ty => ty,
1086 }
1087 }
1088 _ => None,
1089 }
1090 }
1091
1092 #[inline]
1094 fn is_dynamic(&self) -> bool {
1095 match self {
1096 Self::Builtin(DynSolType::Bytes | DynSolType::String | DynSolType::Array(_)) => true,
1099 Self::Array(_) => true,
1100 _ => false,
1101 }
1102 }
1103
1104 #[inline]
1106 fn is_array(&self) -> bool {
1107 matches!(
1108 self,
1109 Self::Array(_)
1110 | Self::FixedArray(_, _)
1111 | Self::Builtin(DynSolType::Array(_))
1112 | Self::Builtin(DynSolType::FixedArray(_, _))
1113 )
1114 }
1115
1116 #[inline]
1118 fn is_dynamic_array(&self) -> bool {
1119 matches!(self, Self::Array(_) | Self::Builtin(DynSolType::Array(_)))
1120 }
1121
1122 fn is_fixed_bytes(&self) -> bool {
1123 matches!(self, Self::Builtin(DynSolType::FixedBytes(_)))
1124 }
1125}
1126
1127#[inline]
1131fn func_members(func: &pt::FunctionDefinition, custom_type: &[String]) -> Option<DynSolType> {
1132 if !matches!(func.ty, pt::FunctionTy::Function) {
1133 return None;
1134 }
1135
1136 let vis = func.attributes.iter().find_map(|attr| match attr {
1137 pt::FunctionAttribute::Visibility(vis) => Some(vis),
1138 _ => None,
1139 });
1140 match vis {
1141 Some(pt::Visibility::External(_)) | Some(pt::Visibility::Public(_)) => {
1142 match custom_type.first().unwrap().as_str() {
1143 "address" => Some(DynSolType::Address),
1144 "selector" => Some(DynSolType::FixedBytes(4)),
1145 _ => None,
1146 }
1147 }
1148 _ => None,
1149 }
1150}
1151
1152#[inline]
1154fn should_continue(expr: &pt::Expression) -> bool {
1155 match expr {
1156 pt::Expression::PreDecrement(_, _) | pt::Expression::PostDecrement(_, _) | pt::Expression::PreIncrement(_, _) | pt::Expression::PostIncrement(_, _) | pt::Expression::Assign(_, _, _) | pt::Expression::AssignAdd(_, _, _) | pt::Expression::AssignSubtract(_, _, _) | pt::Expression::AssignMultiply(_, _, _) | pt::Expression::AssignDivide(_, _, _) | pt::Expression::AssignModulo(_, _, _) | pt::Expression::AssignAnd(_, _, _) | pt::Expression::AssignOr(_, _, _) | pt::Expression::AssignXor(_, _, _) | pt::Expression::AssignShiftLeft(_, _, _) | pt::Expression::AssignShiftRight(_, _, _) => {
1173 true
1174 }
1175
1176 pt::Expression::FunctionCall(_, lhs, _) => {
1178 match lhs.as_ref() {
1179 pt::Expression::MemberAccess(_, _inner, access) => access.name == "pop",
1180 _ => false
1181 }
1182 }
1183
1184 _ => false
1185 }
1186}
1187
1188fn map_parameters(params: &[(pt::Loc, Option<pt::Parameter>)]) -> Vec<Option<Type>> {
1189 params
1190 .iter()
1191 .map(|(_, param)| param.as_ref().and_then(|param| Type::from_expression(¶m.ty)))
1192 .collect()
1193}
1194
1195fn types_to_parameters(
1196 types: Vec<Option<Type>>,
1197 intermediate: Option<&IntermediateOutput>,
1198) -> Vec<DynSolType> {
1199 types.into_iter().filter_map(|ty| ty.and_then(|ty| ty.try_as_ethabi(intermediate))).collect()
1200}
1201
1202fn parse_number_literal(expr: &pt::Expression) -> Option<U256> {
1203 match expr {
1204 pt::Expression::NumberLiteral(_, num, exp, unit) => {
1205 let num = num.parse::<U256>().unwrap_or(U256::ZERO);
1206 let exp = exp.parse().unwrap_or(0u32);
1207 if exp > 77 {
1208 None
1209 } else {
1210 let exp = U256::from(10usize.pow(exp));
1211 let unit_mul = unit_multiplier(unit).ok()?;
1212 Some(num * exp * unit_mul)
1213 }
1214 }
1215 pt::Expression::HexNumberLiteral(_, num, unit) => {
1216 let unit_mul = unit_multiplier(unit).ok()?;
1217 num.parse::<U256>().map(|num| num * unit_mul).ok()
1218 }
1219 pt::Expression::RationalNumberLiteral(..) => None,
1221 _ => None,
1222 }
1223}
1224
1225#[inline]
1226fn unit_multiplier(unit: &Option<pt::Identifier>) -> Result<U256> {
1227 if let Some(unit) = unit {
1228 let mul = match unit.name.as_str() {
1229 "seconds" => 1,
1230 "minutes" => 60,
1231 "hours" => 60 * 60,
1232 "days" => 60 * 60 * 24,
1233 "weeks" => 60 * 60 * 24 * 7,
1234 "wei" => 1,
1235 "gwei" => 10_usize.pow(9),
1236 "ether" => 10_usize.pow(18),
1237 other => eyre::bail!("unknown unit: {other}"),
1238 };
1239 Ok(U256::from(mul))
1240 } else {
1241 Ok(U256::from(1))
1242 }
1243}
1244
1245#[cfg(test)]
1246mod tests {
1247 use super::*;
1248 use foundry_compilers::{error::SolcError, solc::Solc};
1249 use std::sync::Mutex;
1250
1251 #[test]
1252 fn test_expressions() {
1253 static EXPRESSIONS: &[(&str, DynSolType)] = {
1254 use DynSolType::*;
1255 &[
1256 ("1 seconds", Uint(256)),
1259 ("1 minutes", Uint(256)),
1260 ("1 hours", Uint(256)),
1261 ("1 days", Uint(256)),
1262 ("1 weeks", Uint(256)),
1263 ("1 wei", Uint(256)),
1264 ("1 gwei", Uint(256)),
1265 ("1 ether", Uint(256)),
1266 ("-1 seconds", Int(256)),
1268 ("-1 minutes", Int(256)),
1269 ("-1 hours", Int(256)),
1270 ("-1 days", Int(256)),
1271 ("-1 weeks", Int(256)),
1272 ("-1 wei", Int(256)),
1273 ("-1 gwei", Int(256)),
1274 ("-1 ether", Int(256)),
1275 ("true ? 1 : 0", Uint(256)),
1277 ("true ? -1 : 0", Int(256)),
1278 ("1 + 1", Uint(256)),
1284 ("1 - 1", Uint(256)),
1285 ("1 * 1", Uint(256)),
1286 ("1 / 1", Uint(256)),
1287 ("1 % 1", Uint(256)),
1288 ("1 ** 1", Uint(256)),
1289 ("1 | 1", Uint(256)),
1290 ("1 & 1", Uint(256)),
1291 ("1 ^ 1", Uint(256)),
1292 ("1 >> 1", Uint(256)),
1293 ("1 << 1", Uint(256)),
1294 ("int(1) + 1", Int(256)),
1296 ("int(1) - 1", Int(256)),
1297 ("int(1) * 1", Int(256)),
1298 ("int(1) / 1", Int(256)),
1299 ("1 + int(1)", Int(256)),
1300 ("1 - int(1)", Int(256)),
1301 ("1 * int(1)", Int(256)),
1302 ("1 / int(1)", Int(256)),
1303 ("uint256 a; a--", Uint(256)),
1307 ("uint256 a; --a", Uint(256)),
1308 ("uint256 a; a++", Uint(256)),
1309 ("uint256 a; ++a", Uint(256)),
1310 ("uint256 a; a = 1", Uint(256)),
1311 ("uint256 a; a += 1", Uint(256)),
1312 ("uint256 a; a -= 1", Uint(256)),
1313 ("uint256 a; a *= 1", Uint(256)),
1314 ("uint256 a; a /= 1", Uint(256)),
1315 ("uint256 a; a %= 1", Uint(256)),
1316 ("uint256 a; a &= 1", Uint(256)),
1317 ("uint256 a; a |= 1", Uint(256)),
1318 ("uint256 a; a ^= 1", Uint(256)),
1319 ("uint256 a; a <<= 1", Uint(256)),
1320 ("uint256 a; a >>= 1", Uint(256)),
1321 ("true && true", Bool),
1325 ("true || true", Bool),
1326 ("true == true", Bool),
1327 ("true != true", Bool),
1328 ("true < true", Bool),
1329 ("true <= true", Bool),
1330 ("true > true", Bool),
1331 ("true >= true", Bool),
1332 ("!true", Bool),
1333 ]
1335 };
1336
1337 let source = &mut source();
1338
1339 let array_expressions: &[(&str, DynSolType)] = &[
1340 ("[1, 2, 3]", fixed_array(DynSolType::Uint(256), 3)),
1341 ("[uint8(1), 2, 3]", fixed_array(DynSolType::Uint(8), 3)),
1342 ("[int8(1), 2, 3]", fixed_array(DynSolType::Int(8), 3)),
1343 ("new uint256[](3)", array(DynSolType::Uint(256))),
1344 ("uint256[] memory a = new uint256[](3);\na[0]", DynSolType::Uint(256)),
1345 ("uint256[] memory a = new uint256[](3);\na[0:3]", array(DynSolType::Uint(256))),
1346 ];
1347 generic_type_test(source, array_expressions);
1348 generic_type_test(source, EXPRESSIONS);
1349 }
1350
1351 #[test]
1352 fn test_types() {
1353 static TYPES: &[(&str, DynSolType)] = {
1354 use DynSolType::*;
1355 &[
1356 ("bool", Bool),
1358 ("true", Bool),
1359 ("false", Bool),
1360 ("uint", Uint(256)),
1364 ("uint(1)", Uint(256)),
1365 ("1", Uint(256)),
1366 ("0x01", Uint(256)),
1367 ("int", Int(256)),
1368 ("int(1)", Int(256)),
1369 ("int(-1)", Int(256)),
1370 ("-1", Int(256)),
1371 ("-0x01", Int(256)),
1372 ("address", Address),
1376 ("address(0)", Address),
1377 ("0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990", Address),
1378 ("payable(0)", Address),
1379 ("payable(address(0))", Address),
1380 ("string", String),
1384 ("string(\"hello world\")", String),
1385 ("\"hello world\"", String),
1386 ("unicode\"hello world 😀\"", String),
1387 ("bytes", Bytes),
1391 ("bytes(\"hello world\")", Bytes),
1392 ("bytes(unicode\"hello world 😀\")", Bytes),
1393 ("hex\"68656c6c6f20776f726c64\"", Bytes),
1394 ]
1396 };
1397
1398 let mut types: Vec<(String, DynSolType)> = Vec::with_capacity(96 + 32 + 100);
1399 for (n, b) in (8..=256).step_by(8).zip(1..=32) {
1400 types.push((format!("uint{n}(0)"), DynSolType::Uint(n)));
1401 types.push((format!("int{n}(0)"), DynSolType::Int(n)));
1402 types.push((format!("bytes{b}(0x00)"), DynSolType::FixedBytes(b)));
1403 }
1404
1405 for n in 0..=32 {
1406 types.push((
1407 format!("uint256[{n}]"),
1408 DynSolType::FixedArray(Box::new(DynSolType::Uint(256)), n),
1409 ));
1410 }
1411
1412 generic_type_test(&mut source(), TYPES);
1413 generic_type_test(&mut source(), &types);
1414 }
1415
1416 #[test]
1417 fn test_global_vars() {
1418 init_tracing();
1419
1420 let global_variables = {
1422 use DynSolType::*;
1423 &[
1424 ("abi.decode(bytes, (uint8[13]))", Tuple(vec![FixedArray(Box::new(Uint(8)), 13)])),
1426 ("abi.decode(bytes, (address, bytes))", Tuple(vec![Address, Bytes])),
1427 ("abi.decode(bytes, (uint112, uint48))", Tuple(vec![Uint(112), Uint(48)])),
1428 ("abi.encode(_, _)", Bytes),
1429 ("abi.encodePacked(_, _)", Bytes),
1430 ("abi.encodeWithSelector(bytes4, _, _)", Bytes),
1431 ("abi.encodeCall(func(), (_, _))", Bytes),
1432 ("abi.encodeWithSignature(string, _, _)", Bytes),
1433 ("bytes.concat()", Bytes),
1437 ("bytes.concat(_)", Bytes),
1438 ("bytes.concat(_, _)", Bytes),
1439 ("string.concat()", String),
1440 ("string.concat(_)", String),
1441 ("string.concat(_, _)", String),
1442 ("block.basefee", Uint(256)),
1446 ("block.chainid", Uint(256)),
1447 ("block.coinbase", Address),
1448 ("block.difficulty", Uint(256)),
1449 ("block.gaslimit", Uint(256)),
1450 ("block.number", Uint(256)),
1451 ("block.timestamp", Uint(256)),
1452 ("gasleft()", Uint(256)),
1456 ("msg.data", Bytes),
1457 ("msg.sender", Address),
1458 ("msg.sig", FixedBytes(4)),
1459 ("msg.value", Uint(256)),
1460 ("tx.gasprice", Uint(256)),
1461 ("tx.origin", Address),
1462 ("blockhash(uint)", FixedBytes(32)),
1473 ("keccak256(bytes)", FixedBytes(32)),
1474 ("sha256(bytes)", FixedBytes(32)),
1475 ("ripemd160(bytes)", FixedBytes(20)),
1476 ("ecrecover(bytes32, uint8, bytes32, bytes32)", Address),
1477 ("addmod(uint, uint, uint)", Uint(256)),
1478 ("mulmod(uint, uint, uint)", Uint(256)),
1479 ("address(_)", Address),
1483 ("address(this)", Address),
1484 ("address.balance", Uint(256)),
1487 ("address.code", Bytes),
1488 ("address.codehash", FixedBytes(32)),
1489 ("address.send(uint256)", Bool),
1490 ("type(C).name", String),
1495 ("type(C).creationCode", Bytes),
1496 ("type(C).runtimeCode", Bytes),
1497 ("type(I).interfaceId", FixedBytes(4)),
1498 ("type(uint256).min", Uint(256)),
1499 ("type(int128).min", Int(128)),
1500 ("type(int256).min", Int(256)),
1501 ("type(uint256).max", Uint(256)),
1502 ("type(int128).max", Int(128)),
1503 ("type(int256).max", Int(256)),
1504 ("type(Enum1).min", Uint(256)),
1505 ("type(Enum1).max", Uint(256)),
1506 ("this.run.address", Address),
1508 ("this.run.selector", FixedBytes(4)),
1509 ]
1510 };
1511
1512 generic_type_test(&mut source(), global_variables);
1513 }
1514
1515 #[track_caller]
1516 fn source() -> SessionSource {
1517 static PRE_INSTALL_SOLC_LOCK: Mutex<bool> = Mutex::new(false);
1519
1520 let version = "0.8.20";
1523 for _ in 0..3 {
1524 let mut is_preinstalled = PRE_INSTALL_SOLC_LOCK.lock().unwrap();
1525 if !*is_preinstalled {
1526 let solc = Solc::find_or_install(&version.parse().unwrap())
1527 .map(|solc| (solc.version.clone(), solc));
1528 match solc {
1529 Ok((v, solc)) => {
1530 let _ = sh_println!("found installed Solc v{v} @ {}", solc.solc.display());
1532 break;
1533 }
1534 Err(e) => {
1535 let _ = sh_err!("error while trying to re-install Solc v{version}: {e}");
1537 let solc = Solc::blocking_install(&version.parse().unwrap());
1538 if solc.map_err(SolcError::from).is_ok() {
1539 *is_preinstalled = true;
1540 break;
1541 }
1542 }
1543 }
1544 }
1545 }
1546
1547 SessionSource::new(Default::default()).unwrap()
1548 }
1549
1550 fn array(ty: DynSolType) -> DynSolType {
1551 DynSolType::Array(Box::new(ty))
1552 }
1553
1554 fn fixed_array(ty: DynSolType, len: usize) -> DynSolType {
1555 DynSolType::FixedArray(Box::new(ty), len)
1556 }
1557
1558 fn parse(s: &mut SessionSource, input: &str, clear: bool) -> IntermediateOutput {
1559 if clear {
1560 s.clear();
1561 }
1562
1563 *s = s.clone_with_new_line("enum Enum1 { A }".into()).unwrap().0;
1564
1565 let input = format!("{};", input.trim_end().trim_end_matches(';'));
1566 let (mut _s, _) = s.clone_with_new_line(input).unwrap();
1567 *s = _s.clone();
1568 let s = &mut _s;
1569
1570 if let Err(e) = s.parse() {
1571 let source = s.to_repl_source();
1572 panic!("{e}\n\ncould not parse input:\n{source}")
1573 }
1574 s.generate_intermediate_output().expect("could not generate intermediate output")
1575 }
1576
1577 fn expr(stmts: &[pt::Statement]) -> pt::Expression {
1578 match stmts.last().expect("no statements") {
1579 pt::Statement::Expression(_, e) => e.clone(),
1580 s => panic!("Not an expression: {s:?}"),
1581 }
1582 }
1583
1584 fn get_type(
1585 s: &mut SessionSource,
1586 input: &str,
1587 clear: bool,
1588 ) -> (Option<Type>, IntermediateOutput) {
1589 let intermediate = parse(s, input, clear);
1590 let run_func_body = intermediate.run_func_body().expect("no run func body");
1591 let expr = expr(run_func_body);
1592 (Type::from_expression(&expr).map(Type::map_special), intermediate)
1593 }
1594
1595 fn get_type_ethabi(s: &mut SessionSource, input: &str, clear: bool) -> Option<DynSolType> {
1596 let (ty, intermediate) = get_type(s, input, clear);
1597 ty.and_then(|ty| ty.try_as_ethabi(Some(&intermediate)))
1598 }
1599
1600 fn generic_type_test<'a, T, I>(s: &mut SessionSource, input: I)
1601 where
1602 T: AsRef<str> + std::fmt::Display + 'a,
1603 I: IntoIterator<Item = &'a (T, DynSolType)> + 'a,
1604 {
1605 for (input, expected) in input.into_iter() {
1606 let input = input.as_ref();
1607 let ty = get_type_ethabi(s, input, true);
1608 assert_eq!(ty.as_ref(), Some(expected), "\n{input}");
1609 }
1610 }
1611
1612 fn init_tracing() {
1613 let _ = tracing_subscriber::FmtSubscriber::builder()
1614 .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
1615 .try_init();
1616 }
1617}