mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-10-24 04:35:07 +00:00
In `--yul` mode, resolc now properly exits when solc reports validation errors (e.g., invalid hex literals, variable redeclarations), matching `--standard-json behavior.` `runs_without_input_file` was adapted because it was relying on it where the new behavior correctly matches solc --strict-assembly. Closes #477 Co-authored-by: minimax-m2.5 <neo@zion> --------- Signed-off-by: xermicus <bigcyrill@hotmail.com>
399 lines
13 KiB
Rust
399 lines
13 KiB
Rust
//! Solidity to PolkaVM compiler library.
|
|
|
|
#![allow(clippy::too_many_arguments)]
|
|
|
|
use std::collections::BTreeMap;
|
|
use std::collections::BTreeSet;
|
|
use std::collections::HashSet;
|
|
use std::io::Write;
|
|
use std::path::PathBuf;
|
|
|
|
#[cfg(feature = "parallel")]
|
|
use rayon::iter::IntoParallelIterator;
|
|
#[cfg(feature = "parallel")]
|
|
use rayon::iter::ParallelIterator;
|
|
use revive_common::EVMVersion;
|
|
use revive_common::MetadataHash;
|
|
use revive_common::EXIT_CODE_SUCCESS;
|
|
use revive_llvm_context::DebugConfig;
|
|
use revive_llvm_context::OptimizerSettings;
|
|
use revive_solc_json_interface::CombinedJsonSelector;
|
|
use revive_solc_json_interface::ResolcWarning;
|
|
use revive_solc_json_interface::SolcStandardJsonInput;
|
|
use revive_solc_json_interface::SolcStandardJsonInputLanguage;
|
|
use revive_solc_json_interface::SolcStandardJsonInputSettingsLibraries;
|
|
use revive_solc_json_interface::SolcStandardJsonInputSettingsOptimizer;
|
|
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVM;
|
|
use revive_solc_json_interface::SolcStandardJsonInputSettingsPolkaVMMemory;
|
|
use revive_solc_json_interface::SolcStandardJsonInputSettingsSelection;
|
|
use revive_solc_json_interface::SolcStandardJsonOutputError;
|
|
use revive_solc_json_interface::SolcStandardJsonOutputErrorHandler;
|
|
|
|
use crate::linker::Output;
|
|
|
|
pub use self::build::contract::Contract as ContractBuild;
|
|
pub use self::build::Build;
|
|
pub use self::missing_libraries::MissingLibraries;
|
|
pub use self::process::input::Input as ProcessInput;
|
|
#[cfg(not(target_os = "emscripten"))]
|
|
pub use self::process::native_process::NativeProcess;
|
|
pub use self::process::output::Output as ProcessOutput;
|
|
#[cfg(target_os = "emscripten")]
|
|
pub use self::process::worker_process::WorkerProcess;
|
|
pub use self::process::Process;
|
|
pub use self::project::contract::Contract as ProjectContract;
|
|
pub use self::project::Project;
|
|
pub use self::r#const::*;
|
|
#[cfg(not(target_os = "emscripten"))]
|
|
pub use self::solc::solc_compiler::SolcCompiler;
|
|
#[cfg(target_os = "emscripten")]
|
|
pub use self::solc::soljson_compiler::SoljsonCompiler;
|
|
pub use self::solc::version::Version as SolcVersion;
|
|
pub use self::solc::Compiler;
|
|
pub use self::solc::FIRST_SUPPORTED_VERSION as SolcFirstSupportedVersion;
|
|
pub use self::solc::LAST_SUPPORTED_VERSION as SolcLastSupportedVersion;
|
|
pub use self::version::Version as ResolcVersion;
|
|
|
|
pub(crate) mod build;
|
|
#[cfg(not(target_os = "emscripten"))]
|
|
pub mod cli_utils;
|
|
pub(crate) mod r#const;
|
|
pub(crate) mod linker;
|
|
pub(crate) mod missing_libraries;
|
|
pub(crate) mod process;
|
|
pub(crate) mod project;
|
|
pub(crate) mod solc;
|
|
#[cfg(not(target_os = "emscripten"))]
|
|
pub mod test_utils;
|
|
pub mod tests;
|
|
pub(crate) mod version;
|
|
|
|
/// The rayon worker stack size.
|
|
pub const RAYON_WORKER_STACK_SIZE: usize = 64 * 1024 * 1024;
|
|
|
|
/// Runs the Yul mode.
|
|
pub fn yul<T: Compiler>(
|
|
solc: &T,
|
|
input_files: &[PathBuf],
|
|
libraries: &[String],
|
|
metadata_hash: MetadataHash,
|
|
messages: &mut Vec<SolcStandardJsonOutputError>,
|
|
optimizer_settings: OptimizerSettings,
|
|
debug_config: DebugConfig,
|
|
llvm_arguments: &[String],
|
|
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
|
|
) -> anyhow::Result<Build> {
|
|
let libraries = SolcStandardJsonInputSettingsLibraries::try_from(libraries)?;
|
|
let solc_output = solc.validate_yul_paths(input_files, libraries.clone(), messages)?;
|
|
solc_output.exit_on_error();
|
|
|
|
let linker_symbols = libraries.as_linker_symbols()?;
|
|
let project = Project::try_from_yul_paths(input_files, None, libraries, &debug_config)?;
|
|
let mut build = project.compile(
|
|
messages,
|
|
optimizer_settings,
|
|
metadata_hash,
|
|
&debug_config,
|
|
llvm_arguments,
|
|
memory_config,
|
|
)?;
|
|
build.take_and_write_warnings();
|
|
build.check_errors()?;
|
|
|
|
let mut build = build.link(linker_symbols, &debug_config);
|
|
build.take_and_write_warnings();
|
|
build.check_errors()?;
|
|
Ok(build)
|
|
}
|
|
|
|
/// Runs the standard output mode.
|
|
pub fn standard_output<T: Compiler>(
|
|
solc: &T,
|
|
input_files: &[PathBuf],
|
|
libraries: &[String],
|
|
metadata_hash: MetadataHash,
|
|
messages: &mut Vec<SolcStandardJsonOutputError>,
|
|
evm_version: Option<EVMVersion>,
|
|
solc_optimizer_enabled: bool,
|
|
optimizer_settings: OptimizerSettings,
|
|
base_path: Option<String>,
|
|
include_paths: Vec<String>,
|
|
allow_paths: Option<String>,
|
|
remappings: BTreeSet<String>,
|
|
suppressed_warnings: Vec<ResolcWarning>,
|
|
debug_config: DebugConfig,
|
|
llvm_arguments: Vec<String>,
|
|
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
|
|
) -> anyhow::Result<Build> {
|
|
let solc_version = solc.version()?;
|
|
let mut solc_input = SolcStandardJsonInput::try_from_solidity_paths(
|
|
evm_version,
|
|
input_files,
|
|
libraries,
|
|
remappings,
|
|
SolcStandardJsonInputSettingsSelection::new_required_for_codegen_all(),
|
|
SolcStandardJsonInputSettingsOptimizer::new(
|
|
solc_optimizer_enabled,
|
|
SolcStandardJsonInputSettingsOptimizer::default_mode(),
|
|
Default::default(),
|
|
),
|
|
Default::default(),
|
|
suppressed_warnings,
|
|
SolcStandardJsonInputSettingsPolkaVM::new(
|
|
Some(memory_config),
|
|
debug_config.emit_debug_info,
|
|
),
|
|
llvm_arguments,
|
|
false,
|
|
)?;
|
|
let mut solc_output = solc.standard_json(
|
|
&mut solc_input,
|
|
messages,
|
|
base_path,
|
|
include_paths,
|
|
allow_paths,
|
|
)?;
|
|
solc_output.take_and_write_warnings();
|
|
solc_output.check_errors()?;
|
|
|
|
let linker_symbols = solc_input.settings.libraries.as_linker_symbols()?;
|
|
|
|
let project = Project::try_from_standard_json_output(
|
|
&mut solc_output,
|
|
solc_input.settings.libraries,
|
|
&solc_version,
|
|
&debug_config,
|
|
)?;
|
|
solc_output.take_and_write_warnings();
|
|
solc_output.check_errors()?;
|
|
|
|
let mut build = project.compile(
|
|
messages,
|
|
optimizer_settings,
|
|
metadata_hash,
|
|
&debug_config,
|
|
&solc_input.settings.llvm_arguments,
|
|
memory_config,
|
|
)?;
|
|
build.take_and_write_warnings();
|
|
build.check_errors()?;
|
|
|
|
let mut build = build.link(linker_symbols, &debug_config);
|
|
build.take_and_write_warnings();
|
|
build.check_errors()?;
|
|
|
|
Ok(build)
|
|
}
|
|
|
|
/// Runs the standard JSON mode.
|
|
pub fn standard_json<T: Compiler>(
|
|
solc: &T,
|
|
metadata_hash: MetadataHash,
|
|
messages: &mut Vec<SolcStandardJsonOutputError>,
|
|
json_path: Option<PathBuf>,
|
|
base_path: Option<String>,
|
|
include_paths: Vec<String>,
|
|
allow_paths: Option<String>,
|
|
mut debug_config: DebugConfig,
|
|
detect_missing_libraries: bool,
|
|
) -> anyhow::Result<()> {
|
|
let solc_version = solc.version()?;
|
|
let mut solc_input = SolcStandardJsonInput::try_from(json_path.as_deref())?;
|
|
let language = solc_input.language;
|
|
let prune_output = solc_input.settings.selection_to_prune();
|
|
let deployed_libraries = solc_input.settings.libraries.as_paths();
|
|
let linker_symbols = solc_input.settings.libraries.as_linker_symbols()?;
|
|
let optimizer_settings = OptimizerSettings::try_from_cli(solc_input.settings.optimizer.mode)?;
|
|
let detect_missing_libraries =
|
|
solc_input.settings.detect_missing_libraries || detect_missing_libraries;
|
|
debug_config.emit_debug_info = solc_input
|
|
.settings
|
|
.polkavm
|
|
.debug_information
|
|
.unwrap_or(false);
|
|
|
|
solc_input.extend_selection(
|
|
SolcStandardJsonInputSettingsSelection::new_required_for_codegen(
|
|
&solc_input.settings.output_selection,
|
|
),
|
|
);
|
|
solc_input.retain_output_selection();
|
|
|
|
let mut solc_output = solc.standard_json(
|
|
&mut solc_input,
|
|
messages,
|
|
base_path,
|
|
include_paths,
|
|
allow_paths,
|
|
)?;
|
|
|
|
if language == SolcStandardJsonInputLanguage::Yul {
|
|
let solc_output = solc.validate_yul_standard_json(&mut solc_input, messages)?;
|
|
if solc_output.has_errors() {
|
|
solc_output.write_and_exit(prune_output);
|
|
}
|
|
}
|
|
|
|
let project = Project::try_from_standard_json_output(
|
|
&mut solc_output,
|
|
solc_input.settings.libraries,
|
|
&solc_version,
|
|
&debug_config,
|
|
)?;
|
|
|
|
if solc_output.has_errors() {
|
|
solc_output.write_and_exit(prune_output);
|
|
}
|
|
|
|
if detect_missing_libraries {
|
|
let missing_libraries = project.get_missing_libraries(&deployed_libraries);
|
|
missing_libraries.write_to_standard_json(&mut solc_output, &solc_version);
|
|
solc_output.write_and_exit(prune_output);
|
|
}
|
|
|
|
let build = project.compile(
|
|
messages,
|
|
optimizer_settings,
|
|
metadata_hash,
|
|
&debug_config,
|
|
&solc_input.settings.llvm_arguments,
|
|
solc_input
|
|
.settings
|
|
.polkavm
|
|
.memory_config
|
|
.unwrap_or_default(),
|
|
)?;
|
|
if build.has_errors() {
|
|
build.write_to_standard_json(&mut solc_output, &solc_version)?;
|
|
solc_output.write_and_exit(prune_output);
|
|
}
|
|
|
|
let build = build.link(linker_symbols, &debug_config);
|
|
build.write_to_standard_json(&mut solc_output, &solc_version)?;
|
|
solc_output.write_and_exit(prune_output);
|
|
}
|
|
|
|
/// Runs the combined JSON mode.
|
|
pub fn combined_json<T: Compiler>(
|
|
solc: &T,
|
|
paths: &[PathBuf],
|
|
libraries: &[String],
|
|
metadata_hash: MetadataHash,
|
|
messages: &mut Vec<SolcStandardJsonOutputError>,
|
|
evm_version: Option<EVMVersion>,
|
|
format: String,
|
|
solc_optimizer_enabled: bool,
|
|
optimizer_settings: OptimizerSettings,
|
|
base_path: Option<String>,
|
|
include_paths: Vec<String>,
|
|
allow_paths: Option<String>,
|
|
remappings: BTreeSet<String>,
|
|
suppressed_warnings: Vec<ResolcWarning>,
|
|
debug_config: DebugConfig,
|
|
output_directory: Option<PathBuf>,
|
|
overwrite: bool,
|
|
llvm_arguments: Vec<String>,
|
|
memory_config: SolcStandardJsonInputSettingsPolkaVMMemory,
|
|
) -> anyhow::Result<()> {
|
|
let selectors = CombinedJsonSelector::from_cli(format.as_str())
|
|
.into_iter()
|
|
.filter_map(|result| match result {
|
|
Ok(selector) => Some(selector),
|
|
Err(error) => {
|
|
messages.push(SolcStandardJsonOutputError::new_error(
|
|
error.to_string(),
|
|
None,
|
|
None,
|
|
));
|
|
None
|
|
}
|
|
})
|
|
.collect::<HashSet<_>>();
|
|
if !selectors.contains(&CombinedJsonSelector::Bytecode) {
|
|
messages.push(SolcStandardJsonOutputError::new_warning(
|
|
"Bytecode is always emitted even if the selector is not provided.".to_string(),
|
|
None,
|
|
None,
|
|
));
|
|
}
|
|
if selectors.contains(&CombinedJsonSelector::BytecodeRuntime) {
|
|
messages.push(SolcStandardJsonOutputError::new_warning(
|
|
format!("The `{}` selector does not make sense for the PVM target, since there is only one bytecode segment.", CombinedJsonSelector::BytecodeRuntime),
|
|
None,
|
|
None,
|
|
));
|
|
}
|
|
|
|
let mut combined_json = solc.combined_json(paths, selectors)?;
|
|
standard_output(
|
|
solc,
|
|
paths,
|
|
libraries,
|
|
metadata_hash,
|
|
messages,
|
|
evm_version,
|
|
solc_optimizer_enabled,
|
|
optimizer_settings,
|
|
base_path,
|
|
include_paths,
|
|
allow_paths,
|
|
remappings,
|
|
suppressed_warnings,
|
|
debug_config,
|
|
llvm_arguments,
|
|
memory_config,
|
|
)?
|
|
.write_to_combined_json(&mut combined_json)?;
|
|
|
|
match output_directory {
|
|
Some(output_directory) => {
|
|
std::fs::create_dir_all(output_directory.as_path())?;
|
|
combined_json.write_to_directory(output_directory.as_path(), overwrite)?;
|
|
|
|
writeln!(
|
|
std::io::stderr(),
|
|
"Compiler run successful. Artifact(s) can be found in directory {output_directory:?}."
|
|
)?;
|
|
}
|
|
None => {
|
|
serde_json::to_writer(std::io::stdout(), &combined_json)?;
|
|
}
|
|
}
|
|
std::process::exit(EXIT_CODE_SUCCESS);
|
|
}
|
|
|
|
/// Links unlinked bytecode files.
|
|
pub fn link(paths: Vec<String>, libraries: Vec<String>) -> anyhow::Result<()> {
|
|
#[cfg(feature = "parallel")]
|
|
let iter = paths.into_par_iter();
|
|
#[cfg(not(feature = "parallel"))]
|
|
let iter = paths.into_iter();
|
|
|
|
let bytecodes = iter
|
|
.map(|path| {
|
|
let bytecode = std::fs::read(path.as_str())?;
|
|
Ok((path, bytecode))
|
|
})
|
|
.collect::<anyhow::Result<BTreeMap<String, Vec<u8>>>>()?;
|
|
|
|
let output = Output::try_from(&bytecodes, &libraries)?;
|
|
|
|
#[cfg(feature = "parallel")]
|
|
let iter = output.linked.into_par_iter();
|
|
#[cfg(not(feature = "parallel"))]
|
|
let iter = output.linked.into_iter();
|
|
|
|
iter.map(|(path, bytecode)| {
|
|
std::fs::write(path, bytecode)?;
|
|
Ok(())
|
|
})
|
|
.collect::<anyhow::Result<()>>()?;
|
|
|
|
for (path, _) in output.unlinked {
|
|
println!("Warning: file '{path}' still unresolved");
|
|
}
|
|
println!("Linking completed");
|
|
|
|
std::process::exit(EXIT_CODE_SUCCESS);
|
|
}
|