Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: optimize compilation by reading AST #7599

Merged
merged 12 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ foundry-linking = { path = "crates/linking" }

# solc & compilation utilities
foundry-block-explorers = { version = "0.2.6", default-features = false }
foundry-compilers = { version = "0.3.14", default-features = false }
foundry-compilers = { version = "0.3.17", default-features = false }

## revm
# no default features to avoid c-kzg
Expand Down
24 changes: 14 additions & 10 deletions crates/cli/src/utils/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use foundry_common::{cli_warn, fs, TestFunctionExt};
use foundry_compilers::{
artifacts::{CompactBytecode, CompactDeployedBytecode},
cache::{CacheEntry, SolFilesCache},
info::ContractInfo,
utils::read_json_file,
Artifact, ProjectCompileOutput,
};
Expand All @@ -20,24 +19,29 @@ use foundry_evm::{
render_trace_arena, CallTraceDecoder, CallTraceDecoderBuilder, TraceKind, Traces,
},
};
use std::{fmt::Write, path::PathBuf, str::FromStr};
use std::{
fmt::Write,
path::{Path, PathBuf},
str::FromStr,
};
use yansi::Paint;

/// Given a `Project`'s output, removes the matching ABI, Bytecode and
/// Runtime Bytecode of the given contract.
#[track_caller]
pub fn remove_contract(
output: &mut ProjectCompileOutput,
info: &ContractInfo,
path: &Path,
name: &str,
) -> Result<(JsonAbi, CompactBytecode, CompactDeployedBytecode)> {
let contract = if let Some(contract) = output.remove_contract(info) {
let contract = if let Some(contract) = output.remove(path.to_string_lossy(), name) {
contract
} else {
let mut err = format!("could not find artifact: `{}`", info.name);
let mut err = format!("could not find artifact: `{}`", name);
if let Some(suggestion) =
super::did_you_mean(&info.name, output.artifacts().map(|(name, _)| name)).pop()
super::did_you_mean(name, output.artifacts().map(|(name, _)| name)).pop()
{
if suggestion != info.name {
if suggestion != name {
err = format!(
r#"{err}

Expand All @@ -50,17 +54,17 @@ pub fn remove_contract(

let abi = contract
.get_abi()
.ok_or_else(|| eyre::eyre!("contract {} does not contain abi", info))?
.ok_or_else(|| eyre::eyre!("contract {} does not contain abi", name))?
.into_owned();

let bin = contract
.get_bytecode()
.ok_or_else(|| eyre::eyre!("contract {} does not contain bytecode", info))?
.ok_or_else(|| eyre::eyre!("contract {} does not contain bytecode", name))?
.into_owned();

let runtime = contract
.get_deployed_bytecode()
.ok_or_else(|| eyre::eyre!("contract {} does not contain deployed bytecode", info))?
.ok_or_else(|| eyre::eyre!("contract {} does not contain deployed bytecode", name))?
.into_owned();

Ok((abi, bin, runtime))
Expand Down
23 changes: 4 additions & 19 deletions crates/common/src/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use foundry_compilers::{
artifacts::{BytecodeObject, ContractBytecodeSome, Libraries},
remappings::Remapping,
report::{BasicStdoutReporter, NoReporter, Report},
Artifact, ArtifactId, FileFilter, Graph, Project, ProjectCompileOutput, ProjectPathsConfig,
Solc, SolcConfig,
Artifact, ArtifactId, FileFilter, Project, ProjectCompileOutput, ProjectPathsConfig, Solc,
SolcConfig,
};
use foundry_linking::Linker;
use num_format::{Locale, ToFormattedString};
Expand Down Expand Up @@ -470,27 +470,12 @@ pub struct ContractInfo {
/// If `verify` and it's a standalone script, throw error. Only allowed for projects.
///
/// **Note:** this expects the `target_path` to be absolute
pub fn compile_target_with_filter(
pub fn compile_target(
target_path: &Path,
project: &Project,
quiet: bool,
verify: bool,
skip: Vec<SkipBuildFilter>,
) -> Result<ProjectCompileOutput> {
let graph = Graph::resolve(&project.paths)?;

// Checking if it's a standalone script, or part of a project.
let mut compiler = ProjectCompiler::new().quiet(quiet);
if !skip.is_empty() {
compiler = compiler.filter(Box::new(SkipBuildFilters::new(skip)?));
}
if !graph.files().contains_key(target_path) {
if verify {
eyre::bail!("You can only verify deployments from inside a project! Make sure it exists with `forge tree`.");
}
compiler = compiler.files([target_path.into()]);
}
compiler.compile(project)
ProjectCompiler::new().quiet(quiet).files([target_path.into()]).compile(project)
}

/// Compiles an Etherscan source from metadata by creating a project.
Expand Down
1 change: 1 addition & 0 deletions crates/config/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4553,6 +4553,7 @@ mod tests {
stack_allocation: None,
optimizer_steps: Some("dhfoDgvulfnTUtnIf".to_string()),
}),
simple_counter_for_loop_unchecked_increment: None,
}),
..Default::default()
};
Expand Down
22 changes: 13 additions & 9 deletions crates/forge/bin/cmd/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ use foundry_cli::{
utils::{self, read_constructor_args_file, remove_contract, LoadConfig},
};
use foundry_common::{
compile::ProjectCompiler, fmt::parse_tokens, provider::alloy::estimate_eip1559_fees,
compile::{self},
fmt::parse_tokens,
provider::alloy::estimate_eip1559_fees,
};
use foundry_compilers::{artifacts::BytecodeObject, info::ContractInfo, utils::canonicalized};
use foundry_compilers::{artifacts::BytecodeObject, info::ContractInfo, utils::canonicalize};
use serde_json::json;
use std::{borrow::Borrow, marker::PhantomData, path::PathBuf, sync::Arc};

Expand Down Expand Up @@ -84,15 +86,17 @@ impl CreateArgs {
pub async fn run(mut self) -> Result<()> {
// Find Project & Compile
let project = self.opts.project()?;
let mut output =
ProjectCompiler::new().quiet_if(self.json || self.opts.silent).compile(&project)?;

if let Some(ref mut path) = self.contract.path {
// paths are absolute in the project's output
*path = canonicalized(project.root().join(&path)).to_string_lossy().to_string();
}
let target_path = if let Some(ref mut path) = self.contract.path {
canonicalize(project.root().join(path))?
} else {
project.find_contract_path(&self.contract.name)?
};

let mut output =
compile::compile_target(&target_path, &project, self.json || self.opts.silent)?;

let (abi, bin, _) = remove_contract(&mut output, &self.contract)?;
let (abi, bin, _) = remove_contract(&mut output, &target_path, &self.contract.name)?;

let bin = match bin.object {
BytecodeObject::Bytecode(_) => bin.object,
Expand Down
4 changes: 2 additions & 2 deletions crates/forge/bin/cmd/flatten.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use foundry_cli::{
opts::{CoreBuildArgs, ProjectPathsArgs},
utils::LoadConfig,
};
use foundry_common::{compile::ProjectCompiler, fs};
use foundry_common::{compile::compile_target, fs};
use foundry_compilers::{error::SolcError, flatten::Flattener};
use std::path::PathBuf;

Expand Down Expand Up @@ -42,7 +42,7 @@ impl FlattenArgs {
let project = config.ephemeral_no_artifacts_project()?;

let target_path = dunce::canonicalize(target_path)?;
let compiler_output = ProjectCompiler::new().files([target_path.clone()]).compile(&project);
let compiler_output = compile_target(&target_path, &project, false);

let flattened = match compiler_output {
Ok(compiler_output) => {
Expand Down
9 changes: 7 additions & 2 deletions crates/forge/bin/cmd/selectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use foundry_cli::{
utils::FoundryPathExt,
};
use foundry_common::{
compile::ProjectCompiler,
compile::{compile_target, ProjectCompiler},
selectors::{import_selectors, SelectorImportData},
};
use foundry_compilers::{artifacts::output_selection::ContractOutputSelection, info::ContractInfo};
Expand Down Expand Up @@ -71,7 +71,12 @@ impl SelectorsSubcommands {
};

let project = build_args.project()?;
let output = ProjectCompiler::new().quiet(true).compile(&project)?;
let output = if let Some(name) = &contract {
let target_path = project.find_contract_path(name)?;
compile_target(&target_path, &project, false)?
} else {
ProjectCompiler::new().compile(&project)?
};
let artifacts = if all {
output
.into_artifacts_with_files()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Compiling 27 files with 0.8.23
Compiling 1 files with 0.8.23
Solc 0.8.23 finished in 2.27s
Compiler run successful!
Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Compiling 27 files with 0.8.23
Compiling 1 files with 0.8.23
Solc 0.8.23 finished in 1.95s
Compiler run successful!
Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Compiling 28 files with 0.8.23
Compiling 1 files with 0.8.23
Solc 0.8.23 finished in 2.82s
Compiler run successful!
Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Expand Down
43 changes: 6 additions & 37 deletions crates/script/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,15 @@ use crate::{

use alloy_primitives::{Address, Bytes};
use alloy_provider::Provider;
use eyre::{Context, OptionExt, Result};
use eyre::{OptionExt, Result};
use foundry_cheatcodes::ScriptWallets;
use foundry_cli::utils::get_cached_entry_by_name;
use foundry_common::{
compile::{self, ContractSources, ProjectCompiler},
compile::{self, ContractSources},
provider::alloy::try_get_http_provider,
ContractData, ContractsByArtifact,
};
use foundry_compilers::{
artifacts::{BytecodeObject, Libraries},
cache::SolFilesCache,
info::ContractInfo,
ArtifactId, ProjectCompileOutput,
};
Expand Down Expand Up @@ -149,54 +147,25 @@ impl PreprocessedState {
pub fn compile(self) -> Result<CompiledState> {
let Self { args, script_config, script_wallets } = self;
let project = script_config.config.project()?;
let filters = args.skip.clone().unwrap_or_default();

let mut target_name = args.target_contract.clone();

// If we've received correct path, use it as target_path
// Otherwise, parse input as <path>:<name> and use the path from the contract info, if
// present.
let target_path = if let Ok(path) = dunce::canonicalize(&args.path) {
Some(path)
path
} else {
let contract = ContractInfo::from_str(&args.path)?;
target_name = Some(contract.name.clone());
if let Some(path) = contract.path {
Some(dunce::canonicalize(path)?)
dunce::canonicalize(path)?
} else {
None
project.find_contract_path(contract.name.as_str())?
}
};

// If we've found target path above, only compile it.
// Otherwise, compile everything to match contract by name later.
let output = if let Some(target_path) = target_path.clone() {
compile::compile_target_with_filter(
&target_path,
&project,
args.opts.silent,
args.verify,
filters,
)
} else if !project.paths.has_input_files() {
Err(eyre::eyre!("The project doesn't have any input files. Make sure the `script` directory is configured properly in foundry.toml. Otherwise, provide the path to the file."))
} else {
ProjectCompiler::new().compile(&project)
}?;

// If we still don't have target path, find it by name in the compilation cache.
let target_path = if let Some(target_path) = target_path {
target_path
} else {
let target_name = target_name.clone().expect("was set above");
let cache = SolFilesCache::read_joined(&project.paths)
.wrap_err("Could not open compiler cache")?;
let (path, _) = get_cached_entry_by_name(&cache, &target_name)
.wrap_err("Could not find target contract in cache")?;
path
};

let target_path = project.root().join(target_path);
let output = compile::compile_target(&target_path, &project, args.opts.silent)?;

let mut target_id: Option<ArtifactId> = None;

Expand Down
Loading