From 26899817821424f94784e8fed259fc904100c29a Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Sun, 27 Mar 2022 18:56:33 +0200 Subject: [PATCH] feat(solc): include source file ast in artifact (#1081) * refactor: extend artifactsoutput interface * add source file to trait * fix: make it compile again --- .../src/artifact_output/configurable.rs | 13 ++++- ethers-solc/src/artifact_output/mod.rs | 51 +++++++++++++++---- ethers-solc/src/compile/project.rs | 19 ++++--- ethers-solc/src/hh.rs | 10 +++- ethers-solc/src/lib.rs | 23 ++++++--- 5 files changed, 91 insertions(+), 25 deletions(-) diff --git a/ethers-solc/src/artifact_output/configurable.rs b/ethers-solc/src/artifact_output/configurable.rs index 47c4742c..b25e962f 100644 --- a/ethers-solc/src/artifact_output/configurable.rs +++ b/ethers-solc/src/artifact_output/configurable.rs @@ -8,7 +8,7 @@ use crate::{ CompactContractBytecodeCow, CompactEvm, DevDoc, Ewasm, GasEstimates, LosslessAbi, Metadata, Offsets, Settings, StorageLayout, UserDoc, }, - ArtifactOutput, SolcConfig, SolcError, + ArtifactOutput, SolcConfig, SolcError, SourceFile, }; use serde::{Deserialize, Serialize}; use std::{borrow::Cow, collections::BTreeMap, fs, path::Path}; @@ -47,6 +47,8 @@ pub struct ConfigurableContractArtifact { pub ir_optimized: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub ewasm: Option, + #[serde(default, skip_serializing_if = "serde_json::Value::is_null")] + pub ast: serde_json::Value, } impl ConfigurableContractArtifact { @@ -190,7 +192,13 @@ impl ArtifactOutput for ConfigurableArtifacts { self.additional_files.write_extras(contract, file) } - fn contract_to_artifact(&self, _file: &str, _name: &str, contract: Contract) -> Self::Artifact { + fn contract_to_artifact( + &self, + _file: &str, + _name: &str, + contract: Contract, + source_file: Option<&SourceFile>, + ) -> Self::Artifact { let mut artifact_userdoc = None; let mut artifact_devdoc = None; let mut artifact_metadata = None; @@ -276,6 +284,7 @@ impl ArtifactOutput for ConfigurableArtifacts { ir: artifact_ir, ir_optimized: artifact_ir_optimized, ewasm: artifact_ewasm, + ast: source_file.map(|s| s.ast.clone()).unwrap_or_default(), } } } diff --git a/ethers-solc/src/artifact_output/mod.rs b/ethers-solc/src/artifact_output/mod.rs index 5f4bcfd0..e5ed4443 100644 --- a/ethers-solc/src/artifact_output/mod.rs +++ b/ethers-solc/src/artifact_output/mod.rs @@ -18,6 +18,7 @@ mod configurable; use crate::artifacts::{ contract::{CompactContract, CompactContractBytecode, Contract}, BytecodeObject, CompactBytecode, CompactContractBytecodeCow, CompactDeployedBytecode, + SourceFile, }; pub use configurable::*; @@ -437,9 +438,10 @@ pub trait ArtifactOutput { fn on_output( &self, contracts: &VersionedContracts, + sources: &BTreeMap, layout: &ProjectPathsConfig, ) -> Result> { - let mut artifacts = self.output_to_artifacts(contracts); + let mut artifacts = self.output_to_artifacts(contracts, sources); artifacts.join_all(&layout.artifacts); artifacts.write_all()?; @@ -581,16 +583,28 @@ pub trait ArtifactOutput { /// Convert a contract to the artifact type /// /// This is the core conversion function that takes care of converting a `Contract` into the - /// associated `Artifact` type - fn contract_to_artifact(&self, _file: &str, _name: &str, contract: Contract) -> Self::Artifact; + /// associated `Artifact` type. + /// The `SourceFile` is also provided + fn contract_to_artifact( + &self, + _file: &str, + _name: &str, + contract: Contract, + source_file: Option<&SourceFile>, + ) -> Self::Artifact; /// Convert the compiler output into a set of artifacts /// /// **Note:** This does only convert, but _NOT_ write the artifacts to disk, See /// [`Self::on_output()`] - fn output_to_artifacts(&self, contracts: &VersionedContracts) -> Artifacts { + fn output_to_artifacts( + &self, + contracts: &VersionedContracts, + sources: &BTreeMap, + ) -> Artifacts { let mut artifacts = ArtifactsMap::new(); for (file, contracts) in contracts.as_ref().iter() { + let source_file = sources.get(file); let mut entries = BTreeMap::new(); for (name, versioned_contracts) in contracts { let mut contracts = Vec::with_capacity(versioned_contracts.len()); @@ -601,7 +615,13 @@ pub trait ArtifactOutput { } else { Self::output_file(file, name) }; - let artifact = self.contract_to_artifact(file, name, contract.contract.clone()); + + let artifact = self.contract_to_artifact( + file, + name, + contract.contract.clone(), + source_file, + ); contracts.push(ArtifactFile { artifact, @@ -636,7 +656,13 @@ pub struct MinimalCombinedArtifacts { impl ArtifactOutput for MinimalCombinedArtifacts { type Artifact = CompactContractBytecode; - fn contract_to_artifact(&self, _file: &str, _name: &str, contract: Contract) -> Self::Artifact { + fn contract_to_artifact( + &self, + _file: &str, + _name: &str, + contract: Contract, + _source_file: Option<&SourceFile>, + ) -> Self::Artifact { Self::Artifact::from(contract) } } @@ -654,9 +680,10 @@ impl ArtifactOutput for MinimalCombinedArtifactsHardhatFallback { fn on_output( &self, output: &VersionedContracts, + sources: &BTreeMap, layout: &ProjectPathsConfig, ) -> Result> { - MinimalCombinedArtifacts::default().on_output(output, layout) + MinimalCombinedArtifacts::default().on_output(output, sources, layout) } fn read_cached_artifact(path: impl AsRef) -> Result { @@ -673,8 +700,14 @@ impl ArtifactOutput for MinimalCombinedArtifactsHardhatFallback { } } - fn contract_to_artifact(&self, file: &str, name: &str, contract: Contract) -> Self::Artifact { - MinimalCombinedArtifacts::default().contract_to_artifact(file, name, contract) + fn contract_to_artifact( + &self, + file: &str, + name: &str, + contract: Contract, + source_file: Option<&SourceFile>, + ) -> Self::Artifact { + MinimalCombinedArtifacts::default().contract_to_artifact(file, name, contract, source_file) } } diff --git a/ethers-solc/src/compile/project.rs b/ethers-solc/src/compile/project.rs index 3e1553c2..ff64c6d5 100644 --- a/ethers-solc/src/compile/project.rs +++ b/ethers-solc/src/compile/project.rs @@ -266,15 +266,22 @@ impl<'a, T: ArtifactOutput> CompiledState<'a, T> { // write all artifacts via the handler but only if the build succeeded let compiled_artifacts = if cache.project().no_artifacts { - cache.project().artifacts_handler().output_to_artifacts(&output.contracts) - } else if output.has_error() { - tracing::trace!("skip writing cache file due to solc errors: {:?}", output.errors); - cache.project().artifacts_handler().output_to_artifacts(&output.contracts) - } else { cache .project() .artifacts_handler() - .on_output(&output.contracts, &cache.project().paths)? + .output_to_artifacts(&output.contracts, &output.sources) + } else if output.has_error() { + tracing::trace!("skip writing cache file due to solc errors: {:?}", output.errors); + cache + .project() + .artifacts_handler() + .output_to_artifacts(&output.contracts, &output.sources) + } else { + cache.project().artifacts_handler().on_output( + &output.contracts, + &output.sources, + &cache.project().paths, + )? }; Ok(ArtifactsState { output, cache, compiled_artifacts }) diff --git a/ethers-solc/src/hh.rs b/ethers-solc/src/hh.rs index bf215a3f..c71fbeb1 100644 --- a/ethers-solc/src/hh.rs +++ b/ethers-solc/src/hh.rs @@ -6,7 +6,7 @@ use crate::{ contract::{CompactContract, CompactContractBytecode, Contract, ContractBytecode}, CompactContractBytecodeCow, LosslessAbi, Offsets, }, - ArtifactOutput, + ArtifactOutput, SourceFile, }; use serde::{Deserialize, Serialize}; use std::{borrow::Cow, collections::btree_map::BTreeMap}; @@ -97,7 +97,13 @@ pub struct HardhatArtifacts { impl ArtifactOutput for HardhatArtifacts { type Artifact = HardhatArtifact; - fn contract_to_artifact(&self, file: &str, name: &str, contract: Contract) -> Self::Artifact { + fn contract_to_artifact( + &self, + file: &str, + name: &str, + contract: Contract, + _source_file: Option<&SourceFile>, + ) -> Self::Artifact { let (bytecode, link_references, deployed_bytecode, deployed_link_references) = if let Some(evm) = contract.evm { let (deployed_bytecode, deployed_link_references) = diff --git a/ethers-solc/src/lib.rs b/ethers-solc/src/lib.rs index dde6139d..3f0798e3 100644 --- a/ethers-solc/src/lib.rs +++ b/ethers-solc/src/lib.rs @@ -24,7 +24,7 @@ mod config; pub use config::{AllowedLibPaths, PathStyle, ProjectPathsConfig, SolcConfig}; pub mod remappings; -use crate::artifacts::Source; +use crate::artifacts::{Source, SourceFile}; pub mod error; mod filter; @@ -677,9 +677,10 @@ impl ArtifactOutput for Project { fn on_output( &self, contracts: &VersionedContracts, + sources: &BTreeMap, layout: &ProjectPathsConfig, ) -> Result> { - self.artifacts_handler().on_output(contracts, layout) + self.artifacts_handler().on_output(contracts, sources, layout) } fn write_contract_extras(&self, contract: &Contract, file: &Path) -> Result<()> { @@ -738,12 +739,22 @@ impl ArtifactOutput for Project { T::read_cached_artifacts(files) } - fn contract_to_artifact(&self, file: &str, name: &str, contract: Contract) -> Self::Artifact { - self.artifacts_handler().contract_to_artifact(file, name, contract) + fn contract_to_artifact( + &self, + file: &str, + name: &str, + contract: Contract, + source_file: Option<&SourceFile>, + ) -> Self::Artifact { + self.artifacts_handler().contract_to_artifact(file, name, contract, source_file) } - fn output_to_artifacts(&self, contracts: &VersionedContracts) -> Artifacts { - self.artifacts_handler().output_to_artifacts(contracts) + fn output_to_artifacts( + &self, + contracts: &VersionedContracts, + sources: &BTreeMap, + ) -> Artifacts { + self.artifacts_handler().output_to_artifacts(contracts, sources) } }