Separate compilation and linker phases (#376)

Separate between compilation and linker phases to allow deploy time
linking and back-porting era compiler changes to fix #91. Unlinked
contract binaries (caused by missing libraries or missing factory
dependencies in turn) are emitted as raw ELF object.

Few drive by fixes:
- #98
- A compiler panic on missing libraries definitions.
- Fixes some incosistent type forwarding in JSON output (empty string
vs. null object).
- Remove the unused fallback for size optimization setting.
- Remove the broken `--lvm-ir`  mode.
- CI workflow fixes.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
Signed-off-by: xermicus <bigcyrill@hotmail.com>
Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
xermicus
2025-09-27 20:52:22 +02:00
committed by GitHub
parent 13faedf08a
commit 94ec34c4d5
169 changed files with 6288 additions and 5206 deletions
@@ -1,29 +1,39 @@
//! The `solc --standard-json` input.
use std::collections::BTreeMap;
#[cfg(feature = "resolc")]
use std::collections::BTreeSet;
#[cfg(feature = "resolc")]
use std::path::Path;
#[cfg(feature = "resolc")]
use std::path::PathBuf;
#[cfg(all(feature = "parallel", feature = "resolc"))]
use rayon::iter::{IntoParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
use serde::Deserialize;
use serde::Serialize;
#[cfg(feature = "resolc")]
use crate::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata;
#[cfg(feature = "resolc")]
use crate::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
#[cfg(feature = "resolc")]
use crate::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection;
#[cfg(feature = "resolc")]
use crate::SolcStandardJsonInputSettingsLibraries;
#[cfg(feature = "resolc")]
use crate::SolcStandardJsonInputSettingsPolkaVM;
use self::language::Language;
#[cfg(feature = "resolc")]
use self::settings::warning::Warning;
use self::settings::Settings;
use self::source::Source;
pub mod language;
pub mod settings;
pub mod source;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
use std::path::PathBuf;
#[cfg(all(feature = "parallel", feature = "resolc"))]
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use serde::Deserialize;
use serde::Serialize;
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 crate::SolcStandardJsonInputSettingsPolkaVM;
use self::language::Language;
use self::settings::Settings;
use self::source::Source;
/// The `solc --standard-json` input.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -36,93 +46,88 @@ pub struct Input {
pub settings: Settings,
/// The suppressed warnings.
#[cfg(feature = "resolc")]
#[serde(skip_serializing)]
pub suppressed_warnings: Option<Vec<Warning>>,
#[serde(default, skip_serializing)]
pub suppressed_warnings: Vec<Warning>,
}
#[cfg(feature = "resolc")]
impl Input {
/// A shortcut constructor from stdin.
pub fn try_from_stdin() -> anyhow::Result<Self> {
let mut input: Self = serde_json::from_reader(std::io::BufReader::new(std::io::stdin()))?;
input
.settings
.output_selection
.get_or_insert_with(SolcStandardJsonInputSettingsSelection::default)
.extend_with_required();
Ok(input)
/// A shortcut constructor.
///
/// If the `path` is `None`, the input is read from the stdin.
pub fn try_from(path: Option<&Path>) -> anyhow::Result<Self> {
let input_json = match path {
Some(path) => std::fs::read_to_string(path)
.map_err(|error| anyhow::anyhow!("Standard JSON file {path:?} reading: {error}")),
None => std::io::read_to_string(std::io::stdin())
.map_err(|error| anyhow::anyhow!("Standard JSON reading from stdin: {error}")),
}?;
revive_common::deserialize_from_str::<Self>(input_json.as_str())
.map_err(|error| anyhow::anyhow!("Standard JSON parsing: {error}"))
}
/// A shortcut constructor from paths.
#[allow(clippy::too_many_arguments)]
pub fn try_from_paths(
language: Language,
pub fn try_from_solidity_paths(
evm_version: Option<revive_common::EVMVersion>,
paths: &[PathBuf],
library_map: Vec<String>,
remappings: Option<BTreeSet<String>>,
libraries: &[String],
remappings: BTreeSet<String>,
output_selection: SolcStandardJsonInputSettingsSelection,
optimizer: SolcStandardJsonInputSettingsOptimizer,
metadata: Option<SolcStandardJsonInputSettingsMetadata>,
#[cfg(feature = "resolc")] suppressed_warnings: Option<Vec<Warning>>,
polkavm: Option<SolcStandardJsonInputSettingsPolkaVM>,
metadata: SolcStandardJsonInputSettingsMetadata,
suppressed_warnings: Vec<Warning>,
polkavm: SolcStandardJsonInputSettingsPolkaVM,
llvm_arguments: Vec<String>,
detect_missing_libraries: bool,
) -> anyhow::Result<Self> {
let mut paths: BTreeSet<PathBuf> = paths.iter().cloned().collect();
let libraries = Settings::parse_libraries(library_map)?;
for library_file in libraries.keys() {
let libraries = SolcStandardJsonInputSettingsLibraries::try_from(libraries)?;
for library_file in libraries.as_inner().keys() {
paths.insert(PathBuf::from(library_file));
}
let sources = paths
.iter()
.map(|path| {
let source = Source::try_from(path.as_path()).unwrap_or_else(|error| {
panic!("Source code file {path:?} reading error: {error}")
});
(path.to_string_lossy().to_string(), source)
})
.collect();
#[cfg(feature = "parallel")]
let iter = paths.into_par_iter(); // Parallel iterator
#[cfg(not(feature = "parallel"))]
let iter = paths.into_iter(); // Sequential iterator
Ok(Self {
language,
let sources = iter
.map(|path| {
let source = Source::try_read(path.as_path())?;
Ok((path.to_string_lossy().to_string(), source))
})
.collect::<anyhow::Result<BTreeMap<String, Source>>>()?;
Self::try_from_solidity_sources(
evm_version,
sources,
settings: Settings::new(
evm_version,
libraries,
remappings,
output_selection,
optimizer,
metadata,
polkavm,
),
#[cfg(feature = "resolc")]
libraries,
remappings,
output_selection,
optimizer,
metadata,
suppressed_warnings,
})
polkavm,
llvm_arguments,
detect_missing_libraries,
)
}
/// 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(
pub fn try_from_solidity_sources(
evm_version: Option<revive_common::EVMVersion>,
sources: BTreeMap<String, String>,
libraries: BTreeMap<String, BTreeMap<String, String>>,
remappings: Option<BTreeSet<String>>,
sources: BTreeMap<String, Source>,
libraries: SolcStandardJsonInputSettingsLibraries,
remappings: BTreeSet<String>,
output_selection: SolcStandardJsonInputSettingsSelection,
optimizer: SolcStandardJsonInputSettingsOptimizer,
metadata: Option<SolcStandardJsonInputSettingsMetadata>,
suppressed_warnings: Option<Vec<Warning>>,
polkavm: Option<SolcStandardJsonInputSettingsPolkaVM>,
metadata: SolcStandardJsonInputSettingsMetadata,
suppressed_warnings: Vec<Warning>,
polkavm: SolcStandardJsonInputSettingsPolkaVM,
llvm_arguments: Vec<String>,
detect_missing_libraries: bool,
) -> anyhow::Result<Self> {
#[cfg(feature = "parallel")]
let iter = sources.into_par_iter(); // Parallel iterator
#[cfg(not(feature = "parallel"))]
let iter = sources.into_iter(); // Sequential iterator
let sources = iter
.map(|(path, content)| (path, Source::from(content)))
.collect();
Ok(Self {
language: Language::Solidity,
sources,
@@ -134,13 +139,75 @@ impl Input {
optimizer,
metadata,
polkavm,
suppressed_warnings.clone(),
llvm_arguments,
detect_missing_libraries,
),
suppressed_warnings,
})
}
/// Sets the necessary defaults.
pub fn normalize(&mut self) {
self.settings.normalize();
/// A shortcut constructor from paths to Yul source files.
pub fn from_yul_paths(
paths: &[PathBuf],
libraries: SolcStandardJsonInputSettingsLibraries,
optimizer: SolcStandardJsonInputSettingsOptimizer,
llvm_options: Vec<String>,
) -> Self {
let sources = paths
.iter()
.map(|path| {
(
path.to_string_lossy().to_string(),
Source::from(path.as_path()),
)
})
.collect();
Self::from_yul_sources(sources, libraries, optimizer, llvm_options)
}
/// A shortcut constructor from Yul source code.
pub fn from_yul_sources(
sources: BTreeMap<String, Source>,
libraries: SolcStandardJsonInputSettingsLibraries,
optimizer: SolcStandardJsonInputSettingsOptimizer,
llvm_arguments: Vec<String>,
) -> Self {
let output_selection = SolcStandardJsonInputSettingsSelection::new_yul_validation();
Self {
language: Language::Yul,
sources,
settings: Settings::new(
None,
libraries,
Default::default(),
output_selection,
optimizer,
Default::default(),
Default::default(),
vec![],
llvm_arguments,
false,
),
suppressed_warnings: vec![],
}
}
/// Extends the output selection with another one.
pub fn extend_selection(&mut self, selection: SolcStandardJsonInputSettingsSelection) {
self.settings.extend_selection(selection);
}
/// Tries to resolve all sources.
pub fn resolve_sources(&mut self) {
#[cfg(feature = "parallel")]
let iter = self.sources.par_iter_mut();
#[cfg(not(feature = "parallel"))]
let iter = self.sources.iter_mut();
iter.for_each(|(_path, source)| {
let _ = source.try_resolve();
});
}
}