1use crate::prelude::{ChiselDispatcher, ChiselResult, ChiselRunner, SessionSource, SolidityHelper};
6use alloy_dyn_abi::{DynSolType, DynSolValue};
7use alloy_json_abi::EventParam;
8use alloy_primitives::{Address, B256, U256, hex};
9use eyre::{Result, WrapErr};
10use foundry_compilers::Artifact;
11use foundry_evm::{
12 backend::Backend,
13 core::evm::{BlockEnvFor, FoundryEvmNetwork, SpecFor, TxEnvFor},
14 decode::decode_console_logs,
15 executors::ExecutorBuilder,
16 inspectors::CheatsConfig,
17 traces::TraceRequirements,
18};
19use solar::{
20 ast::{ElementaryType, LitKind, StrKind, UnOpKind},
21 sema::{
22 hir::{Event, Expr, ExprKind, StmtKind},
23 ty::{Gcx, Ty, TyKind},
24 },
25};
26use std::ops::ControlFlow;
27use yansi::Paint;
28
29impl<FEN: FoundryEvmNetwork> SessionSource<FEN> {
31 pub async fn execute(&mut self) -> Result<ChiselResult> {
33 let output = self.build()?;
35
36 let (bytecode, final_pc) = output.enter(|output| -> Result<_> {
37 let contract = output
38 .repl_contract()
39 .ok_or_else(|| eyre::eyre!("failed to find REPL contract"))?;
40 trace!(?contract, "REPL contract");
41 let bytecode = contract
42 .get_bytecode_bytes()
43 .ok_or_else(|| eyre::eyre!("No bytecode found for `REPL` contract"))?;
44 Ok((bytecode.into_owned(), output.final_pc(contract)?))
45 })?;
46 let final_pc = final_pc.unwrap_or_default();
47 let mut runner = self.build_runner(final_pc).await?;
48 runner.run(bytecode)
49 }
50
51 pub async fn inspect(&self, input: &str) -> Result<(ControlFlow<()>, Option<String>)> {
63 let line = format!("bytes memory inspectoor = abi.encode({input});");
64 let mut source = match self.clone_with_new_line(line) {
65 Ok((source, _)) => source,
66 Err(err) => {
67 debug!(%err, "failed to build new source for inspection");
68 return Ok((ControlFlow::Continue(()), None));
69 }
70 };
71
72 let mut source_without_inspector = self.clone();
73
74 let (mut res, err) = match source.execute().await {
77 Ok(res) => (res, None),
78 Err(err) => {
79 debug!(?err, %input, "execution failed");
80 let should_execute = self
81 .clone_with_new_line(input.to_string())
82 .ok()
83 .and_then(|(source, do_execute)| {
84 if !do_execute {
85 return None;
86 }
87 source.build().ok().map(|output| {
88 output.enter(|output| {
89 let body = output.run_func_body();
90 let Some(last) = body.last() else { return false };
91 let StmtKind::Expr(expr) = last.kind else { return false };
92 should_continue(expr)
93 })
94 })
95 })
96 .unwrap_or(false);
97 if should_execute {
98 return Ok((ControlFlow::Continue(()), None));
99 }
100 match source_without_inspector.execute().await {
101 Ok(res) => (res, Some(err)),
102 Err(_) => {
103 if self.config.foundry_config.verbosity >= 3 {
104 sh_err!("Could not inspect: {err}")?;
105 }
106 return Ok((ControlFlow::Continue(()), None));
107 }
108 }
109 }
110 };
111
112 if let Some(err) = err {
114 let output = source_without_inspector.build()?;
115
116 let formatted_event = output.enter(|output| {
117 let gcx = output.gcx();
118 output.get_event(input).map(|eid| format_event_definition(gcx, gcx.hir.event(eid)))
119 });
120 if let Some(formatted_event) = formatted_event {
121 return Ok((ControlFlow::Break(()), Some(formatted_event?)));
122 }
123
124 if self.config.foundry_config.verbosity >= 3 {
126 sh_err!("Failed eval: {err}")?;
127 }
128
129 debug!(%err, %input, "failed abi encode input");
130 return Ok((ControlFlow::Break(()), None));
131 }
132 drop(source_without_inspector);
133
134 let Some((stack, memory)) = &res.state else {
135 if let Ok(decoder) = ChiselDispatcher::decode_traces(&source.config, &mut res).await {
137 ChiselDispatcher::<FEN>::show_traces(&decoder, &mut res).await?;
138 }
139 let decoded_logs = decode_console_logs(&res.logs);
140 if !decoded_logs.is_empty() {
141 sh_println!("{}", "Logs:".green())?;
142 for log in decoded_logs {
143 sh_println!(" {log}")?;
144 }
145 }
146
147 return Err(eyre::eyre!("Failed to inspect expression"));
148 };
149
150 let generated_output = source.build()?;
153
154 let res_ty = generated_output.enter(|out| -> Option<(bool, DynSolType)> {
157 let gcx = out.gcx();
158
159 let block = out.run_func_body();
162 let last = block.last()?;
163 let StmtKind::DeclSingle(vid) = last.kind else { return None };
164 let var = gcx.hir.variable(vid);
165 let init = var.initializer?;
166 let ExprKind::Call(_callee, args, _) = &init.kind else { return None };
167 let inner_expr = args.exprs().next()?;
168
169 let ty = expr_to_dyn(gcx, inner_expr)?;
170 Some((should_continue(inner_expr), ty))
171 });
172
173 let Some((cont, ty)) = res_ty else {
174 return Ok((ControlFlow::Continue(()), None));
175 };
176
177 let data = (|| -> Option<_> {
180 let mut offset: usize = stack.last()?.try_into().ok()?;
181 debug!("inspect memory @ {offset}: {}", hex::encode(memory));
182 let mem_offset = memory.get(offset..offset + 32)?;
183 let len: usize = U256::try_from_be_slice(mem_offset)?.try_into().ok()?;
184 offset += 32;
185 memory.get(offset..offset + len)
186 })();
187 let Some(data) = data else {
188 eyre::bail!("Failed to inspect last expression: could not retrieve data from memory");
189 };
190 let token = ty.abi_decode(data).wrap_err("Could not decode inspected values")?;
191 let c = if cont { ControlFlow::Continue(()) } else { ControlFlow::Break(()) };
192 Ok((c, Some(format_token(token))))
193 }
194
195 async fn build_runner(&mut self, final_pc: usize) -> Result<ChiselRunner<FEN>> {
196 let (evm_env, tx_env, fork_block) =
197 self.config.evm_opts.env::<SpecFor<FEN>, BlockEnvFor<FEN>, TxEnvFor<FEN>>().await?;
198
199 let backend = match self.config.backend.clone() {
200 Some(backend) => backend,
201 None => {
202 let fork = self.config.evm_opts.get_fork(
203 &self.config.foundry_config,
204 evm_env.cfg_env.chain_id,
205 fork_block,
206 );
207 let backend = Backend::spawn(fork)?;
208 self.config.backend = Some(backend.clone());
209 backend
210 }
211 };
212
213 let executor = ExecutorBuilder::default()
214 .inspectors(|stack| {
215 stack
216 .logs(self.config.foundry_config.live_logs)
217 .chisel_state(final_pc)
218 .trace_requirements(TraceRequirements::none().with_calls(true))
219 .cheatcodes(
220 CheatsConfig::new(
221 &self.config.foundry_config,
222 self.config.evm_opts.clone(),
223 None,
224 None,
225 None,
226 false,
227 )
228 .into(),
229 )
230 })
231 .gas_limit(self.config.evm_opts.gas_limit())
232 .spec_id(self.config.foundry_config.evm_spec_id::<SpecFor<FEN>>())
233 .legacy_assertions(self.config.foundry_config.legacy_assertions)
234 .build(evm_env, tx_env, backend);
235
236 Ok(ChiselRunner::new(executor, U256::MAX, Address::ZERO, self.config.calldata.clone()))
237 }
238}
239
240fn format_token(token: DynSolValue) -> String {
243 match token {
244 DynSolValue::Address(a) => {
245 format!("Type: {}\n└ Data: {}", "address".red(), a.cyan())
246 }
247 DynSolValue::FixedBytes(b, byte_len) => {
248 format!(
249 "Type: {}\n└ Data: {}",
250 format!("bytes{byte_len}").red(),
251 hex::encode_prefixed(b).cyan()
252 )
253 }
254 DynSolValue::Int(i, bit_len) => {
255 format!(
256 "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
257 format!("int{bit_len}").red(),
258 format!(
259 "0x{}",
260 format!("{i:x}")
261 .chars()
262 .skip(if i.is_negative() { 64 - bit_len / 4 } else { 0 })
263 .collect::<String>()
264 )
265 .cyan(),
266 hex::encode_prefixed(B256::from(i)).cyan(),
267 i.cyan()
268 )
269 }
270 DynSolValue::Uint(i, bit_len) => {
271 format!(
272 "Type: {}\n├ Hex: {}\n├ Hex (full word): {}\n└ Decimal: {}",
273 format!("uint{bit_len}").red(),
274 format!("0x{i:x}").cyan(),
275 hex::encode_prefixed(B256::from(i)).cyan(),
276 i.cyan()
277 )
278 }
279 DynSolValue::Bool(b) => {
280 format!("Type: {}\n└ Value: {}", "bool".red(), b.cyan())
281 }
282 DynSolValue::Bytes(bytes) => {
283 format!(
284 "Type: {}\n└ Data: {}",
285 "dynamic bytes".red(),
286 hex::encode_prefixed(bytes).cyan()
287 )
288 }
289 token @ DynSolValue::String(_) => {
290 let hex = hex::encode(token.abi_encode());
291 let s = token.as_str().expect("matched string value");
292 format!(
293 "Type: {}\n├ UTF-8: {}\n├ Hex (Memory):\n├─ Length ({}): {}\n├─ Contents ({}): {}\n├ Hex (Tuple Encoded):\n├─ Pointer ({}): {}\n├─ Length ({}): {}\n└─ Contents ({}): {}",
294 "string".red(),
295 s.cyan(),
296 "[0x00:0x20]".yellow(),
297 format!("0x{}", &hex[64..128]).cyan(),
298 "[0x20:..]".yellow(),
299 format!("0x{}", &hex[128..]).cyan(),
300 "[0x00:0x20]".yellow(),
301 format!("0x{}", &hex[..64]).cyan(),
302 "[0x20:0x40]".yellow(),
303 format!("0x{}", &hex[64..128]).cyan(),
304 "[0x40:..]".yellow(),
305 format!("0x{}", &hex[128..]).cyan(),
306 )
307 }
308 DynSolValue::FixedArray(tokens) | DynSolValue::Array(tokens) => {
309 let mut out = format!(
310 "{}({}) = {}",
311 "array".red(),
312 format!("{}", tokens.len()).yellow(),
313 '['.red()
314 );
315 for token in tokens {
316 out.push_str("\n ├ ");
317 out.push_str(&format_token(token).replace('\n', "\n "));
318 out.push('\n');
319 }
320 out.push_str(&']'.red().to_string());
321 out
322 }
323 DynSolValue::Tuple(tokens) => {
324 let displayed_types = tokens
325 .iter()
326 .map(|t| t.sol_type_name().unwrap_or_default())
327 .collect::<Vec<_>>()
328 .join(", ");
329 let mut out =
330 format!("{}({}) = {}", "tuple".red(), displayed_types.yellow(), '('.red());
331 for token in tokens {
332 out.push_str("\n ├ ");
333 out.push_str(&format_token(token).replace('\n', "\n "));
334 out.push('\n');
335 }
336 out.push_str(&')'.red().to_string());
337 out
338 }
339 _ => {
340 unimplemented!()
341 }
342 }
343}
344
345fn format_event_definition(gcx: Gcx<'_>, event: &Event<'_>) -> Result<String> {
348 let event_name = event.name.as_str().to_string();
349 let inputs = event
350 .parameters
351 .iter()
352 .map(|&pid| {
353 let var = gcx.hir.variable(pid);
354 let name =
355 var.name.map(|n| n.as_str().to_string()).unwrap_or_else(|| "<anonymous>".into());
356 let kind = solar_ty_to_dyn(gcx, gcx.type_of_item(pid.into()))
357 .ok_or_else(|| eyre::eyre!("Invalid type in event {event_name}"))?;
358 Ok(EventParam {
359 name,
360 ty: kind.to_string(),
361 components: vec![],
362 indexed: var.indexed,
363 internal_type: None,
364 })
365 })
366 .collect::<Result<Vec<_>>>()?;
367 let event = alloy_json_abi::Event { name: event_name, inputs, anonymous: event.anonymous };
368
369 Ok(format!(
370 "Type: {}\n├ Name: {}\n├ Signature: {:?}\n└ Selector: {:?}",
371 "event".red(),
372 SolidityHelper::new().highlight(&format!(
373 "{}({})",
374 event.name,
375 event
376 .inputs
377 .iter()
378 .map(|param| format!(
379 "{}{}{}",
380 param.ty,
381 if param.indexed { " indexed" } else { "" },
382 if param.name.is_empty() {
383 String::default()
384 } else {
385 format!(" {}", param.name)
386 },
387 ))
388 .collect::<Vec<_>>()
389 .join(", ")
390 )),
391 event.signature().cyan(),
392 event.selector().cyan(),
393 ))
394}
395
396fn expr_to_dyn(gcx: Gcx<'_>, expr: &Expr<'_>) -> Option<DynSolType> {
398 gcx.type_of_expr(expr.id).and_then(|ty| solar_expr_ty_to_dyn(gcx, ty, expr))
399}
400
401#[inline]
403fn should_continue(expr: &Expr<'_>) -> bool {
404 match &expr.kind {
405 ExprKind::Assign(_, _, _) => true,
407 ExprKind::Delete(_) => true,
409 ExprKind::Unary(op, _) => matches!(
411 op.kind,
412 UnOpKind::PreInc | UnOpKind::PreDec | UnOpKind::PostInc | UnOpKind::PostDec
413 ),
414 ExprKind::Call(callee, _, _) => match &callee.kind {
416 ExprKind::Member(_, ident) => ident.as_str() == "pop",
417 _ => false,
418 },
419 _ => false,
420 }
421}
422
423const fn elementary_to_dyn(et: ElementaryType) -> Option<DynSolType> {
425 Some(match et {
426 ElementaryType::Address(_) => DynSolType::Address,
427 ElementaryType::Bool => DynSolType::Bool,
428 ElementaryType::String => DynSolType::String,
429 ElementaryType::Bytes => DynSolType::Bytes,
430 ElementaryType::Int(size) => DynSolType::Int(size.bits() as usize),
431 ElementaryType::UInt(size) => DynSolType::Uint(size.bits() as usize),
432 ElementaryType::FixedBytes(size) => DynSolType::FixedBytes(size.bytes() as usize),
433 ElementaryType::Fixed(_, _) | ElementaryType::UFixed(_, _) => return None,
435 })
436}
437
438fn solar_expr_ty_to_dyn<'gcx>(gcx: Gcx<'gcx>, ty: Ty<'gcx>, expr: &Expr<'_>) -> Option<DynSolType> {
440 let expr = expr.peel_parens();
444 if matches!(expr.kind, ExprKind::Lit(lit) if matches!(lit.kind, LitKind::Str(StrKind::Hex, ..)))
445 {
446 return Some(DynSolType::Bytes);
447 }
448
449 solar_ty_to_dyn(gcx, ty)
450}
451
452fn solar_ty_to_dyn<'gcx>(gcx: Gcx<'gcx>, ty: Ty<'gcx>) -> Option<DynSolType> {
453 match ty.kind {
454 TyKind::Elementary(et) => elementary_to_dyn(et),
455 TyKind::Ref(inner, _) => solar_ty_to_dyn(gcx, inner),
456 TyKind::Array(elem, n) => {
457 let inner = solar_ty_to_dyn(gcx, elem)?;
458 let size: usize = n.try_into().ok()?;
459 Some(DynSolType::FixedArray(Box::new(inner), size))
460 }
461 TyKind::DynArray(elem) => {
462 let inner = solar_ty_to_dyn(gcx, elem)?;
463 Some(DynSolType::Array(Box::new(inner)))
464 }
465 TyKind::Slice(array) => solar_ty_to_dyn(gcx, array),
466 TyKind::Tuple(tys) => {
467 Some(DynSolType::Tuple(tys.iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect()))
468 }
469 TyKind::Mapping(_, _) => None,
470 TyKind::Struct(sid) => Some(DynSolType::Tuple(
471 gcx.struct_field_types(sid).iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect(),
472 )),
473 TyKind::Enum(_) => Some(DynSolType::Uint(8)),
474 TyKind::Udvt(inner, _) => solar_ty_to_dyn(gcx, inner),
475 TyKind::Contract(_) => Some(DynSolType::Address),
476 TyKind::Fn(f) => match f.returns.len() {
481 0 => None,
482 1 => solar_ty_to_dyn(gcx, f.returns[0]),
483 _ => Some(DynSolType::Tuple(
484 f.returns.iter().filter_map(|t| solar_ty_to_dyn(gcx, *t)).collect(),
485 )),
486 },
487 TyKind::Type(inner) => solar_ty_to_dyn(gcx, inner),
488 TyKind::Meta(inner) => solar_ty_to_dyn(gcx, inner),
489 TyKind::IntLiteral(neg, size, _) => {
490 let bits = (size.bits() as usize).max(8);
491 let bits = bits.div_ceil(8) * 8;
493 let bits = bits.min(256);
494 if neg {
495 Some(DynSolType::Int(bits.max(8)))
496 } else {
497 Some(DynSolType::Uint(bits.max(8)))
498 }
499 }
500 TyKind::StringLiteral(valid_utf8, _) => {
501 if valid_utf8 {
502 Some(DynSolType::String)
503 } else {
504 Some(DynSolType::Bytes)
505 }
506 }
507 TyKind::Module(_)
508 | TyKind::BuiltinModule(_)
509 | TyKind::Error(_, _)
510 | TyKind::Event(_, _)
511 | TyKind::Err(_) => None,
512 _ => None,
513 }
514}
515
516#[cfg(test)]
517mod tests {
518 use super::*;
519 use foundry_compilers::{error::SolcError, solc::Solc};
520 use foundry_evm::core::evm::EthEvmNetwork;
521 use solar::sema::Compiler;
522 use std::sync::Mutex;
523
524 type TestSessionSource = SessionSource<EthEvmNetwork>;
525
526 #[test]
527 fn test_expressions() {
528 static EXPRESSIONS: &[(&str, DynSolType)] = {
529 use DynSolType::*;
530 &[
531 ("1 seconds", Uint(8)),
534 ("1 minutes", Uint(8)),
535 ("1 hours", Uint(16)),
536 ("1 days", Uint(24)),
537 ("1 weeks", Uint(24)),
538 ("1 wei", Uint(8)),
539 ("1 gwei", Uint(32)),
540 ("1 ether", Uint(64)),
541 ("-1 seconds", Int(8)),
543 ("-1 minutes", Int(8)),
544 ("-1 hours", Int(16)),
545 ("-1 days", Int(24)),
546 ("-1 weeks", Int(24)),
547 ("-1 wei", Int(8)),
548 ("-1 gwei", Int(32)),
549 ("-1 ether", Int(64)),
550 ("true ? 1 : 0", Uint(8)),
552 ("1 + 1", Uint(8)),
558 ("1 - 1", Uint(8)),
559 ("1 * 1", Uint(8)),
560 ("1 / 1", Uint(8)),
561 ("1 % 1", Uint(8)),
562 ("1 ** 1", Uint(8)),
563 ("1 | 1", Uint(8)),
564 ("1 & 1", Uint(8)),
565 ("1 ^ 1", Uint(8)),
566 ("1 >> 1", Uint(8)),
567 ("1 << 1", Uint(8)),
568 ("int(1) + 1", Int(256)),
570 ("int(1) - 1", Int(256)),
571 ("int(1) * 1", Int(256)),
572 ("int(1) / 1", Int(256)),
573 ("1 + int(1)", Int(256)),
574 ("1 - int(1)", Int(256)),
575 ("1 * int(1)", Int(256)),
576 ("1 / int(1)", Int(256)),
577 ("uint256 a; a--", Uint(256)),
581 ("uint256 a; --a", Uint(256)),
582 ("uint256 a; a++", Uint(256)),
583 ("uint256 a; ++a", Uint(256)),
584 ("uint256 a; a = 1", Uint(256)),
585 ("uint256 a; a += 1", Uint(256)),
586 ("uint256 a; a -= 1", Uint(256)),
587 ("uint256 a; a *= 1", Uint(256)),
588 ("uint256 a; a /= 1", Uint(256)),
589 ("uint256 a; a %= 1", Uint(256)),
590 ("uint256 a; a &= 1", Uint(256)),
591 ("uint256 a; a |= 1", Uint(256)),
592 ("uint256 a; a ^= 1", Uint(256)),
593 ("uint256 a; a <<= 1", Uint(256)),
594 ("uint256 a; a >>= 1", Uint(256)),
595 ("true && true", Bool),
599 ("true || true", Bool),
600 ("true == true", Bool),
601 ("true != true", Bool),
602 ("!true", Bool),
603 ]
605 };
606
607 let source = &mut source();
608
609 let array_expressions: &[(&str, DynSolType)] = &[
610 ("[1, 2, 3]", fixed_array(DynSolType::Uint(8), 3)),
611 ("[uint8(1), 2, 3]", fixed_array(DynSolType::Uint(8), 3)),
612 ("[int8(1), 2, 3]", fixed_array(DynSolType::Int(8), 3)),
613 ("new uint256[](3)", array(DynSolType::Uint(256))),
614 ("uint256[] memory a = new uint256[](3);\na[0]", DynSolType::Uint(256)),
615 ];
616 generic_type_test(source, array_expressions);
617 generic_type_test(source, EXPRESSIONS);
618 }
619
620 #[test]
621 fn test_types() {
622 static TYPES: &[(&str, DynSolType)] = {
623 use DynSolType::*;
624 &[
625 ("bool", Bool),
627 ("true", Bool),
628 ("false", Bool),
629 ("uint", Uint(256)),
633 ("uint(1)", Uint(256)),
634 ("1", Uint(8)),
635 ("0x01", Uint(8)),
636 ("int", Int(256)),
637 ("int(1)", Int(256)),
638 ("int(-1)", Int(256)),
639 ("-1", Int(8)),
640 ("-0x01", Int(8)),
641 ("address", Address),
645 ("address(0)", Address),
646 ("0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990", Address),
647 ("payable(0)", Address),
648 ("payable(address(0))", Address),
649 ("string", String),
653 ("string(\"hello world\")", String),
654 ("\"hello world\"", String),
655 ("unicode\"hello world 😀\"", String),
656 ("bytes", Bytes),
660 ("bytes(\"hello world\")", Bytes),
661 ("bytes(unicode\"hello world 😀\")", Bytes),
662 ("hex\"68656c6c6f20776f726c64\"", Bytes),
663 ]
665 };
666
667 let mut types: Vec<(String, DynSolType)> = Vec::with_capacity(96 + 32 + 100);
668 for (n, b) in (8..=256).step_by(8).zip(1..=32) {
669 types.push((format!("uint{n}(0)"), DynSolType::Uint(n)));
670 types.push((format!("int{n}(0)"), DynSolType::Int(n)));
671 types.push((format!("bytes{b}(0x00)"), DynSolType::FixedBytes(b)));
672 }
673
674 for n in 1..=32 {
675 types.push((
676 format!("uint256[{n}]"),
677 DynSolType::FixedArray(Box::new(DynSolType::Uint(256)), n),
678 ));
679 }
680
681 generic_type_test(&mut source(), TYPES);
682 generic_type_test(&mut source(), &types);
683 }
684
685 #[test]
686 fn test_global_vars() {
687 init_tracing();
688
689 let global_variables = {
691 use DynSolType::*;
692 &[
693 ("abi.decode(bytes(\"\"), (uint8[13]))", FixedArray(Box::new(Uint(8)), 13)),
695 ("abi.decode(bytes(\"\"), (address, bytes))", Tuple(vec![Address, Bytes])),
696 ("abi.decode(bytes(\"\"), (uint112, uint48))", Tuple(vec![Uint(112), Uint(48)])),
697 ("abi.encode(1, 2)", Bytes),
698 ("abi.encodePacked(uint256(1), uint256(2))", Bytes),
699 ("abi.encodeWithSelector(bytes4(0), 1, 2)", Bytes),
700 ("abi.encodeWithSignature(\"f(uint256)\", 1)", Bytes),
701 ("bytes.concat()", Bytes),
705 ("bytes.concat(bytes(\"\"))", Bytes),
706 ("bytes.concat(bytes(\"\"), bytes(\"\"))", Bytes),
707 ("string.concat()", String),
708 ("string.concat(\"\")", String),
709 ("string.concat(\"\", \"\")", String),
710 ("block.basefee", Uint(256)),
714 ("block.chainid", Uint(256)),
715 ("block.coinbase", Address),
716 ("block.difficulty", Uint(256)),
717 ("block.gaslimit", Uint(256)),
718 ("block.number", Uint(256)),
719 ("block.timestamp", Uint(256)),
720 ("gasleft()", Uint(256)),
724 ("msg.data", Bytes),
725 ("msg.sender", Address),
726 ("msg.sig", FixedBytes(4)),
727 ("msg.value", Uint(256)),
728 ("tx.gasprice", Uint(256)),
729 ("tx.origin", Address),
730 ("blockhash(0)", FixedBytes(32)),
741 ("keccak256(bytes(\"\"))", FixedBytes(32)),
742 ("sha256(bytes(\"\"))", FixedBytes(32)),
743 ("ripemd160(bytes(\"\"))", FixedBytes(20)),
744 ("ecrecover(bytes32(0), 0, bytes32(0), bytes32(0))", Address),
745 ("addmod(1, 2, 3)", Uint(256)),
746 ("mulmod(1, 2, 3)", Uint(256)),
747 ("address(0)", Address),
751 ("address(this)", Address),
752 ("address(0).balance", Uint(256)),
755 ("address(0).code", Bytes),
756 ("address(0).codehash", FixedBytes(32)),
757 ("payable(address(0)).send(1)", Bool),
758 ("type(C).name", String),
763 ("type(C).creationCode", Bytes),
764 ("type(C).runtimeCode", Bytes),
765 ("type(I).interfaceId", FixedBytes(4)),
766 ("type(uint256).min", Uint(256)),
767 ("type(int128).min", Int(128)),
768 ("type(int256).min", Int(256)),
769 ("type(uint256).max", Uint(256)),
770 ("type(int128).max", Int(128)),
771 ("type(int256).max", Int(256)),
772 ("type(Enum1).min", Uint(8)),
773 ("type(Enum1).max", Uint(8)),
774 ("this.run.address", Address),
776 ("this.run.selector", FixedBytes(4)),
777 ]
778 };
779
780 generic_type_test(&mut source(), global_variables);
781 }
782
783 #[track_caller]
784 fn source() -> TestSessionSource {
785 static PRE_INSTALL_SOLC_LOCK: Mutex<bool> = Mutex::new(false);
787
788 let version = "0.8.20";
791 for _ in 0..3 {
792 let mut is_preinstalled = PRE_INSTALL_SOLC_LOCK.lock().unwrap();
793 if !*is_preinstalled {
794 let solc = Solc::find_or_install(&version.parse().unwrap())
795 .map(|solc| (solc.version.clone(), solc));
796 match solc {
797 Ok((v, solc)) => {
798 let _ = sh_println!("found installed Solc v{v} @ {}", solc.solc.display());
800 break;
801 }
802 Err(e) => {
803 let _ = sh_err!("error while trying to re-install Solc v{version}: {e}");
805 let solc = Solc::blocking_install(&version.parse().unwrap());
806 if solc.map_err(SolcError::from).is_ok() {
807 *is_preinstalled = true;
808 break;
809 }
810 }
811 }
812 }
813 }
814
815 SessionSource::new(Default::default()).unwrap()
816 }
817
818 fn array(ty: DynSolType) -> DynSolType {
819 DynSolType::Array(Box::new(ty))
820 }
821
822 fn fixed_array(ty: DynSolType, len: usize) -> DynSolType {
823 DynSolType::FixedArray(Box::new(ty), len)
824 }
825
826 fn get_type_ethabi(s: &mut TestSessionSource, input: &str, clear: bool) -> Option<DynSolType> {
833 if clear {
834 s.clear();
835 }
836
837 *s = s.clone_with_new_line("enum Enum1 { A }".into()).unwrap().0;
839 *s = s.clone_with_new_line("contract C {}".into()).unwrap().0;
840 *s = s.clone_with_new_line("interface I {}".into()).unwrap().0;
841
842 let input = format!("{};", input.trim_end().trim_end_matches(';'));
843 let (new_source, _) = s.clone_with_new_line(input).unwrap();
844 *s = new_source.clone();
845
846 let src = new_source.to_repl_source();
847 let mut opts = solar::interface::config::CompileOpts::default();
848 opts.unstable.typeck = true;
849 let sess = solar::interface::Session::builder()
850 .opts(opts)
851 .with_buffer_emitter(Default::default())
852 .build();
853 let mut compiler = Compiler::new(sess);
854
855 compiler.enter_mut(|c| -> Option<DynSolType> {
856 let analyzed = {
858 let mut pcx = c.parse();
859 let file = c
860 .sess()
861 .source_map()
862 .new_source_file(
863 std::path::PathBuf::from(new_source.file_name.clone()),
864 src.clone(),
865 )
866 .ok()?;
867 pcx.add_file(file);
868 pcx.parse();
869 matches!(c.lower_asts(), Ok(ControlFlow::Continue(())))
870 && matches!(c.analysis(), Ok(ControlFlow::Continue(())))
871 };
872 if !analyzed {
873 return None;
874 }
875
876 let gcx = c.gcx();
878 let hir = &gcx.hir;
879 let repl = hir.contracts().find(|c| c.name.as_str() == "REPL")?;
880 let run_fid = repl
881 .functions()
882 .find(|&f| hir.function(f).name.as_ref().map(|n| n.as_str()) == Some("run"))?;
883 let body = hir.function(run_fid).body?;
884 let last = body.last()?;
885 let expr = match last.kind {
886 StmtKind::Expr(e) => e,
887 _ => return None,
888 };
889 expr_to_dyn(gcx, expr)
890 })
891 }
892
893 fn generic_type_test<'a, T, I>(s: &mut TestSessionSource, input: I)
894 where
895 T: AsRef<str> + std::fmt::Display + 'a,
896 I: IntoIterator<Item = &'a (T, DynSolType)> + 'a,
897 {
898 let mut failures = Vec::new();
899 for (input, expected) in input {
900 let input = input.as_ref();
901 let ty = get_type_ethabi(s, input, true);
902 if ty.as_ref() != Some(expected) {
903 failures.push(format!("{input}: got {ty:?}, expected {expected:?}"));
904 }
905 }
906 assert!(failures.is_empty(), "\n{}", failures.join("\n"));
907 }
908
909 fn init_tracing() {
910 let _ = tracing_subscriber::FmtSubscriber::builder()
911 .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
912 .try_init();
913 }
914}