From 6a85dbaa62f1c305f31cab37781232913055ae28 Mon Sep 17 00:00:00 2001 From: Arsenii Kulikov Date: Thu, 13 Jun 2024 22:24:54 +0300 Subject: [PATCH] feat: display source name in debugger (#8154) * feat: display source name in debugger * fmt * clippy * refactor --- Cargo.lock | 1 - crates/common/Cargo.toml | 1 - crates/common/src/compile.rs | 38 +++++++------------ crates/debugger/src/tui/draw.rs | 30 +++++++++------ crates/evm/traces/Cargo.toml | 1 + crates/evm/traces/src/identifier/etherscan.rs | 23 +++++++---- crates/forge/bin/cmd/test/mod.rs | 3 +- crates/script/src/build.rs | 3 +- 8 files changed, 52 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dc6ee9638123..206103b789f0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3594,7 +3594,6 @@ dependencies = [ "serde", "serde_json", "similar-asserts", - "tempfile", "thiserror", "tokio", "tower", diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 56f430c4ab32..907f3b62579c 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -47,7 +47,6 @@ reqwest.workspace = true semver = "1" serde_json.workspace = true serde.workspace = true -tempfile.workspace = true thiserror = "1" tokio.workspace = true tracing.workspace = true diff --git a/crates/common/src/compile.rs b/crates/common/src/compile.rs index d26b362a9adb..3488e6482a00 100644 --- a/crates/common/src/compile.rs +++ b/crates/common/src/compile.rs @@ -275,6 +275,7 @@ impl ProjectCompiler { pub struct SourceData { pub source: Arc, pub language: MultiCompilerLanguage, + pub name: String, } #[derive(Clone, Debug)] @@ -297,11 +298,12 @@ impl ContractSources { /// Collects the contract sources and artifacts from the project compile output. pub fn from_project_output( output: &ProjectCompileOutput, - link_data: Option<(&Path, &Libraries)>, + root: impl AsRef, + libraries: Option<&Libraries>, ) -> Result { let mut sources = Self::default(); - sources.insert(output, link_data)?; + sources.insert(output, root, libraries)?; Ok(sources) } @@ -309,12 +311,14 @@ impl ContractSources { pub fn insert( &mut self, output: &ProjectCompileOutput, - link_data: Option<(&Path, &Libraries)>, + root: impl AsRef, + libraries: Option<&Libraries>, ) -> Result<()> where C::Language: Into, { - let link_data = link_data.map(|(root, libraries)| { + let root = root.as_ref(); + let link_data = libraries.map(|libraries| { let linker = Linker::new(root, output.artifact_ids().collect()); (linker, libraries) }); @@ -355,7 +359,11 @@ impl ContractSources { self.sources_by_id.entry(build_id.clone()).or_default().insert( *source_id, - SourceData { source: source_code, language: build.language.into() }, + SourceData { + source: source_code, + language: build.language.into(), + name: path.strip_prefix(root).unwrap_or(path).to_string_lossy().to_string(), + }, ); } } @@ -496,26 +504,6 @@ pub fn compile_target( ProjectCompiler::new().quiet(quiet).files([target_path.into()]).compile(project) } -/// Compiles an Etherscan source from metadata by creating a project. -/// Returns the artifact_id, the file_id, and the bytecode -pub async fn compile_from_source( - metadata: &Metadata, -) -> Result> { - let root = tempfile::tempdir()?; - let root_path = root.path(); - let project = etherscan_project(metadata, root_path)?; - - let project_output = project.compile()?; - - if project_output.has_compiler_errors() { - eyre::bail!("{project_output}") - } - - root.close()?; - - Ok(project_output) -} - /// Creates a [Project] from an Etherscan source. pub fn etherscan_project( metadata: &Metadata, diff --git a/crates/debugger/src/tui/draw.rs b/crates/debugger/src/tui/draw.rs index aa6ac13cf2d1..06056034cd05 100644 --- a/crates/debugger/src/tui/draw.rs +++ b/crates/debugger/src/tui/draw.rs @@ -191,8 +191,8 @@ impl DebuggerContext<'_> { } fn draw_src(&self, f: &mut Frame<'_>, area: Rect) { - let text_output = self.src_text(area); - let title = match self.call_kind() { + let (text_output, source_name) = self.src_text(area); + let call_kind_text = match self.call_kind() { CallKind::Create | CallKind::Create2 => "Contract creation", CallKind::Call => "Contract call", CallKind::StaticCall => "Contract staticcall", @@ -200,15 +200,20 @@ impl DebuggerContext<'_> { CallKind::DelegateCall => "Contract delegatecall", CallKind::AuthCall => "Contract authcall", }; + let title = format!( + "{} {} ", + call_kind_text, + source_name.map(|s| format!("| {s}")).unwrap_or_default() + ); let block = Block::default().title(title).borders(Borders::ALL); let paragraph = Paragraph::new(text_output).block(block).wrap(Wrap { trim: false }); f.render_widget(paragraph, area); } - fn src_text(&self, area: Rect) -> Text<'_> { - let (source_element, source_code) = match self.src_map() { + fn src_text(&self, area: Rect) -> (Text<'_>, Option<&str>) { + let (source_element, source_code, source_file) = match self.src_map() { Ok(r) => r, - Err(e) => return Text::from(e), + Err(e) => return (Text::from(e), None), }; // We are handed a vector of SourceElements that give us a span of sourcecode that is @@ -330,10 +335,11 @@ impl DebuggerContext<'_> { } } - Text::from(lines.lines) + (Text::from(lines.lines), Some(source_file)) } - fn src_map(&self) -> Result<(SourceElement, &str), String> { + /// Returns source map, source code and source name of the current line. + fn src_map(&self) -> Result<(SourceElement, &str, &str), String> { let address = self.address(); let Some(contract_name) = self.debugger.identified_contracts.get(address) else { return Err(format!("Unknown contract at address {address}")); @@ -351,7 +357,7 @@ impl DebuggerContext<'_> { let is_create = matches!(self.call_kind(), CallKind::Create | CallKind::Create2); let pc = self.current_step().pc; - let Some((source_element, source_code)) = + let Some((source_element, source_code, source_file)) = files_source_code.find_map(|(artifact, source)| { let bytecode = if is_create { &artifact.bytecode.bytecode @@ -376,7 +382,7 @@ impl DebuggerContext<'_> { // if index matches current file_id, return current source code .and_then(|index| { (index == artifact.file_id) - .then(|| (source_element.clone(), source.source.as_str())) + .then(|| (source_element.clone(), source.source.as_str(), &source.name)) }) .or_else(|| { // otherwise find the source code for the element's index @@ -385,7 +391,9 @@ impl DebuggerContext<'_> { .sources_by_id .get(&artifact.build_id)? .get(&source_element.index()?) - .map(|source| (source_element.clone(), source.source.as_str())) + .map(|source| { + (source_element.clone(), source.source.as_str(), &source.name) + }) }); res @@ -394,7 +402,7 @@ impl DebuggerContext<'_> { return Err(format!("No source map for contract {contract_name}")); }; - Ok((source_element, source_code)) + Ok((source_element, source_code, source_file)) } fn draw_op_list(&self, f: &mut Frame<'_>, area: Rect) { diff --git a/crates/evm/traces/Cargo.toml b/crates/evm/traces/Cargo.toml index d6b13e3ccf85..8c2ccb3ae971 100644 --- a/crates/evm/traces/Cargo.toml +++ b/crates/evm/traces/Cargo.toml @@ -41,6 +41,7 @@ tokio = { workspace = true, features = ["time", "macros"] } tracing = "0.1" yansi.workspace = true rustc-hash.workspace = true +tempfile.workspace = true [dev-dependencies] tempfile.workspace = true diff --git a/crates/evm/traces/src/identifier/etherscan.rs b/crates/evm/traces/src/identifier/etherscan.rs index 794b3a9edd11..87d7e9c92d83 100644 --- a/crates/evm/traces/src/identifier/etherscan.rs +++ b/crates/evm/traces/src/identifier/etherscan.rs @@ -4,13 +4,12 @@ use foundry_block_explorers::{ contract::{ContractMetadata, Metadata}, errors::EtherscanError, }; -use foundry_common::compile::{self, ContractSources}; +use foundry_common::compile::{etherscan_project, ContractSources}; use foundry_config::{Chain, Config}; use futures::{ future::{join_all, Future}, stream::{FuturesUnordered, Stream, StreamExt}, task::{Context, Poll}, - TryFutureExt, }; use std::{ borrow::Cow, @@ -64,11 +63,18 @@ impl EtherscanIdentifier { .iter() // filter out vyper files .filter(|(_, metadata)| !metadata.is_vyper()) - .map(|(address, metadata)| { + .map(|(address, metadata)| async move { println!("Compiling: {} {address}", metadata.contract_name); - let err_msg = - format!("Failed to compile contract {} from {address}", metadata.contract_name); - compile::compile_from_source(metadata).map_err(move |err| err.wrap_err(err_msg)) + let root = tempfile::tempdir()?; + let root_path = root.path(); + let project = etherscan_project(metadata, root_path)?; + let output = project.compile()?; + + if output.has_compiler_errors() { + eyre::bail!("{output}") + } + + Ok((project, output, root)) }) .collect::>(); @@ -78,8 +84,9 @@ impl EtherscanIdentifier { let mut sources: ContractSources = Default::default(); // construct the map - for output in outputs { - sources.insert(&output?, None)?; + for res in outputs { + let (project, output, _) = res?; + sources.insert(&output, project.root(), None)?; } Ok(sources) diff --git a/crates/forge/bin/cmd/test/mod.rs b/crates/forge/bin/cmd/test/mod.rs index c27e2053d6f1..6f65dcfec5b4 100644 --- a/crates/forge/bin/cmd/test/mod.rs +++ b/crates/forge/bin/cmd/test/mod.rs @@ -337,7 +337,8 @@ impl TestArgs { let sources = ContractSources::from_project_output( output_clone.as_ref().unwrap(), - Some((project.root(), &libraries)), + project.root(), + Some(&libraries), )?; // Run the debugger. diff --git a/crates/script/src/build.rs b/crates/script/src/build.rs index f1a18d45fa8b..2fc0691d742f 100644 --- a/crates/script/src/build.rs +++ b/crates/script/src/build.rs @@ -136,7 +136,8 @@ impl LinkedBuildData { ) -> Result { let sources = ContractSources::from_project_output( &build_data.output, - Some((&build_data.project_root, &libraries)), + &build_data.project_root, + Some(&libraries), )?; let known_contracts =