mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-17 14:41:04 +00:00
@@ -0,0 +1,361 @@
|
||||
//! Solidity to PolkaVM compiler arguments.
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use path_slash::PathExt;
|
||||
use structopt::StructOpt;
|
||||
|
||||
/// Compiles the provided Solidity input files (or use the standard input if no files
|
||||
/// are given or "-" is specified as a file name). Outputs the components based on the
|
||||
/// chosen options, either to the standard output or to files within the designated
|
||||
/// output directory.
|
||||
/// Example: resolc ERC20.sol -O3 --bin --output-dir './build/'
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(name = "The PolkaVM Solidity compiler")]
|
||||
pub struct Arguments {
|
||||
/// Print the version and exit.
|
||||
#[structopt(long = "version")]
|
||||
pub version: bool,
|
||||
|
||||
/// Specify the input paths and remappings.
|
||||
/// If an argument contains a '=', it is considered a remapping.
|
||||
/// Multiple Solidity files can be passed in the default Solidity mode.
|
||||
/// Yul, LLVM IR, and PolkaVM Assembly modes currently support only a single file.
|
||||
pub inputs: Vec<String>,
|
||||
|
||||
/// Set the given path as the root of the source tree instead of the root of the filesystem.
|
||||
/// Passed to `solc` without changes.
|
||||
#[structopt(long = "base-path")]
|
||||
pub base_path: Option<String>,
|
||||
|
||||
/// Make an additional source directory available to the default import callback.
|
||||
/// Can be used multiple times. Can only be used if the base path has a non-empty value.
|
||||
/// Passed to `solc` without changes.
|
||||
#[structopt(long = "include-path")]
|
||||
pub include_paths: Vec<String>,
|
||||
|
||||
/// Allow a given path for imports. A list of paths can be supplied by separating them with a comma.
|
||||
/// Passed to `solc` without changes.
|
||||
#[structopt(long = "allow-paths")]
|
||||
pub allow_paths: Option<String>,
|
||||
|
||||
/// Create one file per component and contract/file at the specified directory, if given.
|
||||
#[structopt(short = "o", long = "output-dir")]
|
||||
pub output_directory: Option<PathBuf>,
|
||||
|
||||
/// Overwrite existing files (used together with -o).
|
||||
#[structopt(long = "overwrite")]
|
||||
pub overwrite: bool,
|
||||
|
||||
/// Set the optimization parameter -O[0 | 1 | 2 | 3 | s | z].
|
||||
/// Use `3` for best performance and `z` for minimal size.
|
||||
#[structopt(short = "O", long = "optimization")]
|
||||
pub optimization: Option<char>,
|
||||
|
||||
/// Try to recompile with -Oz if the bytecode is too large.
|
||||
#[structopt(long = "fallback-Oz")]
|
||||
pub fallback_to_optimizing_for_size: bool,
|
||||
|
||||
/// Disable the system request memoization.
|
||||
#[structopt(long = "disable-system-request-memoization")]
|
||||
pub disable_system_request_memoization: bool,
|
||||
|
||||
/// Disable the `solc` optimizer.
|
||||
/// Use it if your project uses the `MSIZE` instruction, or in other cases.
|
||||
/// Beware that it will prevent libraries from being inlined.
|
||||
#[structopt(long = "disable-solc-optimizer")]
|
||||
pub disable_solc_optimizer: bool,
|
||||
|
||||
/// Specify the path to the `solc` executable. By default, the one in `${PATH}` is used.
|
||||
/// Yul mode: `solc` is used for source code validation, as `resolc` itself assumes that the input Yul is valid.
|
||||
/// LLVM IR mode: `solc` is unused.
|
||||
#[structopt(long = "solc")]
|
||||
pub solc: Option<String>,
|
||||
|
||||
/// The EVM target version to generate IR for.
|
||||
/// See https://github.com/xermicus/revive/blob/main/crates/common/src/evm_version.rs for reference.
|
||||
#[structopt(long = "evm-version")]
|
||||
pub evm_version: Option<String>,
|
||||
|
||||
/// Specify addresses of deployable libraries. Syntax: `<libraryName>=<address> [, or whitespace] ...`.
|
||||
/// Addresses are interpreted as hexadecimal strings prefixed with `0x`.
|
||||
#[structopt(short = "l", long = "libraries")]
|
||||
pub libraries: Vec<String>,
|
||||
|
||||
/// Output a single JSON document containing the specified information.
|
||||
/// Available arguments: `abi`, `hashes`, `metadata`, `devdoc`, `userdoc`, `storage-layout`, `ast`, `asm`, `bin`, `bin-runtime`.
|
||||
#[structopt(long = "combined-json")]
|
||||
pub combined_json: Option<String>,
|
||||
|
||||
/// Switch to standard JSON input/output mode. Read from stdin, write the result to stdout.
|
||||
/// This is the default used by the Hardhat plugin.
|
||||
#[structopt(long = "standard-json")]
|
||||
pub standard_json: bool,
|
||||
|
||||
/// Switch to missing deployable libraries detection mode.
|
||||
/// Only available for standard JSON input/output mode.
|
||||
/// Contracts are not compiled in this mode, and all compilation artifacts are not included.
|
||||
#[structopt(long = "detect-missing-libraries")]
|
||||
pub detect_missing_libraries: bool,
|
||||
|
||||
/// Switch to Yul mode.
|
||||
/// Only one input Yul file is allowed.
|
||||
/// Cannot be used with combined and standard JSON modes.
|
||||
#[structopt(long = "yul")]
|
||||
pub yul: bool,
|
||||
|
||||
/// Switch to LLVM IR mode.
|
||||
/// Only one input LLVM IR file is allowed.
|
||||
/// Cannot be used with combined and standard JSON modes.
|
||||
/// Use this mode at your own risk, as LLVM IR input validation is not implemented.
|
||||
#[structopt(long = "llvm-ir")]
|
||||
pub llvm_ir: bool,
|
||||
|
||||
/// Forcibly switch to EVM legacy assembly pipeline.
|
||||
/// It is useful for older revisions of `solc` 0.8, where Yul was considered highly experimental
|
||||
/// and contained more bugs than today.
|
||||
#[structopt(long = "force-evmla")]
|
||||
pub force_evmla: bool,
|
||||
|
||||
/// Enable system contract compilation mode.
|
||||
/// In this mode PolkaVM extensions are enabled. For example, calls to addresses `0xFFFF` and below
|
||||
/// are substituted by special PolkaVM instructions.
|
||||
/// In the Yul mode, the `verbatim_*` instruction family is available.
|
||||
#[structopt(long = "system-mode")]
|
||||
pub is_system_mode: bool,
|
||||
|
||||
/// Set metadata hash mode.
|
||||
/// The only supported value is `none` that disables appending the metadata hash.
|
||||
/// Is enabled by default.
|
||||
#[structopt(long = "metadata-hash")]
|
||||
pub metadata_hash: Option<String>,
|
||||
|
||||
/// Output PolkaVM assembly of the contracts.
|
||||
#[structopt(long = "asm")]
|
||||
pub output_assembly: bool,
|
||||
|
||||
/// Output PolkaVM bytecode of the contracts.
|
||||
#[structopt(long = "bin")]
|
||||
pub output_binary: bool,
|
||||
|
||||
/// Suppress specified warnings.
|
||||
/// Available arguments: `ecrecover`, `sendtransfer`, `extcodesize`, `txorigin`, `blocktimestamp`, `blocknumber`, `blockhash`.
|
||||
#[structopt(long = "suppress-warnings")]
|
||||
pub suppress_warnings: Option<Vec<String>>,
|
||||
|
||||
/// Dump all IRs to files in the specified directory.
|
||||
/// Only for testing and debugging.
|
||||
#[structopt(long = "debug-output-dir")]
|
||||
pub debug_output_directory: Option<PathBuf>,
|
||||
|
||||
/// Set the verify-each option in LLVM.
|
||||
/// Only for testing and debugging.
|
||||
#[structopt(long = "llvm-verify-each")]
|
||||
pub llvm_verify_each: bool,
|
||||
|
||||
/// Set the debug-logging option in LLVM.
|
||||
/// Only for testing and debugging.
|
||||
#[structopt(long = "llvm-debug-logging")]
|
||||
pub llvm_debug_logging: bool,
|
||||
|
||||
/// Run this process recursively and provide JSON input to compile a single contract.
|
||||
/// Only for usage from within the compiler.
|
||||
#[structopt(long = "recursive-process")]
|
||||
pub recursive_process: bool,
|
||||
}
|
||||
|
||||
impl Default for Arguments {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Arguments {
|
||||
/// A shortcut constructor.
|
||||
pub fn new() -> Self {
|
||||
Self::from_args()
|
||||
}
|
||||
|
||||
/// Validates the arguments.
|
||||
#[allow(clippy::collapsible_if)]
|
||||
pub fn validate(&self) -> anyhow::Result<()> {
|
||||
if self.version && std::env::args().count() > 2 {
|
||||
anyhow::bail!("No other options are allowed while getting the compiler version.");
|
||||
}
|
||||
|
||||
if self.recursive_process && std::env::args().count() > 2 {
|
||||
anyhow::bail!("No other options are allowed in recursive mode.");
|
||||
}
|
||||
|
||||
let modes_count = [
|
||||
self.yul,
|
||||
self.llvm_ir,
|
||||
self.combined_json.is_some(),
|
||||
self.standard_json,
|
||||
]
|
||||
.iter()
|
||||
.filter(|&&x| x)
|
||||
.count();
|
||||
if modes_count > 1 {
|
||||
anyhow::bail!("Only one modes is allowed at the same time: Yul, LLVM IR, PolkaVM assembly, combined JSON, standard JSON.");
|
||||
}
|
||||
|
||||
if self.yul || self.llvm_ir {
|
||||
if self.base_path.is_some() {
|
||||
anyhow::bail!(
|
||||
"`base-path` is not used in Yul, LLVM IR and PolkaVM assembly modes."
|
||||
);
|
||||
}
|
||||
if !self.include_paths.is_empty() {
|
||||
anyhow::bail!(
|
||||
"`include-paths` is not used in Yul, LLVM IR and PolkaVM assembly modes."
|
||||
);
|
||||
}
|
||||
if self.allow_paths.is_some() {
|
||||
anyhow::bail!(
|
||||
"`allow-paths` is not used in Yul, LLVM IR and PolkaVM assembly modes."
|
||||
);
|
||||
}
|
||||
if !self.libraries.is_empty() {
|
||||
anyhow::bail!(
|
||||
"Libraries are not supported in Yul, LLVM IR and PolkaVM assembly modes."
|
||||
);
|
||||
}
|
||||
|
||||
if self.evm_version.is_some() {
|
||||
anyhow::bail!(
|
||||
"`evm-version` is not used in Yul, LLVM IR and PolkaVM assembly modes."
|
||||
);
|
||||
}
|
||||
|
||||
if self.force_evmla {
|
||||
anyhow::bail!("EVM legacy assembly mode is not supported in Yul, LLVM IR and PolkaVM assembly modes.");
|
||||
}
|
||||
|
||||
if self.disable_solc_optimizer {
|
||||
anyhow::bail!("Disabling the solc optimizer is not supported in Yul, LLVM IR and PolkaVM assembly modes.");
|
||||
}
|
||||
}
|
||||
|
||||
if self.llvm_ir {
|
||||
if self.solc.is_some() {
|
||||
anyhow::bail!("`solc` is not used in LLVM IR and PolkaVM assembly modes.");
|
||||
}
|
||||
|
||||
if self.is_system_mode {
|
||||
anyhow::bail!(
|
||||
"System contract mode is not supported in LLVM IR and PolkaVM assembly modes."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if self.combined_json.is_some() {
|
||||
if self.output_assembly || self.output_binary {
|
||||
anyhow::bail!(
|
||||
"Cannot output assembly or binary outside of JSON in combined JSON mode."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if self.standard_json {
|
||||
if self.output_assembly || self.output_binary {
|
||||
anyhow::bail!(
|
||||
"Cannot output assembly or binary outside of JSON in standard JSON mode."
|
||||
);
|
||||
}
|
||||
|
||||
if !self.inputs.is_empty() {
|
||||
anyhow::bail!("Input files must be passed via standard JSON input.");
|
||||
}
|
||||
if !self.libraries.is_empty() {
|
||||
anyhow::bail!("Libraries must be passed via standard JSON input.");
|
||||
}
|
||||
if self.evm_version.is_some() {
|
||||
anyhow::bail!("EVM version must be passed via standard JSON input.");
|
||||
}
|
||||
|
||||
if self.output_directory.is_some() {
|
||||
anyhow::bail!("Output directory cannot be used in standard JSON mode.");
|
||||
}
|
||||
if self.overwrite {
|
||||
anyhow::bail!("Overwriting flag cannot be used in standard JSON mode.");
|
||||
}
|
||||
if self.disable_solc_optimizer {
|
||||
anyhow::bail!(
|
||||
"Disabling the solc optimizer must specified in standard JSON input settings."
|
||||
);
|
||||
}
|
||||
if self.optimization.is_some() {
|
||||
anyhow::bail!("LLVM optimizations must specified in standard JSON input settings.");
|
||||
}
|
||||
if self.fallback_to_optimizing_for_size {
|
||||
anyhow::bail!(
|
||||
"Falling back to -Oz must specified in standard JSON input settings."
|
||||
);
|
||||
}
|
||||
if self.disable_system_request_memoization {
|
||||
anyhow::bail!(
|
||||
"Disabling the system request memoization must specified in standard JSON input settings."
|
||||
);
|
||||
}
|
||||
if self.metadata_hash.is_some() {
|
||||
anyhow::bail!("Metadata hash mode must specified in standard JSON input settings.");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns remappings from input paths.
|
||||
pub fn split_input_files_and_remappings(
|
||||
&self,
|
||||
) -> anyhow::Result<(Vec<PathBuf>, Option<BTreeSet<String>>)> {
|
||||
let mut input_files = Vec::with_capacity(self.inputs.len());
|
||||
let mut remappings = BTreeSet::new();
|
||||
|
||||
for input in self.inputs.iter() {
|
||||
if input.contains('=') {
|
||||
let mut parts = Vec::with_capacity(2);
|
||||
for path in input.trim().split('=') {
|
||||
let path = PathBuf::from(path);
|
||||
parts.push(
|
||||
Self::path_to_posix(path.as_path())?
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
if parts.len() != 2 {
|
||||
anyhow::bail!(
|
||||
"Invalid remapping `{}`: expected two parts separated by '='",
|
||||
input
|
||||
);
|
||||
}
|
||||
remappings.insert(parts.join("="));
|
||||
} else {
|
||||
let path = PathBuf::from(input.trim());
|
||||
let path = Self::path_to_posix(path.as_path())?;
|
||||
input_files.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
let remappings = if remappings.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(remappings)
|
||||
};
|
||||
|
||||
Ok((input_files, remappings))
|
||||
}
|
||||
|
||||
/// Normalizes an input path by converting it to POSIX format.
|
||||
fn path_to_posix(path: &Path) -> anyhow::Result<PathBuf> {
|
||||
let path = path
|
||||
.to_slash()
|
||||
.ok_or_else(|| anyhow::anyhow!("Input path {:?} POSIX conversion error", path))?
|
||||
.to_string();
|
||||
let path = PathBuf::from(path.as_str());
|
||||
Ok(path)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
//! Solidity to PolkaVM compiler binary.
|
||||
|
||||
pub mod arguments;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use self::arguments::Arguments;
|
||||
|
||||
/// The rayon worker stack size.
|
||||
const RAYON_WORKER_STACK_SIZE: usize = 16 * 1024 * 1024;
|
||||
|
||||
#[cfg(target_env = "musl")]
|
||||
#[global_allocator]
|
||||
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||
|
||||
/// The application entry point.
|
||||
fn main() {
|
||||
std::process::exit(match main_inner() {
|
||||
Ok(()) => revive_common::EXIT_CODE_SUCCESS,
|
||||
Err(error) => {
|
||||
eprintln!("{error}");
|
||||
revive_common::EXIT_CODE_FAILURE
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// The auxiliary `main` function to facilitate the `?` error conversion operator.
|
||||
fn main_inner() -> anyhow::Result<()> {
|
||||
let arguments = Arguments::new();
|
||||
arguments.validate()?;
|
||||
|
||||
if arguments.version {
|
||||
println!(
|
||||
"{} v{} (LLVM build {:?})",
|
||||
env!("CARGO_PKG_DESCRIPTION"),
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
inkwell::support::get_llvm_version()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
rayon::ThreadPoolBuilder::new()
|
||||
.stack_size(RAYON_WORKER_STACK_SIZE)
|
||||
.build_global()
|
||||
.expect("Thread pool configuration failure");
|
||||
inkwell::support::enable_llvm_pretty_stack_trace();
|
||||
revive_llvm_context::initialize_target(revive_llvm_context::Target::PVM); // TODO: pass from CLI
|
||||
|
||||
if arguments.recursive_process {
|
||||
return revive_solidity::run_process();
|
||||
}
|
||||
|
||||
let debug_config = match arguments.debug_output_directory {
|
||||
Some(ref debug_output_directory) => {
|
||||
std::fs::create_dir_all(debug_output_directory.as_path())?;
|
||||
Some(revive_llvm_context::DebugConfig::new(
|
||||
debug_output_directory.to_owned(),
|
||||
))
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let (input_files, remappings) = arguments.split_input_files_and_remappings()?;
|
||||
|
||||
let suppressed_warnings = match arguments.suppress_warnings {
|
||||
Some(warnings) => Some(revive_solidity::Warning::try_from_strings(
|
||||
warnings.as_slice(),
|
||||
)?),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let mut solc = revive_solidity::SolcCompiler::new(
|
||||
arguments
|
||||
.solc
|
||||
.unwrap_or_else(|| revive_solidity::SolcCompiler::DEFAULT_EXECUTABLE_NAME.to_owned()),
|
||||
)?;
|
||||
|
||||
let evm_version = match arguments.evm_version {
|
||||
Some(evm_version) => Some(revive_common::EVMVersion::try_from(evm_version.as_str())?),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let mut optimizer_settings = match arguments.optimization {
|
||||
Some(mode) => revive_llvm_context::OptimizerSettings::try_from_cli(mode)?,
|
||||
None => revive_llvm_context::OptimizerSettings::cycles(),
|
||||
};
|
||||
if arguments.fallback_to_optimizing_for_size {
|
||||
optimizer_settings.enable_fallback_to_size();
|
||||
}
|
||||
if arguments.disable_system_request_memoization {
|
||||
optimizer_settings.disable_system_request_memoization();
|
||||
}
|
||||
optimizer_settings.is_verify_each_enabled = arguments.llvm_verify_each;
|
||||
optimizer_settings.is_debug_logging_enabled = arguments.llvm_debug_logging;
|
||||
|
||||
let include_metadata_hash = match arguments.metadata_hash {
|
||||
Some(metadata_hash) => {
|
||||
let metadata =
|
||||
revive_llvm_context::PolkaVMMetadataHash::from_str(metadata_hash.as_str())?;
|
||||
metadata != revive_llvm_context::PolkaVMMetadataHash::None
|
||||
}
|
||||
None => true,
|
||||
};
|
||||
|
||||
let build = if arguments.yul {
|
||||
revive_solidity::yul(
|
||||
input_files.as_slice(),
|
||||
&mut solc,
|
||||
optimizer_settings,
|
||||
arguments.is_system_mode,
|
||||
include_metadata_hash,
|
||||
debug_config,
|
||||
)
|
||||
} else if arguments.llvm_ir {
|
||||
revive_solidity::llvm_ir(
|
||||
input_files.as_slice(),
|
||||
optimizer_settings,
|
||||
arguments.is_system_mode,
|
||||
include_metadata_hash,
|
||||
debug_config,
|
||||
)
|
||||
} else if arguments.standard_json {
|
||||
revive_solidity::standard_json(
|
||||
&mut solc,
|
||||
arguments.detect_missing_libraries,
|
||||
arguments.force_evmla,
|
||||
arguments.is_system_mode,
|
||||
arguments.base_path,
|
||||
arguments.include_paths,
|
||||
arguments.allow_paths,
|
||||
debug_config,
|
||||
)?;
|
||||
return Ok(());
|
||||
} else if let Some(format) = arguments.combined_json {
|
||||
revive_solidity::combined_json(
|
||||
format,
|
||||
input_files.as_slice(),
|
||||
arguments.libraries,
|
||||
&mut solc,
|
||||
evm_version,
|
||||
!arguments.disable_solc_optimizer,
|
||||
optimizer_settings,
|
||||
arguments.force_evmla,
|
||||
arguments.is_system_mode,
|
||||
include_metadata_hash,
|
||||
arguments.base_path,
|
||||
arguments.include_paths,
|
||||
arguments.allow_paths,
|
||||
remappings,
|
||||
suppressed_warnings,
|
||||
debug_config,
|
||||
arguments.output_directory,
|
||||
arguments.overwrite,
|
||||
)?;
|
||||
return Ok(());
|
||||
} else {
|
||||
revive_solidity::standard_output(
|
||||
input_files.as_slice(),
|
||||
arguments.libraries,
|
||||
&mut solc,
|
||||
evm_version,
|
||||
!arguments.disable_solc_optimizer,
|
||||
optimizer_settings,
|
||||
arguments.force_evmla,
|
||||
arguments.is_system_mode,
|
||||
include_metadata_hash,
|
||||
arguments.base_path,
|
||||
arguments.include_paths,
|
||||
arguments.allow_paths,
|
||||
remappings,
|
||||
suppressed_warnings,
|
||||
debug_config,
|
||||
)
|
||||
}?;
|
||||
|
||||
if let Some(output_directory) = arguments.output_directory {
|
||||
std::fs::create_dir_all(&output_directory)?;
|
||||
|
||||
build.write_to_directory(
|
||||
&output_directory,
|
||||
arguments.output_assembly,
|
||||
arguments.output_binary,
|
||||
arguments.overwrite,
|
||||
)?;
|
||||
|
||||
eprintln!(
|
||||
"Compiler run successful. Artifact(s) can be found in directory {output_directory:?}."
|
||||
);
|
||||
} else if arguments.output_assembly || arguments.output_binary {
|
||||
for (path, contract) in build.contracts.into_iter() {
|
||||
if arguments.output_assembly {
|
||||
let assembly_text = contract.build.assembly_text;
|
||||
|
||||
println!("Contract `{}` assembly:\n\n{}", path, assembly_text);
|
||||
}
|
||||
if arguments.output_binary {
|
||||
println!(
|
||||
"Contract `{}` bytecode: 0x{}",
|
||||
path,
|
||||
hex::encode(contract.build.bytecode)
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("Compiler run successful. No output requested. Use --asm and --bin flags.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user