Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

Commit

Permalink
feat(solc): yul compilation (#994)
Browse files Browse the repository at this point in the history
* added yul compilation

* fix doctest

* fixes

* changelog

* cleanup

* fmt

* test update

* lint

* ci fix

Co-authored-by: Georgios Konstantopoulos <[email protected]>
  • Loading branch information
alexeuler and gakonst authored Mar 10, 2022
1 parent 37c25df commit e1d66b8
Show file tree
Hide file tree
Showing 8 changed files with 118 additions and 38 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
- Add a getter to `ProjectCompileOutput` that returns a mapping of compiler
versions to a vector of name + contract struct tuples
[#908](https://github.com/gakonst/ethers-rs/pull/908)
- Add Yul compilation [994](https://github.com/gakonst/ethers-rs/pull/994)
- Enforce commutativity of ENS reverse resolution
[#996](https://github.com/gakonst/ethers-rs/pull/996)

Expand Down
42 changes: 33 additions & 9 deletions ethers-solc/src/artifacts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,37 @@ pub struct CompilerInput {

impl CompilerInput {
/// Reads all contracts found under the path
pub fn new(path: impl AsRef<Path>) -> Result<Self, SolcIoError> {
pub fn new(path: impl AsRef<Path>) -> Result<Vec<Self>, SolcIoError> {
Source::read_all_from(path.as_ref()).map(Self::with_sources)
}

/// Creates a new Compiler input with default settings and the given sources
pub fn with_sources(sources: Sources) -> Self {
Self { language: "Solidity".to_string(), sources, settings: Default::default() }
pub fn with_sources(sources: Sources) -> Vec<Self> {
let mut solidity_sources = BTreeMap::new();
let mut yul_sources = BTreeMap::new();
for (path, source) in sources {
if path.extension() == Some(std::ffi::OsStr::new("yul")) {
yul_sources.insert(path, source);
} else {
solidity_sources.insert(path, source);
}
}
let mut res = Vec::new();
if !solidity_sources.is_empty() {
res.push(Self {
language: "Solidity".to_string(),
sources: solidity_sources,
settings: Default::default(),
});
}
if !yul_sources.is_empty() {
res.push(Self {
language: "Yul".to_string(),
sources: yul_sources,
settings: Default::default(),
});
}
res
}

/// Sets the settings for compilation
Expand Down Expand Up @@ -99,12 +123,6 @@ impl CompilerInput {
}
}

impl Default for CompilerInput {
fn default() -> Self {
Self::with_sources(Default::default())
}
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Settings {
Expand Down Expand Up @@ -805,6 +823,12 @@ impl CompilerOutput {
err.source_location.as_ref().map(|s| files.contains(s.file.as_str())).unwrap_or(true)
});
}

pub fn merge(&mut self, other: CompilerOutput) {
self.errors.extend(other.errors);
self.contracts.extend(other.contracts);
self.sources.extend(other.sources);
}
}

/// A wrapper helper type for the `Contracts` type alias
Expand Down
13 changes: 9 additions & 4 deletions ethers-solc/src/compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,12 @@ impl Solc {
/// Convenience function for compiling all sources under the given path
pub fn compile_source(&self, path: impl AsRef<Path>) -> Result<CompilerOutput> {
let path = path.as_ref();
self.compile(&CompilerInput::new(path)?)
let mut res: CompilerOutput = Default::default();
for input in CompilerInput::new(path)? {
let output = self.compile(&input)?;
res.merge(output)
}
Ok(res)
}

/// Same as [`Self::compile()`], but only returns those files which are included in the
Expand All @@ -425,7 +430,7 @@ impl Solc {
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use ethers_solc::{CompilerInput, Solc};
/// let solc = Solc::default();
/// let input = CompilerInput::new("./contracts")?;
/// let input = CompilerInput::new("./contracts")?[0].clone();
/// let output = solc.compile_exact(&input)?;
/// # Ok(())
/// # }
Expand Down Expand Up @@ -571,8 +576,8 @@ impl Solc {
/// use ethers_solc::{CompilerInput, Solc};
/// let solc1 = Solc::default();
/// let solc2 = Solc::default();
/// let input1 = CompilerInput::new("contracts").unwrap();
/// let input2 = CompilerInput::new("src").unwrap();
/// let input1 = CompilerInput::new("contracts").unwrap()[0].clone();
/// let input2 = CompilerInput::new("src").unwrap()[0].clone();
///
/// let outputs = Solc::compile_many([(solc1, input1), (solc2, input2)], 2).await.flattened().unwrap();
/// # }
Expand Down
48 changes: 24 additions & 24 deletions ethers-solc/src/compile/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,24 +341,23 @@ fn compile_sequential(
solc.args
);

let input = CompilerInput::with_sources(sources)
.settings(settings.clone())
.normalize_evm_version(&version)
.with_remappings(paths.remappings.clone());

tracing::trace!(
"calling solc `{}` with {} sources {:?}",
version,
input.sources.len(),
input.sources.keys()
);

report::solc_spawn(&solc, &version, &input);
let output = solc.compile_exact(&input)?;
report::solc_success(&solc, &version, &output);
tracing::trace!("compiled input, output has error: {}", output.has_error());

aggregated.extend(version, output);
for input in CompilerInput::with_sources(sources) {
let input = input
.settings(settings.clone())
.normalize_evm_version(&version)
.with_remappings(paths.remappings.clone());
tracing::trace!(
"calling solc `{}` with {} sources {:?}",
version,
input.sources.len(),
input.sources.keys()
);
report::solc_spawn(&solc, &version, &input);
let output = solc.compile_exact(&input)?;
report::solc_success(&solc, &version, &output);
tracing::trace!("compiled input, output has error: {}", output.has_error());
aggregated.extend(version.clone(), output);
}
}
Ok(aggregated)
}
Expand All @@ -383,13 +382,14 @@ fn compile_parallel(
// nothing to compile
continue
}
for input in CompilerInput::with_sources(sources) {
let job = input
.settings(settings.clone())
.normalize_evm_version(&version)
.with_remappings(paths.remappings.clone());

let job = CompilerInput::with_sources(sources)
.settings(settings.clone())
.normalize_evm_version(&version)
.with_remappings(paths.remappings.clone());

jobs.push((solc, version, job))
jobs.push((solc.clone(), version.clone(), job))
}
}

// start a rayon threadpool that will execute all `Solc::compile()` processes
Expand Down
4 changes: 3 additions & 1 deletion ethers-solc/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ pub fn source_files(root: impl AsRef<Path>) -> Vec<PathBuf> {
.into_iter()
.filter_map(Result::ok)
.filter(|e| e.file_type().is_file())
.filter(|e| e.path().extension().map(|ext| ext == "sol").unwrap_or_default())
.filter(|e| {
e.path().extension().map(|ext| (ext == "sol") || (ext == "yul")).unwrap_or_default()
})
.map(|e| e.path().into())
.collect()
}
Expand Down
7 changes: 7 additions & 0 deletions ethers-solc/test-data/yul-sample/Dapp.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.6.6;

contract Dapp {

function modified() public {}
}
11 changes: 11 additions & 0 deletions ethers-solc/test-data/yul-sample/SimpleStore.yul
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
object "SimpleStore" {
code {
datacopy(0, dataoffset("Runtime"), datasize("Runtime"))
return(0, datasize("Runtime"))
}
object "Runtime" {
code {
calldatacopy(0, 0, 36) // write calldata to memory
}
}
}
30 changes: 30 additions & 0 deletions ethers-solc/tests/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,36 @@ fn can_compile_dapp_sample() {
assert_eq!(cache, updated_cache);
}

#[test]
fn can_compile_yul_sample() {
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/yul-sample");
let paths = ProjectPathsConfig::builder().sources(root);
let project = TempProject::<ConfigurableArtifacts>::new(paths).unwrap();

let compiled = project.compile().unwrap();
assert!(compiled.find("Dapp").is_some());
assert!(compiled.find("SimpleStore").is_some());
assert!(!compiled.has_compiler_errors());

// nothing to compile
let compiled = project.compile().unwrap();
assert!(compiled.find("Dapp").is_some());
assert!(compiled.find("SimpleStore").is_some());
assert!(compiled.is_unchanged());

let cache = SolFilesCache::read(project.cache_path()).unwrap();

// delete artifacts
std::fs::remove_dir_all(&project.paths().artifacts).unwrap();
let compiled = project.compile().unwrap();
assert!(compiled.find("Dapp").is_some());
assert!(compiled.find("SimpleStore").is_some());
assert!(!compiled.is_unchanged());

let updated_cache = SolFilesCache::read(project.cache_path()).unwrap();
assert_eq!(cache, updated_cache);
}

#[test]
fn can_compile_configured() {
let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("test-data/dapp-sample");
Expand Down

0 comments on commit e1d66b8

Please sign in to comment.