resolc crate (#328)

- Factor the YUL crate out of `revive-solidity`.
- `revive-solidity` is in reality not a Solidity implementation but the
revive solidity compiler driver (`resolc`). By renaming we not only get
this straight but also a binary with the same name as the crate which
should be less confusing.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
This commit is contained in:
xermicus authored and GitHub committed 2025-05-27 09:48:43 +02:00
1 parent 090e3ac13c
commit bd4e108bb0
99 files changed
+599 -624

No files matched your search

+155
View File
@@ -0,0 +1,155 @@
//! The Solidity contract build.
use std::collections::HashSet;
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;
/// The Solidity contract build.
#[derive(Debug, Serialize, Deserialize)]
pub struct Contract {
/// The contract path.
pub path: String,
/// The auxiliary identifier. Used to identify Yul objects.
pub identifier: String,
/// The LLVM module build.
pub build: revive_llvm_context::PolkaVMBuild,
/// The metadata JSON.
pub metadata_json: serde_json::Value,
/// The factory dependencies.
pub factory_dependencies: HashSet<String>,
}
impl Contract {
/// A shortcut constructor.
pub fn new(
path: String,
identifier: String,
build: revive_llvm_context::PolkaVMBuild,
metadata_json: serde_json::Value,
factory_dependencies: HashSet<String>,
) -> Self {
Self {
path,
identifier,
build,
metadata_json,
factory_dependencies,
}
}
/// Writes the contract text assembly and bytecode to files.
pub fn write_to_directory(
self,
path: &Path,
output_assembly: bool,
output_binary: bool,
overwrite: bool,
) -> anyhow::Result<()> {
let file_name = Self::short_path(self.path.as_str());
if output_assembly {
let file_name = format!(
"{}.{}",
file_name,
revive_common::EXTENSION_POLKAVM_ASSEMBLY
);
let mut file_path = path.to_owned();
file_path.push(file_name);
if file_path.exists() && !overwrite {
anyhow::bail!(
"Refusing to overwrite an existing file {file_path:?} (use --overwrite to force)."
);
} else {
let assembly_text = self.build.assembly_text;
File::create(&file_path)
.map_err(|error| {
anyhow::anyhow!("File {:?} creating error: {}", file_path, error)
})?
.write_all(assembly_text.as_bytes())
.map_err(|error| {
anyhow::anyhow!("File {:?} writing error: {}", file_path, error)
})?;
}
}
if output_binary {
let file_name = format!("{}.{}", file_name, revive_common::EXTENSION_POLKAVM_BINARY);
let mut file_path = path.to_owned();
file_path.push(file_name);
if file_path.exists() && !overwrite {
anyhow::bail!(
"Refusing to overwrite an existing file {file_path:?} (use --overwrite to force)."
);
} else {
File::create(&file_path)
.map_err(|error| {
anyhow::anyhow!("File {:?} creating error: {}", file_path, error)
})?
.write_all(self.build.bytecode.as_slice())
.map_err(|error| {
anyhow::anyhow!("File {:?} writing error: {}", file_path, error)
})?;
}
}
Ok(())
}
/// Writes the contract text assembly and bytecode to the combined JSON.
pub fn write_to_combined_json(
self,
combined_json_contract: &mut CombinedJsonContract,
) -> anyhow::Result<()> {
if let Some(metadata) = combined_json_contract.metadata.as_mut() {
*metadata = self.metadata_json.to_string();
}
if let Some(asm) = combined_json_contract.asm.as_mut() {
*asm = serde_json::Value::String(self.build.assembly_text);
}
let hexadecimal_bytecode = hex::encode(self.build.bytecode);
combined_json_contract.bin = Some(hexadecimal_bytecode);
combined_json_contract
.bin_runtime
.clone_from(&combined_json_contract.bin);
combined_json_contract.factory_deps = Some(self.build.factory_dependencies);
Ok(())
}
/// Writes the contract text assembly and bytecode to the standard JSON.
pub fn write_to_standard_json(
self,
standard_json_contract: &mut SolcStandardJsonOutputContract,
) -> anyhow::Result<()> {
standard_json_contract.metadata = Some(self.metadata_json);
let assembly_text = self.build.assembly_text;
let bytecode = hex::encode(self.build.bytecode.as_slice());
if let Some(evm) = standard_json_contract.evm.as_mut() {
evm.modify(assembly_text, bytecode);
}
standard_json_contract.factory_dependencies = Some(self.build.factory_dependencies);
standard_json_contract.hash = Some(self.build.bytecode_hash);
Ok(())
}
/// Converts the full path to a short one.
pub fn short_path(path: &str) -> &str {
path.rfind('/')
.map(|last_slash| &path[last_slash + 1..])
.unwrap_or_else(|| path)
}
}
+94
View File
@@ -0,0 +1,94 @@
//! The Solidity project build.
pub mod contract;
use std::collections::BTreeMap;
use std::path::Path;
use revive_solc_json_interface::combined_json::CombinedJson;
use revive_solc_json_interface::SolcStandardJsonOutput;
use crate::solc::version::Version as SolcVersion;
use crate::ResolcVersion;
use self::contract::Contract;
/// The Solidity project build.
#[derive(Debug, Default)]
pub struct Build {
/// The contract data,
pub contracts: BTreeMap<String, Contract>,
}
impl Build {
/// Writes all contracts to the specified directory.
pub fn write_to_directory(
self,
output_directory: &Path,
output_assembly: bool,
output_binary: bool,
overwrite: bool,
) -> anyhow::Result<()> {
for (_path, contract) in self.contracts.into_iter() {
contract.write_to_directory(
output_directory,
output_assembly,
output_binary,
overwrite,
)?;
}
Ok(())
}
/// Writes all contracts assembly and bytecode to the combined JSON.
pub fn write_to_combined_json(self, combined_json: &mut CombinedJson) -> anyhow::Result<()> {
for (path, contract) in self.contracts.into_iter() {
let combined_json_contract = combined_json
.contracts
.iter_mut()
.find_map(|(json_path, contract)| {
if path.ends_with(json_path) {
Some(contract)
} else {
None
}
})
.ok_or_else(|| anyhow::anyhow!("Contract `{}` not found in the project", path))?;
contract.write_to_combined_json(combined_json_contract)?;
}
combined_json.revive_version = Some(ResolcVersion::default().long);
Ok(())
}
/// Writes all contracts assembly and bytecode to the standard JSON.
pub fn write_to_standard_json(
mut self,
standard_json: &mut SolcStandardJsonOutput,
solc_version: &SolcVersion,
) -> anyhow::Result<()> {
let contracts = match standard_json.contracts.as_mut() {
Some(contracts) => contracts,
None => return Ok(()),
};
for (path, contracts) in contracts.iter_mut() {
for (name, contract) in contracts.iter_mut() {
let full_name = format!("{path}:{name}");
if let Some(contract_data) = self.contracts.remove(full_name.as_str()) {
contract_data.write_to_standard_json(contract)?;
}
}
}
standard_json.version = Some(solc_version.default.to_string());
standard_json.long_version = Some(solc_version.long.to_owned());
standard_json.revive_version = Some(ResolcVersion::default().long);
Ok(())
}
}