forked from foundry-rs/compilers
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
1,314 additions
and
1 deletion.
There are no files selected for viewing
195 changes: 195 additions & 0 deletions
195
crates/compilers/src/compile/resolc/artifact_output/resolc_artifact_output.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
use std::{ | ||
borrow::{Borrow, Cow}, | ||
collections::BTreeMap, | ||
path::Path, | ||
}; | ||
|
||
use alloy_json_abi::{Constructor, Event, Function, JsonAbi}; | ||
use alloy_primitives::{hex, Bytes}; | ||
use foundry_compilers_artifacts::{ | ||
BytecodeObject, CompactBytecode, CompactContract, CompactContractBytecode, | ||
CompactContractBytecodeCow, CompactDeployedBytecode, Contract, SourceFile, | ||
}; | ||
use serde::{de::value, Deserialize, Serialize}; | ||
use serde_json::Error; | ||
use yansi::Paint; | ||
|
||
use crate::ArtifactOutput; | ||
|
||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] | ||
pub struct ResolcArtifactOutput(); | ||
|
||
#[derive(Debug, Serialize, Deserialize)] | ||
pub struct ResolcContractArtifact { | ||
artifact: revive_solidity::SolcStandardJsonOutput, | ||
} | ||
|
||
impl Default for ResolcContractArtifact { | ||
fn default() -> Self { | ||
Self { | ||
artifact: revive_solidity::SolcStandardJsonOutput { | ||
contracts: None, | ||
sources: None, | ||
errors: None, | ||
version: None, | ||
long_version: None, | ||
zk_version: None, | ||
}, | ||
} | ||
} | ||
} | ||
|
||
impl<'a> From<&'a ResolcContractArtifact> for CompactContractBytecodeCow<'a> { | ||
fn from(value: &'a ResolcContractArtifact) -> Self { | ||
let (standard_abi, compact_bytecode, compact_deployed_bytecode) = create_byte_code(value); | ||
|
||
Self { | ||
abi: Some(Cow::Owned(standard_abi)), | ||
bytecode: Some(Cow::Owned(compact_bytecode)), | ||
deployed_bytecode: Some(Cow::Owned(compact_deployed_bytecode)), | ||
} | ||
} | ||
} | ||
|
||
impl From<ResolcContractArtifact> for CompactContractBytecode { | ||
fn from(value: ResolcContractArtifact) -> Self { | ||
let (standard_abi, compact_bytecode, compact_deployed_bytecode) = create_byte_code(&value); | ||
Self { | ||
abi: Some(standard_abi), | ||
bytecode: Some(compact_bytecode), | ||
deployed_bytecode: Some(compact_deployed_bytecode), | ||
} | ||
} | ||
} | ||
|
||
impl From<ResolcContractArtifact> for CompactContract { | ||
fn from(value: ResolcContractArtifact) -> Self { | ||
// See https://docs.soliditylang.org/en/develop/abi-spec.html | ||
let (standard_abi, compact_bytecode, _) = create_byte_code(&value); | ||
Self { bin: Some(compact_bytecode.object.clone()), bin_runtime: Some(compact_bytecode.object), abi: Some(standard_abi) } | ||
} | ||
} | ||
|
||
impl ArtifactOutput for ResolcArtifactOutput { | ||
type Artifact = ResolcContractArtifact; | ||
|
||
fn contract_to_artifact( | ||
&self, | ||
_file: &std::path::Path, | ||
_name: &str, | ||
_contract: foundry_compilers_artifacts::Contract, | ||
_source_file: Option<&foundry_compilers_artifacts::SourceFile>, | ||
) -> Self::Artifact { | ||
todo!("Implement this if needed") | ||
} | ||
|
||
fn standalone_source_file_to_artifact( | ||
&self, | ||
_path: &std::path::Path, | ||
_file: &crate::sources::VersionedSourceFile, | ||
) -> Option<Self::Artifact> { | ||
None | ||
} | ||
} | ||
|
||
impl ResolcArtifactOutput { | ||
pub fn resolc_contract_to_artifact( | ||
&self, | ||
_file: &Path, | ||
_name: &str, | ||
contract: Contract, | ||
source_file: Option<&SourceFile>, | ||
) -> ResolcContractArtifact { | ||
/* let Contract { | ||
abi, | ||
metadata, | ||
userdoc, | ||
devdoc, | ||
ir, | ||
storage_layout, | ||
transient_storage_layout, | ||
evm, | ||
ewasm, | ||
ir_optimized, | ||
ir_optimized_ast, | ||
} = contract; | ||
let mut output = ResolcContractArtifact::default();*/ | ||
todo!("Implement this function converting standard json to revive json"); | ||
|
||
} | ||
} | ||
|
||
fn create_byte_code( | ||
value: &ResolcContractArtifact, | ||
) -> (JsonAbi, CompactBytecode, CompactDeployedBytecode) { | ||
let binding = value.artifact.contracts.clone().unwrap(); | ||
let parent_contract = | ||
binding.values().last().and_then(|inner_map| inner_map.values().next()).unwrap(); | ||
let abi_array: Vec<serde_json::Value> = | ||
serde_json::from_value(parent_contract.clone().abi.unwrap()).unwrap(); | ||
let mut standard_abi = JsonAbi { | ||
constructor: None, | ||
fallback: None, | ||
receive: None, | ||
functions: BTreeMap::new(), | ||
events: BTreeMap::new(), | ||
errors: BTreeMap::new(), | ||
}; | ||
|
||
for item in abi_array { | ||
match item["type"].as_str() { | ||
Some("constructor") => { | ||
standard_abi.constructor = serde_json::from_value(item).unwrap(); | ||
} | ||
Some("fallback") => { | ||
standard_abi.fallback = serde_json::from_value(item).unwrap(); | ||
} | ||
Some("receive") => { | ||
standard_abi.receive = serde_json::from_value(item).unwrap(); | ||
} | ||
Some("function") => { | ||
let function: Function = serde_json::from_value(item).unwrap(); | ||
standard_abi | ||
.functions | ||
.entry(function.name.clone()) | ||
.or_insert_with(Vec::new) | ||
.push(function); | ||
} | ||
Some("event") => { | ||
let event: Event = serde_json::from_value(item).unwrap(); | ||
standard_abi.events.entry(event.name.clone()).or_insert_with(Vec::new).push(event); | ||
} | ||
Some("error") => { | ||
let error: alloy_json_abi::Error = serde_json::from_value(item).unwrap(); | ||
standard_abi.errors.entry(error.name.clone()).or_insert_with(Vec::new).push(error); | ||
} | ||
_ => continue, | ||
} | ||
} | ||
|
||
let binding = parent_contract.evm.clone().unwrap().bytecode.unwrap(); | ||
let raw_bytecode = binding.object.as_str(); | ||
let binding = parent_contract.evm.clone().unwrap().deployed_bytecode.unwrap(); | ||
let raw_deployed_bytecode = binding.object.as_str(); | ||
|
||
let bytecode = BytecodeObject::Bytecode(Bytes::from(hex::decode(raw_bytecode).unwrap())); | ||
let deployed_bytecode = | ||
BytecodeObject::Bytecode(Bytes::from(hex::decode(raw_deployed_bytecode).unwrap())); | ||
|
||
let compact_bytecode = CompactBytecode { | ||
object: bytecode, | ||
source_map: None, | ||
link_references: BTreeMap::default(), | ||
}; | ||
let compact_bytecode_deployed = CompactBytecode { | ||
object: deployed_bytecode, | ||
source_map: None, | ||
link_references: BTreeMap::default(), | ||
}; | ||
let compact_deployed_bytecode = CompactDeployedBytecode { | ||
bytecode: Some(compact_bytecode_deployed), | ||
immutable_references: BTreeMap::default(), | ||
}; | ||
|
||
(standard_abi, compact_bytecode, compact_deployed_bytecode) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
pub mod project; | ||
pub mod artifact_output; |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
use foundry_compilers_artifacts::{CompilerOutput, Error, SolcLanguage}; | ||
use foundry_compilers_core::error::{Result, SolcError}; | ||
use semver::Version; | ||
use serde::Serialize; | ||
use std::{ | ||
path::{Path, PathBuf}, | ||
process::{Command, Output, Stdio}, | ||
str::FromStr, | ||
}; | ||
|
||
use crate::{compilers, resolver::parse::SolData, Compiler, CompilerVersion}; | ||
|
||
use super::{ResolcInput, ResolcSettings, ResolcVersionedInput}; | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct Resolc { | ||
pub resolc: PathBuf, | ||
pub extra_args: Vec<String>, | ||
} | ||
|
||
impl Compiler for Resolc { | ||
type Input = ResolcVersionedInput; | ||
type CompilationError = Error; | ||
type ParsedSource = SolData; | ||
type Settings = ResolcSettings; | ||
type Language = SolcLanguage; | ||
|
||
fn available_versions(&self, _language: &Self::Language) -> Vec<CompilerVersion> { | ||
let compiler = revive_solidity::SolcCompiler::new( | ||
revive_solidity::SolcCompiler::DEFAULT_EXECUTABLE_NAME.to_owned(), | ||
) | ||
.unwrap(); | ||
let mut versions = Vec::new(); | ||
versions.push(CompilerVersion::Remote(compiler.version.unwrap().default)); | ||
versions | ||
} | ||
|
||
fn compile( | ||
&self, | ||
_input: &Self::Input, | ||
) -> Result< | ||
compilers::CompilerOutput<foundry_compilers_artifacts::Error>, | ||
foundry_compilers_core::error::SolcError, | ||
> { | ||
panic!("`Compiler::compile` not supported for `Resolc`, should call Resolc::compile()"); | ||
} | ||
} | ||
|
||
impl Resolc { | ||
pub fn new(path: PathBuf) -> Result<Self> { | ||
Ok(Self { resolc: path, extra_args: Vec::new() }) | ||
} | ||
|
||
pub fn compile(&self, input: &ResolcInput) -> Result<CompilerOutput> { | ||
match self.compile_output::<ResolcInput>(input) { | ||
Ok(results) => { | ||
let output = std::str::from_utf8(&results).map_err(|_| SolcError::InvalidUtf8)?; | ||
serde_json::from_str(output).map_err(|e| SolcError::msg(e.to_string())) | ||
} | ||
Err(_) => Ok(CompilerOutput::default()), | ||
} | ||
} | ||
|
||
pub fn compile_output<T: Serialize>(&self, input: &ResolcInput) -> Result<Vec<u8>> { | ||
let mut cmd = self.configure_cmd(); | ||
println!("input: {:?}\n\n", input.clone()); | ||
let mut child = cmd.spawn().map_err(|err| SolcError::io(err, &self.resolc))?; | ||
|
||
let stdin = child.stdin.as_mut().unwrap(); | ||
serde_json::to_writer(stdin, input)?; | ||
|
||
let output = child.wait_with_output().map_err(|err| SolcError::io(err, &self.resolc))?; | ||
|
||
compile_output(output) | ||
} | ||
|
||
fn configure_cmd(&self) -> Command { | ||
let mut cmd = Command::new(&self.resolc); | ||
cmd.stdin(Stdio::piped()).stderr(Stdio::piped()).stdout(Stdio::piped()); | ||
cmd.args(&self.extra_args); | ||
cmd.arg("--standard-json"); | ||
cmd | ||
} | ||
|
||
pub fn get_version_for_path(path: &Path) -> Result<Version> { | ||
let mut cmd = Command::new(path); | ||
cmd.arg("--version").stdin(Stdio::piped()).stderr(Stdio::piped()).stdout(Stdio::piped()); | ||
debug!(?cmd, "getting Resolc version"); | ||
let output = cmd.output().map_err(map_io_err(path))?; | ||
trace!(?output); | ||
let version = version_from_output(output)?; | ||
debug!(%version); | ||
Ok(version) | ||
} | ||
} | ||
|
||
fn map_io_err(path: &Path) -> impl FnOnce(std::io::Error) -> SolcError + '_ { | ||
move |err| SolcError::io(err, path) | ||
} | ||
|
||
fn version_from_output(output: Output) -> Result<Version> { | ||
if output.status.success() { | ||
let stdout = String::from_utf8_lossy(&output.stdout); | ||
let version = stdout | ||
.lines() | ||
.filter(|l| !l.trim().is_empty()) | ||
.last() | ||
.ok_or_else(|| SolcError::msg("Version not found in resolc output"))?; | ||
|
||
version | ||
.split_whitespace() | ||
.find_map(|s| { | ||
let trimmed = s.trim_start_matches('v'); | ||
Version::from_str(trimmed).ok() | ||
}) | ||
.ok_or_else(|| SolcError::msg("Unable to retrieve version from resolc output")) | ||
} else { | ||
Err(SolcError::solc_output(&output)) | ||
} | ||
} | ||
|
||
fn compile_output(output: Output) -> Result<Vec<u8>> { | ||
if output.status.success() { | ||
Ok(output.stdout) | ||
} else { | ||
Err(SolcError::solc_output(&output)) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::*; | ||
|
||
fn resolc_instance() -> Resolc { | ||
Resolc::new(PathBuf::from( | ||
revive_solidity::SolcCompiler::DEFAULT_EXECUTABLE_NAME.to_owned(), | ||
)) | ||
.unwrap() | ||
} | ||
|
||
#[test] | ||
fn resolc_version_works() { | ||
Resolc::get_version_for_path(&mut PathBuf::from( | ||
revive_solidity::SolcCompiler::DEFAULT_EXECUTABLE_NAME.to_owned(), | ||
)) | ||
.unwrap(); | ||
} | ||
|
||
#[test] | ||
fn resolc_compile_works() { | ||
let input = include_str!("../../../../../test-data/resolc/input/compile-input.json"); | ||
let input: ResolcInput = serde_json::from_str(input).unwrap(); | ||
let out = resolc_instance().compile(&input).unwrap(); | ||
println!("out: {:?}", out); | ||
assert!(!out.has_error()); | ||
} | ||
} |
Oops, something went wrong.