diff --git a/CHANGELOG.md b/CHANGELOG.md index c58a162..5da33b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Supported `polkadot-sdk` rev:`c29e72a8628835e34deb6aa7db9a78a2e4eabcee` ### Added - Support for solc v0.8.29 +- Decouples the solc JSON-input-output type definitions from the Solidity fronted and expose them via a dedicated crate. - `--supported-solc-versions` for `resolc` binary to return a `semver` range of supported `solc` versions. ### Changed diff --git a/Cargo.lock b/Cargo.lock index 6720679..17e7a4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8374,6 +8374,7 @@ dependencies = [ "revive-common", "revive-linker", "revive-runtime-api", + "revive-solc-json-interface", "revive-stdlib", "semver 1.0.25", "serde", @@ -8406,6 +8407,18 @@ dependencies = [ "revive-common", ] +[[package]] +name = "revive-solc-json-interface" +version = "0.1.0-dev.12" +dependencies = [ + "anyhow", + "rayon", + "revive-common", + "semver 1.0.25", + "serde", + "serde_json", +] + [[package]] name = "revive-solidity" version = "0.1.0-dev.12" @@ -8424,6 +8437,7 @@ dependencies = [ "regex", "revive-common", "revive-llvm-context", + "revive-solc-json-interface", "semver 1.0.25", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index caff500..69c3460 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ lld-sys = { version = "0.1.0-dev.12", path = "crates/lld-sys" } revive-llvm-context = { version = "0.1.0-dev.12", path = "crates/llvm-context" } revive-runtime-api = { version = "0.1.0-dev.12", path = "crates/runtime-api" } revive-runner = { version = "0.1.0-dev.12", path = "crates/runner" } +revive-solc-json-interface = { version = "0.1.0-dev.12", path = "crates/solc-json-interface" } revive-solidity = { version = "0.1.0-dev.12", path = "crates/solidity" } revive-stdlib = { version = "0.1.0-dev.12", path = "crates/stdlib" } revive-build-utils = { version = "0.1.0-dev.12", path = "crates/build-utils" } diff --git a/crates/llvm-context/Cargo.toml b/crates/llvm-context/Cargo.toml index 85dda1b..28d564f 100644 --- a/crates/llvm-context/Cargo.toml +++ b/crates/llvm-context/Cargo.toml @@ -29,3 +29,4 @@ revive-common = { workspace = true } revive-runtime-api = { workspace = true } revive-linker = { workspace = true } revive-stdlib = { workspace = true } +revive-solc-json-interface = { workspace = true } diff --git a/crates/llvm-context/src/lib.rs b/crates/llvm-context/src/lib.rs index 279ad4b..01b4080 100644 --- a/crates/llvm-context/src/lib.rs +++ b/crates/llvm-context/src/lib.rs @@ -66,7 +66,6 @@ pub use self::polkavm::evm::memory as polkavm_evm_memory; pub use self::polkavm::evm::r#return as polkavm_evm_return; pub use self::polkavm::evm::return_data as polkavm_evm_return_data; pub use self::polkavm::evm::storage as polkavm_evm_storage; -pub use self::polkavm::metadata_hash::MetadataHash as PolkaVMMetadataHash; pub use self::polkavm::r#const as polkavm_const; pub use self::polkavm::Dependency as PolkaVMDependency; pub use self::polkavm::DummyDependency as PolkaVMDummyDependency; diff --git a/crates/llvm-context/src/optimizer/settings/mod.rs b/crates/llvm-context/src/optimizer/settings/mod.rs index 56dd7c0..7a6fd3a 100644 --- a/crates/llvm-context/src/optimizer/settings/mod.rs +++ b/crates/llvm-context/src/optimizer/settings/mod.rs @@ -2,6 +2,7 @@ pub mod size_level; +use revive_solc_json_interface::SolcStandardJsonInputSettingsOptimizer; use serde::Deserialize; use serde::Serialize; @@ -226,3 +227,18 @@ impl std::fmt::Display for Settings { ) } } + +impl TryFrom<&SolcStandardJsonInputSettingsOptimizer> for Settings { + type Error = anyhow::Error; + + fn try_from(value: &SolcStandardJsonInputSettingsOptimizer) -> Result { + let mut result = match value.mode { + Some(mode) => Self::try_from_cli(mode)?, + None => Self::cycles(), + }; + if value.fallback_to_optimizing_for_size.unwrap_or_default() { + result.enable_fallback_to_size(); + } + Ok(result) + } +} diff --git a/crates/llvm-context/src/polkavm/mod.rs b/crates/llvm-context/src/polkavm/mod.rs index f60857d..f486d4a 100644 --- a/crates/llvm-context/src/polkavm/mod.rs +++ b/crates/llvm-context/src/polkavm/mod.rs @@ -3,7 +3,6 @@ pub mod r#const; pub mod context; pub mod evm; -pub mod metadata_hash; pub use self::r#const::*; diff --git a/crates/solc-json-interface/Cargo.toml b/crates/solc-json-interface/Cargo.toml new file mode 100644 index 0000000..c25fcf8 --- /dev/null +++ b/crates/solc-json-interface/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "revive-solc-json-interface" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true +repository.workspace = true +rust-version.workspace = true +description = "Rust bindings for the solc standard JSON and combined JSON interface" + +[features] +default = ["parallel"] +parallel = ["rayon"] +resolc = [] # The resolc binary adds a bunch of custom fields to the format + +[dependencies] +revive-common = { workspace = true } + +anyhow = { workspace = true } +rayon = { workspace = true, optional = true } +semver = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } \ No newline at end of file diff --git a/crates/solidity/src/solc/combined_json/contract.rs b/crates/solc-json-interface/src/combined_json/contract.rs similarity index 100% rename from crates/solidity/src/solc/combined_json/contract.rs rename to crates/solc-json-interface/src/combined_json/contract.rs diff --git a/crates/solidity/src/solc/combined_json/mod.rs b/crates/solc-json-interface/src/combined_json/mod.rs similarity index 100% rename from crates/solidity/src/solc/combined_json/mod.rs rename to crates/solc-json-interface/src/combined_json/mod.rs diff --git a/crates/solc-json-interface/src/lib.rs b/crates/solc-json-interface/src/lib.rs new file mode 100644 index 0000000..3141fba --- /dev/null +++ b/crates/solc-json-interface/src/lib.rs @@ -0,0 +1,27 @@ +//! This crates provides (de)serializable Rust types for interacting +//! `solc` via the [JSON-input-output][0] interface. +//! +//! [0]: https://docs.soliditylang.org/en/latest/using-the-compiler.html#compiler-input-and-output-json-description + +pub use self::combined_json::contract::Contract as CombinedJsonContract; +pub use self::standard_json::input::language::Language as SolcStandardJsonInputLanguage; +pub use self::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata; +pub use self::standard_json::input::settings::metadata_hash::MetadataHash as SolcStandardJsonInputSettingsMetadataHash; +pub use self::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer; +pub use self::standard_json::input::settings::selection::file::flag::Flag as SolcStandardJsonInputSettingsSelectionFileFlag; +pub use self::standard_json::input::settings::selection::file::File as SolcStandardJsonInputSettingsSelectionFile; +pub use self::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection; +pub use self::standard_json::input::settings::Settings as SolcStandardJsonInputSettings; +pub use self::standard_json::input::source::Source as SolcStandardJsonInputSource; +pub use self::standard_json::input::Input as SolcStandardJsonInput; +pub use self::standard_json::output::contract::evm::bytecode::Bytecode as SolcStandardJsonOutputContractEVMBytecode; +pub use self::standard_json::output::contract::evm::EVM as SolcStandardJsonOutputContractEVM; +pub use self::standard_json::output::contract::Contract as SolcStandardJsonOutputContract; +pub use self::standard_json::output::Output as SolcStandardJsonOutput; +#[cfg(feature = "resolc")] +pub use self::warning::Warning as ResolcWarning; + +pub mod combined_json; +pub mod standard_json; +#[cfg(feature = "resolc")] +pub mod warning; diff --git a/crates/solidity/src/solc/standard_json/input/language.rs b/crates/solc-json-interface/src/standard_json/input/language.rs similarity index 100% rename from crates/solidity/src/solc/standard_json/input/language.rs rename to crates/solc-json-interface/src/standard_json/input/language.rs diff --git a/crates/solidity/src/solc/standard_json/input/mod.rs b/crates/solc-json-interface/src/standard_json/input/mod.rs similarity index 87% rename from crates/solidity/src/solc/standard_json/input/mod.rs rename to crates/solc-json-interface/src/standard_json/input/mod.rs index 41e7263..5c7d4d2 100644 --- a/crates/solidity/src/solc/standard_json/input/mod.rs +++ b/crates/solc-json-interface/src/standard_json/input/mod.rs @@ -8,14 +8,15 @@ use std::collections::BTreeMap; use std::collections::BTreeSet; use std::path::PathBuf; -#[cfg(feature = "parallel")] +#[cfg(all(feature = "parallel", feature = "resolc"))] use rayon::iter::{IntoParallelIterator, ParallelIterator}; use serde::Deserialize; use serde::Serialize; -use crate::solc::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata; -use crate::solc::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer; -use crate::solc::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection; +use crate::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata; +use crate::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer; +use crate::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection; +#[cfg(feature = "resolc")] use crate::warning::Warning; use self::language::Language; @@ -33,6 +34,7 @@ pub struct Input { /// The compiler settings. pub settings: Settings, /// The suppressed warnings. + #[cfg(feature = "resolc")] #[serde(skip_serializing)] pub suppressed_warnings: Option>, } @@ -60,7 +62,7 @@ impl Input { output_selection: SolcStandardJsonInputSettingsSelection, optimizer: SolcStandardJsonInputSettingsOptimizer, metadata: Option, - suppressed_warnings: Option>, + #[cfg(feature = "resolc")] suppressed_warnings: Option>, ) -> anyhow::Result { let mut paths: BTreeSet = paths.iter().cloned().collect(); let libraries = Settings::parse_libraries(library_map)?; @@ -89,12 +91,14 @@ impl Input { optimizer, metadata, ), + #[cfg(feature = "resolc")] suppressed_warnings, }) } /// A shortcut constructor from source code. /// Only for the integration test purposes. + #[cfg(feature = "resolc")] #[allow(clippy::too_many_arguments)] pub fn try_from_sources( evm_version: Option, diff --git a/crates/solidity/src/solc/standard_json/input/settings/metadata.rs b/crates/solc-json-interface/src/standard_json/input/settings/metadata.rs similarity index 73% rename from crates/solidity/src/solc/standard_json/input/settings/metadata.rs rename to crates/solc-json-interface/src/standard_json/input/settings/metadata.rs index cfbd555..ff89713 100644 --- a/crates/solidity/src/solc/standard_json/input/settings/metadata.rs +++ b/crates/solc-json-interface/src/standard_json/input/settings/metadata.rs @@ -3,18 +3,20 @@ use serde::Deserialize; use serde::Serialize; +use crate::standard_json::input::settings::metadata_hash::MetadataHash; + /// The `solc --standard-json` input settings metadata. #[derive(Debug, Default, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Metadata { /// The bytecode hash mode. #[serde(skip_serializing_if = "Option::is_none")] - pub bytecode_hash: Option, + pub bytecode_hash: Option, } impl Metadata { /// A shortcut constructor. - pub fn new(bytecode_hash: revive_llvm_context::PolkaVMMetadataHash) -> Self { + pub fn new(bytecode_hash: MetadataHash) -> Self { Self { bytecode_hash: Some(bytecode_hash), } diff --git a/crates/llvm-context/src/polkavm/metadata_hash.rs b/crates/solc-json-interface/src/standard_json/input/settings/metadata_hash.rs similarity index 100% rename from crates/llvm-context/src/polkavm/metadata_hash.rs rename to crates/solc-json-interface/src/standard_json/input/settings/metadata_hash.rs diff --git a/crates/solidity/src/solc/standard_json/input/settings/mod.rs b/crates/solc-json-interface/src/standard_json/input/settings/mod.rs similarity index 99% rename from crates/solidity/src/solc/standard_json/input/settings/mod.rs rename to crates/solc-json-interface/src/standard_json/input/settings/mod.rs index dff5a83..250d928 100644 --- a/crates/solidity/src/solc/standard_json/input/settings/mod.rs +++ b/crates/solc-json-interface/src/standard_json/input/settings/mod.rs @@ -1,6 +1,7 @@ //! The `solc --standard-json` input settings. pub mod metadata; +pub mod metadata_hash; pub mod optimizer; pub mod selection; diff --git a/crates/solidity/src/solc/standard_json/input/settings/optimizer/details.rs b/crates/solc-json-interface/src/standard_json/input/settings/optimizer/details.rs similarity index 100% rename from crates/solidity/src/solc/standard_json/input/settings/optimizer/details.rs rename to crates/solc-json-interface/src/standard_json/input/settings/optimizer/details.rs diff --git a/crates/solidity/src/solc/standard_json/input/settings/optimizer/mod.rs b/crates/solc-json-interface/src/standard_json/input/settings/optimizer/mod.rs similarity index 75% rename from crates/solidity/src/solc/standard_json/input/settings/optimizer/mod.rs rename to crates/solc-json-interface/src/standard_json/input/settings/optimizer/mod.rs index 62b2391..e276613 100644 --- a/crates/solidity/src/solc/standard_json/input/settings/optimizer/mod.rs +++ b/crates/solc-json-interface/src/standard_json/input/settings/optimizer/mod.rs @@ -49,18 +49,3 @@ impl Optimizer { }; } } - -impl TryFrom<&Optimizer> for revive_llvm_context::OptimizerSettings { - type Error = anyhow::Error; - - fn try_from(value: &Optimizer) -> Result { - let mut result = match value.mode { - Some(mode) => Self::try_from_cli(mode)?, - None => Self::cycles(), - }; - if value.fallback_to_optimizing_for_size.unwrap_or_default() { - result.enable_fallback_to_size(); - } - Ok(result) - } -} diff --git a/crates/solidity/src/solc/standard_json/input/settings/selection/file/flag.rs b/crates/solc-json-interface/src/standard_json/input/settings/selection/file/flag.rs similarity index 100% rename from crates/solidity/src/solc/standard_json/input/settings/selection/file/flag.rs rename to crates/solc-json-interface/src/standard_json/input/settings/selection/file/flag.rs diff --git a/crates/solidity/src/solc/standard_json/input/settings/selection/file/mod.rs b/crates/solc-json-interface/src/standard_json/input/settings/selection/file/mod.rs similarity index 100% rename from crates/solidity/src/solc/standard_json/input/settings/selection/file/mod.rs rename to crates/solc-json-interface/src/standard_json/input/settings/selection/file/mod.rs diff --git a/crates/solidity/src/solc/standard_json/input/settings/selection/mod.rs b/crates/solc-json-interface/src/standard_json/input/settings/selection/mod.rs similarity index 100% rename from crates/solidity/src/solc/standard_json/input/settings/selection/mod.rs rename to crates/solc-json-interface/src/standard_json/input/settings/selection/mod.rs diff --git a/crates/solidity/src/solc/standard_json/input/source.rs b/crates/solc-json-interface/src/standard_json/input/source.rs similarity index 100% rename from crates/solidity/src/solc/standard_json/input/source.rs rename to crates/solc-json-interface/src/standard_json/input/source.rs diff --git a/crates/solc-json-interface/src/standard_json/mod.rs b/crates/solc-json-interface/src/standard_json/mod.rs new file mode 100644 index 0000000..2ceb790 --- /dev/null +++ b/crates/solc-json-interface/src/standard_json/mod.rs @@ -0,0 +1,4 @@ +//! The `solc .sol --standard-json` interface input and output. + +pub mod input; +pub mod output; diff --git a/crates/solidity/src/solc/standard_json/output/contract/evm/bytecode.rs b/crates/solc-json-interface/src/standard_json/output/contract/evm/bytecode.rs similarity index 100% rename from crates/solidity/src/solc/standard_json/output/contract/evm/bytecode.rs rename to crates/solc-json-interface/src/standard_json/output/contract/evm/bytecode.rs diff --git a/crates/solidity/src/solc/standard_json/output/contract/evm/mod.rs b/crates/solc-json-interface/src/standard_json/output/contract/evm/mod.rs similarity index 100% rename from crates/solidity/src/solc/standard_json/output/contract/evm/mod.rs rename to crates/solc-json-interface/src/standard_json/output/contract/evm/mod.rs diff --git a/crates/solidity/src/solc/standard_json/output/contract/mod.rs b/crates/solc-json-interface/src/standard_json/output/contract/mod.rs similarity index 100% rename from crates/solidity/src/solc/standard_json/output/contract/mod.rs rename to crates/solc-json-interface/src/standard_json/output/contract/mod.rs diff --git a/crates/solidity/src/solc/standard_json/output/error/mod.rs b/crates/solc-json-interface/src/standard_json/output/error/mod.rs similarity index 100% rename from crates/solidity/src/solc/standard_json/output/error/mod.rs rename to crates/solc-json-interface/src/standard_json/output/error/mod.rs diff --git a/crates/solidity/src/solc/standard_json/output/error/source_location.rs b/crates/solc-json-interface/src/standard_json/output/error/source_location.rs similarity index 100% rename from crates/solidity/src/solc/standard_json/output/error/source_location.rs rename to crates/solc-json-interface/src/standard_json/output/error/source_location.rs diff --git a/crates/solc-json-interface/src/standard_json/output/mod.rs b/crates/solc-json-interface/src/standard_json/output/mod.rs new file mode 100644 index 0000000..95779db --- /dev/null +++ b/crates/solc-json-interface/src/standard_json/output/mod.rs @@ -0,0 +1,71 @@ +//! The `solc --standard-json` output. + +pub mod contract; +pub mod error; +pub mod source; + +use std::collections::BTreeMap; + +use serde::Deserialize; +use serde::Serialize; + +#[cfg(feature = "resolc")] +use crate::warning::Warning; + +use self::contract::Contract; +use self::error::Error as SolcStandardJsonOutputError; +use self::source::Source; + +/// The `solc --standard-json` output. +#[derive(Debug, Serialize, Deserialize, Clone, Default)] +pub struct Output { + /// The file-contract hashmap. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub contracts: Option>>, + /// The source code mapping data. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sources: Option>, + /// The compilation errors and warnings. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub errors: Option>, + /// The `solc` compiler version. + #[serde(skip_serializing_if = "Option::is_none")] + pub version: Option, + /// The `solc` compiler long version. + #[serde(skip_serializing_if = "Option::is_none")] + pub long_version: Option, + /// The `resolc` compiler version. + #[serde(skip_serializing_if = "Option::is_none")] + pub revive_version: Option, +} + +impl Output { + /// Traverses the AST and returns the list of additional errors and warnings. + #[cfg(feature = "resolc")] + pub fn preprocess_ast(&mut self, suppressed_warnings: &[Warning]) -> anyhow::Result<()> { + let sources = match self.sources.as_ref() { + Some(sources) => sources, + None => return Ok(()), + }; + + let mut messages = Vec::new(); + for (path, source) in sources.iter() { + if let Some(ast) = source.ast.as_ref() { + let mut polkavm_messages = Source::get_messages(ast, suppressed_warnings); + for message in polkavm_messages.iter_mut() { + message.push_contract_path(path.as_str()); + } + messages.extend(polkavm_messages); + } + } + self.errors = match self.errors.take() { + Some(mut errors) => { + errors.extend(messages); + Some(errors) + } + None => Some(messages), + }; + + Ok(()) + } +} diff --git a/crates/solidity/src/solc/standard_json/output/source.rs b/crates/solc-json-interface/src/standard_json/output/source.rs similarity index 97% rename from crates/solidity/src/solc/standard_json/output/source.rs rename to crates/solc-json-interface/src/standard_json/output/source.rs index 97b2e14..6472372 100644 --- a/crates/solidity/src/solc/standard_json/output/source.rs +++ b/crates/solc-json-interface/src/standard_json/output/source.rs @@ -3,7 +3,8 @@ use serde::Deserialize; use serde::Serialize; -use crate::solc::standard_json::output::error::Error as SolcStandardJsonOutputError; +use crate::standard_json::output::error::Error as SolcStandardJsonOutputError; +#[cfg(feature = "resolc")] use crate::warning::Warning; /// The `solc --standard-json` output source. @@ -131,6 +132,7 @@ impl Source { } /// Returns the list of messages for some specific parts of the AST. + #[cfg(feature = "resolc")] pub fn get_messages( ast: &serde_json::Value, suppressed_warnings: &[Warning], diff --git a/crates/solidity/src/warning.rs b/crates/solc-json-interface/src/warning.rs similarity index 76% rename from crates/solidity/src/warning.rs rename to crates/solc-json-interface/src/warning.rs index b0d882a..f1a6d48 100644 --- a/crates/solidity/src/warning.rs +++ b/crates/solc-json-interface/src/warning.rs @@ -1,26 +1,22 @@ -//! The compiler warning. +//! `resolc` custom compiler warnings. +//! +//! The revive compiler adds warnings only applicable when compilng +//! to the revive stack on Polkadot to the output. use std::str::FromStr; use serde::Deserialize; use serde::Serialize; -/// The compiler warning. +// The `resolc` custom compiler warning. #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)] pub enum Warning { - /// The warning for eponymous feature. EcRecover, - /// The warning for eponymous feature. SendTransfer, - /// The warning for eponymous feature. ExtCodeSize, - /// The warning for eponymous feature. TxOrigin, - /// The warning for eponymous feature. BlockTimestamp, - /// The warning for eponymous feature. BlockNumber, - /// The warning for eponymous feature. BlockHash, } diff --git a/crates/solidity/Cargo.toml b/crates/solidity/Cargo.toml index e2bc150..5843ba0 100644 --- a/crates/solidity/Cargo.toml +++ b/crates/solidity/Cargo.toml @@ -37,6 +37,7 @@ inkwell = { workspace = true } revive-common = { workspace = true } revive-llvm-context = { workspace = true } +revive-solc-json-interface = { workspace = true, features = ["resolc"] } [target.'cfg(target_env = "musl")'.dependencies] mimalloc = { version = "*", default-features = false } diff --git a/crates/solidity/src/build/contract.rs b/crates/solidity/src/build/contract.rs index b2d75ed..721f501 100644 --- a/crates/solidity/src/build/contract.rs +++ b/crates/solidity/src/build/contract.rs @@ -5,12 +5,11 @@ use std::fs::File; use std::io::Write; use std::path::Path; +use revive_solc_json_interface::CombinedJsonContract; +use revive_solc_json_interface::SolcStandardJsonOutputContract; use serde::Deserialize; use serde::Serialize; -use crate::solc::combined_json::contract::Contract as CombinedJsonContract; -use crate::solc::standard_json::output::contract::Contract as StandardJsonOutputContract; - /// The Solidity contract build. #[derive(Debug, Serialize, Deserialize)] pub struct Contract { @@ -131,7 +130,7 @@ impl Contract { /// Writes the contract text assembly and bytecode to the standard JSON. pub fn write_to_standard_json( self, - standard_json_contract: &mut StandardJsonOutputContract, + standard_json_contract: &mut SolcStandardJsonOutputContract, ) -> anyhow::Result<()> { standard_json_contract.metadata = Some(self.metadata_json); diff --git a/crates/solidity/src/build/mod.rs b/crates/solidity/src/build/mod.rs index 5dd5d7e..853a48f 100644 --- a/crates/solidity/src/build/mod.rs +++ b/crates/solidity/src/build/mod.rs @@ -5,8 +5,9 @@ pub mod contract; use std::collections::BTreeMap; use std::path::Path; -use crate::solc::combined_json::CombinedJson; -use crate::solc::standard_json::output::Output as StandardJsonOutput; +use revive_solc_json_interface::combined_json::CombinedJson; +use revive_solc_json_interface::SolcStandardJsonOutput; + use crate::solc::version::Version as SolcVersion; use crate::ResolcVersion; @@ -66,7 +67,7 @@ impl Build { /// Writes all contracts assembly and bytecode to the standard JSON. pub fn write_to_standard_json( mut self, - standard_json: &mut StandardJsonOutput, + standard_json: &mut SolcStandardJsonOutput, solc_version: &SolcVersion, ) -> anyhow::Result<()> { let contracts = match standard_json.contracts.as_mut() { diff --git a/crates/solidity/src/lib.rs b/crates/solidity/src/lib.rs index 24776e2..94169c1 100644 --- a/crates/solidity/src/lib.rs +++ b/crates/solidity/src/lib.rs @@ -7,7 +7,6 @@ pub(crate) mod process; pub(crate) mod project; pub(crate) mod solc; pub(crate) mod version; -pub(crate) mod warning; pub(crate) mod yul; pub use self::build::contract::Contract as ContractBuild; @@ -23,31 +22,15 @@ pub use self::process::Process; pub use self::project::contract::Contract as ProjectContract; pub use self::project::Project; pub use self::r#const::*; -pub use self::solc::combined_json::contract::Contract as SolcCombinedJsonContract; -pub use self::solc::combined_json::CombinedJson as SolcCombinedJson; #[cfg(not(target_os = "emscripten"))] pub use self::solc::solc_compiler::SolcCompiler; #[cfg(target_os = "emscripten")] pub use self::solc::soljson_compiler::SoljsonCompiler; -pub use self::solc::standard_json::input::language::Language as SolcStandardJsonInputLanguage; -pub use self::solc::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata; -pub use self::solc::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer; -pub use self::solc::standard_json::input::settings::selection::file::flag::Flag as SolcStandardJsonInputSettingsSelectionFileFlag; -pub use self::solc::standard_json::input::settings::selection::file::File as SolcStandardJsonInputSettingsSelectionFile; -pub use self::solc::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection; -pub use self::solc::standard_json::input::settings::Settings as SolcStandardJsonInputSettings; -pub use self::solc::standard_json::input::source::Source as SolcStandardJsonInputSource; -pub use self::solc::standard_json::input::Input as SolcStandardJsonInput; -pub use self::solc::standard_json::output::contract::evm::bytecode::Bytecode as SolcStandardJsonOutputContractEVMBytecode; -pub use self::solc::standard_json::output::contract::evm::EVM as SolcStandardJsonOutputContractEVM; -pub use self::solc::standard_json::output::contract::Contract as SolcStandardJsonOutputContract; -pub use self::solc::standard_json::output::Output as SolcStandardJsonOutput; pub use self::solc::version::Version as SolcVersion; pub use self::solc::Compiler; pub use self::solc::FIRST_SUPPORTED_VERSION as SolcFirstSupportedVersion; pub use self::solc::LAST_SUPPORTED_VERSION as SolcLastSupportedVersion; pub use self::version::Version as ResolcVersion; -pub use self::warning::Warning; #[cfg(not(target_os = "emscripten"))] pub mod test_utils; @@ -57,6 +40,13 @@ use std::collections::BTreeSet; use std::io::Write; use std::path::PathBuf; +use revive_solc_json_interface::standard_json::input::settings::metadata_hash::MetadataHash; +use revive_solc_json_interface::ResolcWarning; +use revive_solc_json_interface::SolcStandardJsonInput; +use revive_solc_json_interface::SolcStandardJsonInputLanguage; +use revive_solc_json_interface::SolcStandardJsonInputSettingsOptimizer; +use revive_solc_json_interface::SolcStandardJsonInputSettingsSelection; + /// Runs the Yul mode. pub fn yul( input_files: &[PathBuf], @@ -126,7 +116,7 @@ pub fn standard_output( include_paths: Vec, allow_paths: Option, remappings: Option>, - suppressed_warnings: Option>, + suppressed_warnings: Option>, debug_config: revive_llvm_context::DebugConfig, ) -> anyhow::Result { let solc_version = solc.version()?; @@ -155,7 +145,7 @@ pub fn standard_output( .collect(); let libraries = solc_input.settings.libraries.clone().unwrap_or_default(); - let mut solc_output = solc.standard_json(solc_input, base_path, include_paths, allow_paths)?; + let solc_output = solc.standard_json(solc_input, base_path, include_paths, allow_paths)?; if let Some(errors) = solc_output.errors.as_deref() { let mut has_errors = false; @@ -173,8 +163,13 @@ pub fn standard_output( } } - let project = - solc_output.try_to_project(source_code_files, libraries, &solc_version, &debug_config)?; + let project = Project::try_from_standard_json_output( + &solc_output, + source_code_files, + libraries, + &solc_version, + &debug_config, + )?; let build = project.compile(optimizer_settings, include_metadata_hash, debug_config)?; @@ -203,9 +198,7 @@ pub fn standard_json( revive_llvm_context::OptimizerSettings::try_from(&solc_input.settings.optimizer)?; let include_metadata_hash = match solc_input.settings.metadata { - Some(ref metadata) => { - metadata.bytecode_hash != Some(revive_llvm_context::PolkaVMMetadataHash::None) - } + Some(ref metadata) => metadata.bytecode_hash != Some(MetadataHash::None), None => true, }; @@ -221,8 +214,13 @@ pub fn standard_json( } } - let project = - solc_output.try_to_project(source_code_files, libraries, &solc_version, &debug_config)?; + let project = Project::try_from_standard_json_output( + &solc_output, + source_code_files, + libraries, + &solc_version, + &debug_config, + )?; if detect_missing_libraries { let missing_libraries = project.get_missing_libraries(); @@ -250,7 +248,7 @@ pub fn combined_json( include_paths: Vec, allow_paths: Option, remappings: Option>, - suppressed_warnings: Option>, + suppressed_warnings: Option>, debug_config: revive_llvm_context::DebugConfig, output_directory: Option, overwrite: bool, diff --git a/crates/solidity/src/missing_libraries.rs b/crates/solidity/src/missing_libraries.rs index f14927f..dc5b990 100644 --- a/crates/solidity/src/missing_libraries.rs +++ b/crates/solidity/src/missing_libraries.rs @@ -3,7 +3,8 @@ use std::collections::BTreeMap; use std::collections::HashSet; -use crate::solc::standard_json::output::Output as StandardJsonOutput; +use revive_solc_json_interface::SolcStandardJsonOutput; + use crate::solc::version::Version as SolcVersion; use crate::ResolcVersion; @@ -22,7 +23,7 @@ impl MissingLibraries { /// Writes the missing libraries to the standard JSON. pub fn write_to_standard_json( mut self, - standard_json: &mut StandardJsonOutput, + standard_json: &mut SolcStandardJsonOutput, solc_version: &SolcVersion, ) -> anyhow::Result<()> { let contracts = match standard_json.contracts.as_mut() { diff --git a/crates/solidity/src/project/mod.rs b/crates/solidity/src/project/mod.rs index 77eb4c2..1d43583 100644 --- a/crates/solidity/src/project/mod.rs +++ b/crates/solidity/src/project/mod.rs @@ -9,6 +9,7 @@ use std::path::Path; #[cfg(feature = "parallel")] use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use revive_solc_json_interface::SolcStandardJsonOutput; use serde::Deserialize; use serde::Serialize; use sha3::Digest; @@ -243,6 +244,69 @@ impl Project { BTreeMap::new(), )) } + + /// Converts the `solc` JSON output into a convenient project. + pub fn try_from_standard_json_output( + output: &SolcStandardJsonOutput, + source_code_files: BTreeMap, + libraries: BTreeMap>, + solc_version: &SolcVersion, + debug_config: &revive_llvm_context::DebugConfig, + ) -> anyhow::Result { + let files = match output.contracts.as_ref() { + Some(files) => files, + None => match &output.errors { + Some(errors) if errors.iter().any(|e| e.severity == "error") => { + anyhow::bail!(serde_json::to_string_pretty(errors).expect("Always valid")); + } + _ => &BTreeMap::new(), + }, + }; + let mut project_contracts = BTreeMap::new(); + + for (path, contracts) in files.iter() { + for (name, contract) in contracts.iter() { + let full_path = format!("{path}:{name}"); + + let ir_optimized = match contract.ir_optimized.to_owned() { + Some(ir_optimized) => ir_optimized, + None => continue, + }; + if ir_optimized.is_empty() { + continue; + } + + debug_config.dump_yul(full_path.as_str(), ir_optimized.as_str())?; + + let mut lexer = Lexer::new(ir_optimized.to_owned()); + let object = Object::parse(&mut lexer, None).map_err(|error| { + anyhow::anyhow!("Contract `{}` parsing error: {:?}", full_path, error) + })?; + + let source = IR::new_yul(ir_optimized.to_owned(), object); + + let source_code = source_code_files + .get(path.as_str()) + .ok_or_else(|| anyhow::anyhow!("Source code for path `{}` not found", path))?; + let source_hash = sha3::Keccak256::digest(source_code.as_bytes()).into(); + + let project_contract = Contract::new( + full_path.clone(), + source_hash, + solc_version.to_owned(), + source, + contract.metadata.to_owned(), + ); + project_contracts.insert(full_path, project_contract); + } + } + + Ok(Project::new( + solc_version.to_owned(), + project_contracts, + libraries, + )) + } } impl revive_llvm_context::PolkaVMDependency for Project { diff --git a/crates/solidity/src/resolc/main.rs b/crates/solidity/src/resolc/main.rs index 5e133f6..632346c 100644 --- a/crates/solidity/src/resolc/main.rs +++ b/crates/solidity/src/resolc/main.rs @@ -104,7 +104,7 @@ fn main_inner() -> anyhow::Result<()> { let (input_files, remappings) = arguments.split_input_files_and_remappings()?; let suppressed_warnings = match arguments.suppress_warnings { - Some(warnings) => Some(revive_solidity::Warning::try_from_strings( + Some(warnings) => Some(revive_solc_json_interface::ResolcWarning::try_from_strings( warnings.as_slice(), )?), None => None, @@ -142,8 +142,10 @@ fn main_inner() -> anyhow::Result<()> { let include_metadata_hash = match arguments.metadata_hash { Some(metadata_hash) => { let metadata = - revive_llvm_context::PolkaVMMetadataHash::from_str(metadata_hash.as_str())?; - metadata != revive_llvm_context::PolkaVMMetadataHash::None + revive_solc_json_interface::SolcStandardJsonInputSettingsMetadataHash::from_str( + metadata_hash.as_str(), + )?; + metadata != revive_solc_json_interface::SolcStandardJsonInputSettingsMetadataHash::None } None => true, }; diff --git a/crates/solidity/src/solc/mod.rs b/crates/solidity/src/solc/mod.rs index f5d0bed..da4af93 100644 --- a/crates/solidity/src/solc/mod.rs +++ b/crates/solidity/src/solc/mod.rs @@ -1,19 +1,18 @@ //! The Solidity compiler. -pub mod combined_json; #[cfg(not(target_os = "emscripten"))] pub mod solc_compiler; #[cfg(target_os = "emscripten")] pub mod soljson_compiler; -pub mod standard_json; pub mod version; use std::path::Path; use std::path::PathBuf; -use self::combined_json::CombinedJson; -use self::standard_json::input::Input as StandardJsonInput; -use self::standard_json::output::Output as StandardJsonOutput; +use revive_solc_json_interface::combined_json::CombinedJson; +use revive_solc_json_interface::SolcStandardJsonInput; +use revive_solc_json_interface::SolcStandardJsonOutput; + use self::version::Version; /// The first version of `solc` with the support of standard JSON interface. @@ -30,11 +29,11 @@ pub trait Compiler { /// Compiles the Solidity `--standard-json` input into Yul IR. fn standard_json( &mut self, - input: StandardJsonInput, + input: SolcStandardJsonInput, base_path: Option, include_paths: Vec, allow_paths: Option, - ) -> anyhow::Result; + ) -> anyhow::Result; /// The `solc --combined-json abi,hashes...` mirror. fn combined_json( diff --git a/crates/solidity/src/solc/solc_compiler.rs b/crates/solidity/src/solc/solc_compiler.rs index 947e97b..5d92110 100644 --- a/crates/solidity/src/solc/solc_compiler.rs +++ b/crates/solidity/src/solc/solc_compiler.rs @@ -4,9 +4,10 @@ use std::io::Write; use std::path::Path; use std::path::PathBuf; -use crate::solc::combined_json::CombinedJson; -use crate::solc::standard_json::input::Input as StandardJsonInput; -use crate::solc::standard_json::output::Output as StandardJsonOutput; +use revive_solc_json_interface::combined_json::CombinedJson; +use revive_solc_json_interface::SolcStandardJsonInput; +use revive_solc_json_interface::SolcStandardJsonOutput; + use crate::solc::version::Version; use super::Compiler; @@ -39,11 +40,11 @@ impl Compiler for SolcCompiler { /// Compiles the Solidity `--standard-json` input into Yul IR. fn standard_json( &mut self, - mut input: StandardJsonInput, + mut input: SolcStandardJsonInput, base_path: Option, include_paths: Vec, allow_paths: Option, - ) -> anyhow::Result { + ) -> anyhow::Result { let version = self.version()?.validate(&include_paths)?.default; let mut command = std::process::Command::new(self.executable.as_str()); @@ -93,7 +94,7 @@ impl Compiler for SolcCompiler { ); } - let mut output: StandardJsonOutput = + let mut output: SolcStandardJsonOutput = revive_common::deserialize_from_slice(output.stdout.as_slice()).map_err(|error| { anyhow::anyhow!( "{} subprocess output parsing error: {}\n{}", diff --git a/crates/solidity/src/solc/soljson_compiler.rs b/crates/solidity/src/solc/soljson_compiler.rs index 0c94664..438daaf 100644 --- a/crates/solidity/src/solc/soljson_compiler.rs +++ b/crates/solidity/src/solc/soljson_compiler.rs @@ -3,9 +3,10 @@ use std::path::Path; use std::path::PathBuf; -use crate::solc::combined_json::CombinedJson; -use crate::solc::standard_json::input::Input as StandardJsonInput; -use crate::solc::standard_json::output::Output as StandardJsonOutput; +use revive_solc_json_interface::combined_json::CombinedJson; +use revive_solc_json_interface::SolcStandardJsonInput; +use revive_solc_json_interface::SolcStandardJsonOutput; + use crate::solc::version::Version; use anyhow::Context; use std::ffi::{c_char, c_void, CStr, CString}; @@ -24,11 +25,11 @@ impl Compiler for SoljsonCompiler { /// Compiles the Solidity `--standard-json` input into Yul IR. fn standard_json( &mut self, - mut input: StandardJsonInput, + mut input: SolcStandardJsonInput, base_path: Option, include_paths: Vec, allow_paths: Option, - ) -> anyhow::Result { + ) -> anyhow::Result { if !include_paths.is_empty() { anyhow::bail!("configuring include paths is not supported with solJson") } @@ -46,8 +47,8 @@ impl Compiler for SoljsonCompiler { let input_json = serde_json::to_string(&input).expect("Always valid"); let out = Self::compile_standard_json(input_json)?; - let mut output: StandardJsonOutput = revive_common::deserialize_from_slice(out.as_bytes()) - .map_err(|error| { + let mut output: SolcStandardJsonOutput = + revive_common::deserialize_from_slice(out.as_bytes()).map_err(|error| { anyhow::anyhow!( "Soljson output parsing error: {}\n{}", error, diff --git a/crates/solidity/src/solc/standard_json/mod.rs b/crates/solidity/src/solc/standard_json/mod.rs deleted file mode 100644 index b0468b1..0000000 --- a/crates/solidity/src/solc/standard_json/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! The `solc .sol --standard-json`. - -pub mod input; -pub mod output; diff --git a/crates/solidity/src/solc/standard_json/output/mod.rs b/crates/solidity/src/solc/standard_json/output/mod.rs deleted file mode 100644 index 5ea4100..0000000 --- a/crates/solidity/src/solc/standard_json/output/mod.rs +++ /dev/null @@ -1,138 +0,0 @@ -//! The `solc --standard-json` output. - -pub mod contract; -pub mod error; -pub mod source; - -use std::collections::BTreeMap; - -use serde::Deserialize; -use serde::Serialize; -use sha3::Digest; - -use crate::project::contract::ir::IR as ProjectContractIR; -use crate::project::contract::Contract as ProjectContract; -use crate::project::Project; -use crate::solc::version::Version as SolcVersion; -use crate::warning::Warning; -use crate::yul::lexer::Lexer; -use crate::yul::parser::statement::object::Object; - -use self::contract::Contract; -use self::error::Error as SolcStandardJsonOutputError; -use self::source::Source; -/// The `solc --standard-json` output. -#[derive(Debug, Serialize, Deserialize, Clone, Default)] -pub struct Output { - /// The file-contract hashmap. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub contracts: Option>>, - /// The source code mapping data. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub sources: Option>, - /// The compilation errors and warnings. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub errors: Option>, - /// The `solc` compiler version. - #[serde(skip_serializing_if = "Option::is_none")] - pub version: Option, - /// The `solc` compiler long version. - #[serde(skip_serializing_if = "Option::is_none")] - pub long_version: Option, - /// The `resolc` compiler version. - #[serde(skip_serializing_if = "Option::is_none")] - pub revive_version: Option, -} - -impl Output { - /// Converts the `solc` JSON output into a convenient project. - pub fn try_to_project( - &mut self, - source_code_files: BTreeMap, - libraries: BTreeMap>, - solc_version: &SolcVersion, - debug_config: &revive_llvm_context::DebugConfig, - ) -> anyhow::Result { - let files = match self.contracts.as_ref() { - Some(files) => files, - None => match &self.errors { - Some(errors) if errors.iter().any(|e| e.severity == "error") => { - anyhow::bail!(serde_json::to_string_pretty(errors).expect("Always valid")); - } - _ => &BTreeMap::new(), - }, - }; - let mut project_contracts = BTreeMap::new(); - - for (path, contracts) in files.iter() { - for (name, contract) in contracts.iter() { - let full_path = format!("{path}:{name}"); - - let ir_optimized = match contract.ir_optimized.to_owned() { - Some(ir_optimized) => ir_optimized, - None => continue, - }; - if ir_optimized.is_empty() { - continue; - } - - debug_config.dump_yul(full_path.as_str(), ir_optimized.as_str())?; - - let mut lexer = Lexer::new(ir_optimized.to_owned()); - let object = Object::parse(&mut lexer, None).map_err(|error| { - anyhow::anyhow!("Contract `{}` parsing error: {:?}", full_path, error) - })?; - - let source = ProjectContractIR::new_yul(ir_optimized.to_owned(), object); - - let source_code = source_code_files - .get(path.as_str()) - .ok_or_else(|| anyhow::anyhow!("Source code for path `{}` not found", path))?; - let source_hash = sha3::Keccak256::digest(source_code.as_bytes()).into(); - - let project_contract = ProjectContract::new( - full_path.clone(), - source_hash, - solc_version.to_owned(), - source, - contract.metadata.to_owned(), - ); - project_contracts.insert(full_path, project_contract); - } - } - - Ok(Project::new( - solc_version.to_owned(), - project_contracts, - libraries, - )) - } - - /// Traverses the AST and returns the list of additional errors and warnings. - pub fn preprocess_ast(&mut self, suppressed_warnings: &[Warning]) -> anyhow::Result<()> { - let sources = match self.sources.as_ref() { - Some(sources) => sources, - None => return Ok(()), - }; - - let mut messages = Vec::new(); - for (path, source) in sources.iter() { - if let Some(ast) = source.ast.as_ref() { - let mut polkavm_messages = Source::get_messages(ast, suppressed_warnings); - for message in polkavm_messages.iter_mut() { - message.push_contract_path(path.as_str()); - } - messages.extend(polkavm_messages); - } - } - self.errors = match self.errors.take() { - Some(mut errors) => { - errors.extend(messages); - Some(errors) - } - None => Some(messages), - }; - - Ok(()) - } -} diff --git a/crates/solidity/src/test_utils.rs b/crates/solidity/src/test_utils.rs index 5bc7131..4879f30 100644 --- a/crates/solidity/src/test_utils.rs +++ b/crates/solidity/src/test_utils.rs @@ -7,17 +7,17 @@ use std::sync::Mutex; use once_cell::sync::Lazy; use revive_llvm_context::OptimizerSettings; +use revive_solc_json_interface::standard_json::output::contract::evm::bytecode::Bytecode; +use revive_solc_json_interface::standard_json::output::contract::evm::bytecode::DeployedBytecode; +use revive_solc_json_interface::warning::Warning; +use revive_solc_json_interface::SolcStandardJsonInput; +use revive_solc_json_interface::SolcStandardJsonInputSettingsOptimizer; +use revive_solc_json_interface::SolcStandardJsonInputSettingsSelection; +use revive_solc_json_interface::SolcStandardJsonOutput; use crate::project::Project; use crate::solc::solc_compiler::SolcCompiler; -use crate::solc::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer; -use crate::solc::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection; -use crate::solc::standard_json::input::Input as SolcStandardJsonInput; -use crate::solc::standard_json::output::contract::evm::bytecode::Bytecode; -use crate::solc::standard_json::output::contract::evm::bytecode::DeployedBytecode; -use crate::solc::standard_json::output::Output as SolcStandardJsonOutput; use crate::solc::Compiler; -use crate::warning::Warning; static PVM_BLOB_CACHE: Lazy>>> = Lazy::new(Default::default); static EVM_BLOB_CACHE: Lazy>>> = Lazy::new(Default::default); @@ -98,7 +98,13 @@ pub fn build_solidity_with_options( let mut output = solc.standard_json(input, None, vec![], None)?; - let project = output.try_to_project(sources, libraries, &solc_version, &DEBUG_CONFIG)?; + let project = Project::try_from_standard_json_output( + &output, + sources, + libraries, + &solc_version, + &DEBUG_CONFIG, + )?; let build: crate::Build = project.compile(optimizer_settings, false, DEBUG_CONFIG)?; build.write_to_standard_json(&mut output, &solc_version)?; @@ -188,7 +194,13 @@ pub fn build_solidity_and_detect_missing_libraries( let mut output = solc.standard_json(input, None, vec![], None)?; - let project = output.try_to_project(sources, libraries, &solc_version, &DEBUG_CONFIG)?; + let project = Project::try_from_standard_json_output( + &output, + sources, + libraries, + &solc_version, + &DEBUG_CONFIG, + )?; let missing_libraries = project.get_missing_libraries(); missing_libraries.write_to_standard_json(&mut output, &solc.version()?)?; diff --git a/crates/solidity/src/tests/messages.rs b/crates/solidity/src/tests/messages.rs index d0ce0af..eb8fae3 100644 --- a/crates/solidity/src/tests/messages.rs +++ b/crates/solidity/src/tests/messages.rs @@ -4,7 +4,7 @@ use std::collections::BTreeMap; -use crate::warning::Warning; +use revive_solc_json_interface::warning::Warning; pub const ECRECOVER_TEST_SOURCE: &str = r#" // SPDX-License-Identifier: MIT