factor out solc JSON interface crate (#264)

The differential testing framework will make a second consumer. There
seems to be no re-usable Rust crate for this. But we already have
everything here, just needs a small refactor to make it fully re-usable.

- Mostly decouple the solc JSON-input-output interface types from the
`solidity` frontend crate
- Expose the JSON-input-output interface types in a dedicated crate

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
xermicus
2025-03-20 17:11:40 +01:00
committed by GitHub
parent 36ea69b50f
commit 497dae2494
45 changed files with 328 additions and 245 deletions
+64
View File
@@ -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<String, String>,
libraries: BTreeMap<String, BTreeMap<String, String>>,
solc_version: &SolcVersion,
debug_config: &revive_llvm_context::DebugConfig,
) -> anyhow::Result<Self> {
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 {