solidity: various small resolc fixes (#189)

This commit is contained in:
xermicus
2025-02-03 16:16:54 +01:00
committed by GitHub
parent bfda465c32
commit 8ed689e7ec
9 changed files with 84 additions and 76 deletions
+4
View File
@@ -8,10 +8,14 @@ This is a development pre-release.
- Support for the `coinbase` opcode.
### Changed
- Missing the `--overwrite` flag emits an error instead of a warning.
- The `resolc` executable prints the help by default.
- Removed support for legacy EVM assembly (EVMLA) translation.
### Fixed
- Solidity: Add the solc `--libraries` files to sources.
- A data race in tests.
- Fix `broken pipe` errors.
## v0.1.0-dev.9
+2 -2
View File
@@ -64,7 +64,7 @@ impl Contract {
file_path.push(file_name);
if file_path.exists() && !overwrite {
eprintln!(
anyhow::bail!(
"Refusing to overwrite an existing file {file_path:?} (use --overwrite to force)."
);
} else {
@@ -87,7 +87,7 @@ impl Contract {
file_path.push(file_name);
if file_path.exists() && !overwrite {
eprintln!(
anyhow::bail!(
"Refusing to overwrite an existing file {file_path:?} (use --overwrite to force)."
);
} else {
+5 -3
View File
@@ -51,6 +51,7 @@ pub mod test_utils;
pub mod tests;
use std::collections::BTreeSet;
use std::io::Write;
use std::path::PathBuf;
/// Runs the Yul mode.
@@ -161,7 +162,7 @@ pub fn standard_output<T: Compiler>(
has_errors = true;
}
eprintln!("{error}");
writeln!(std::io::stderr(), "{error}")?;
}
if has_errors {
@@ -278,10 +279,11 @@ pub fn combined_json<T: Compiler>(
combined_json.write_to_directory(output_directory.as_path(), overwrite)?;
}
None => {
println!(
writeln!(
std::io::stdout(),
"{}",
serde_json::to_string(&combined_json).expect("Always valid")
);
)?;
}
}
std::process::exit(0);
+29 -40
View File
@@ -13,14 +13,14 @@ use path_slash::PathExt;
/// output directory.
/// Example: resolc ERC20.sol -O3 --bin --output-dir './build/'
#[derive(Debug, Parser)]
#[structopt(name = "The PolkaVM Solidity compiler")]
#[command(name = "The PolkaVM Solidity compiler", arg_required_else_help = true)]
pub struct Arguments {
/// Print the version and exit.
#[structopt(long = "version")]
#[arg(long = "version")]
pub version: bool,
/// Print the licence and exit.
#[structopt(long = "license")]
#[arg(long = "license")]
pub license: bool,
/// Specify the input paths and remappings.
@@ -31,151 +31,140 @@ pub struct Arguments {
/// 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")]
#[arg(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")]
#[arg(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")]
#[arg(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")]
#[arg(short = 'o', long = "output-dir")]
pub output_directory: Option<PathBuf>,
/// Overwrite existing files (used together with -o).
#[structopt(long = "overwrite")]
#[arg(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")]
#[arg(short = 'O', long = "optimization")]
pub optimization: Option<char>,
/// Try to recompile with -Oz if the bytecode is too large.
#[structopt(long = "fallback-Oz")]
#[arg(long = "fallback-Oz")]
pub fallback_to_optimizing_for_size: 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")]
#[arg(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")]
#[arg(long = "solc")]
pub solc: Option<String>,
/// The EVM target version to generate IR for.
/// See https://github.com/paritytech/revive/blob/main/crates/common/src/evm_version.rs for reference.
#[structopt(long = "evm-version")]
#[arg(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")]
#[arg(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")]
#[arg(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")]
#[arg(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")]
#[arg(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")]
#[arg(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")]
#[arg(long = "llvm-ir")]
pub llvm_ir: 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")]
#[arg(long = "metadata-hash")]
pub metadata_hash: Option<String>,
/// Output PolkaVM assembly of the contracts.
#[structopt(long = "asm")]
#[arg(long = "asm")]
pub output_assembly: bool,
/// Output PolkaVM bytecode of the contracts.
#[structopt(long = "bin")]
#[arg(long = "bin")]
pub output_binary: bool,
/// Suppress specified warnings.
/// Available arguments: `ecrecover`, `sendtransfer`, `extcodesize`, `txorigin`, `blocktimestamp`, `blocknumber`, `blockhash`.
#[structopt(long = "suppress-warnings")]
#[arg(long = "suppress-warnings")]
pub suppress_warnings: Option<Vec<String>>,
/// Generate source based debug information in the output code file. This only has an effect
/// with the LLVM-IR code generator and is ignored otherwise.
#[structopt(short = 'g')]
#[arg(short = 'g')]
pub emit_source_debug_info: bool,
/// Dump all IRs to files in the specified directory.
/// Only for testing and debugging.
#[structopt(long = "debug-output-dir")]
#[arg(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")]
#[arg(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")]
#[arg(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")]
#[arg(long = "recursive-process")]
pub recursive_process: bool,
/// Specify the input file to use instead of stdin when --recursive-process is given.
/// This is only intended for use when developing the compiler.
#[cfg(debug_assertions)]
#[structopt(long = "recursive-process-input")]
#[arg(long = "recursive-process-input")]
pub recursive_process_input: Option<String>,
}
impl Default for Arguments {
fn default() -> Self {
Self::new()
}
}
impl Arguments {
/// A shortcut constructor.
pub fn new() -> Self {
Self::parse()
}
/// Validates the arguments.
#[allow(clippy::collapsible_if)]
pub fn validate(&self) -> anyhow::Result<()> {
+24 -14
View File
@@ -2,6 +2,7 @@
pub mod arguments;
use std::io::Write;
use std::str::FromStr;
use revive_solidity::Process;
@@ -16,28 +17,27 @@ const RAYON_WORKER_STACK_SIZE: usize = 16 * 1024 * 1024;
#[global_allocator]
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
/// The application entry point.
fn main() {
fn main() -> anyhow::Result<()> {
std::process::exit(match main_inner() {
Ok(()) => revive_common::EXIT_CODE_SUCCESS,
Err(error) => {
eprintln!("{error}");
writeln!(std::io::stderr(), "{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();
let arguments = <Arguments as clap::Parser>::try_parse()?;
arguments.validate()?;
if arguments.version {
println!(
writeln!(
std::io::stdout(),
"{} version {}",
env!("CARGO_PKG_DESCRIPTION"),
revive_solidity::ResolcVersion::default().long
);
)?;
return Ok(());
}
@@ -45,7 +45,7 @@ fn main_inner() -> anyhow::Result<()> {
let license_mit = include_str!("../../../../LICENSE-MIT");
let license_apache = include_str!("../../../../LICENSE-APACHE");
println!("{}\n{}\n", license_mit, license_apache);
writeln!(std::io::stdout(), "{}\n{}\n", license_mit, license_apache)?;
return Ok(());
}
@@ -211,26 +211,36 @@ fn main_inner() -> anyhow::Result<()> {
arguments.overwrite,
)?;
eprintln!(
writeln!(
std::io::stderr(),
"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);
writeln!(
std::io::stdout(),
"Contract `{}` assembly:\n\n{}",
path,
assembly_text
)?;
}
if arguments.output_binary {
println!(
writeln!(
std::io::stdout(),
"Contract `{}` bytecode: 0x{}",
path,
hex::encode(contract.build.bytecode)
);
)?;
}
}
} else {
eprintln!("Compiler run successful. No output requested. Use --asm and --bin flags.");
writeln!(
std::io::stderr(),
"Compiler run successful. No output requested. Use --asm and --bin flags."
)?;
}
Ok(())
@@ -80,10 +80,9 @@ impl CombinedJson {
file_path.push(format!("combined.{}", revive_common::EXTENSION_JSON));
if file_path.exists() && !overwrite {
eprintln!(
anyhow::bail!(
"Refusing to overwrite an existing file {file_path:?} (use --overwrite to force)."
);
return Ok(());
}
File::create(&file_path)
+10 -2
View File
@@ -152,8 +152,16 @@ impl Compiler for SolcCompiler {
anyhow::anyhow!("{} subprocess error: {:?}", self.executable, error)
})?;
if !output.status.success() {
println!("{}", String::from_utf8_lossy(output.stdout.as_slice()));
println!("{}", String::from_utf8_lossy(output.stderr.as_slice()));
writeln!(
std::io::stdout(),
"{}",
String::from_utf8_lossy(output.stdout.as_slice())
)?;
writeln!(
std::io::stdout(),
"{}",
String::from_utf8_lossy(output.stderr.as_slice())
)?;
anyhow::bail!(
"{} error: {}",
self.executable,
@@ -68,12 +68,8 @@ impl Input {
paths.insert(PathBuf::from(library_file));
}
#[cfg(feature = "parallel")]
let iter = paths.into_par_iter(); // Parallel iterator
#[cfg(not(feature = "parallel"))]
let iter = paths.iter(); // Sequential iterator
let sources = iter
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}")
@@ -10,7 +10,7 @@ describe("Run resolc without any options", () => {
const result = executeCommand(command);
it("Info with help is presented", () => {
expect(result.output).toMatch(/(No input sources specified|Error(s) found.)/i);
expect(result.output).toMatch(/(Usage: resolc)/i);
});
it("Exit code = 1", () => {
@@ -28,7 +28,7 @@ describe("Run resolc without any options", () => {
//#1713
describe("Default run a command from the help", () => {
const command = `resolc ${paths.pathToBasicSolContract} -O3 --bin --output-dir "${paths.pathToOutputDir}"`; // potential issue on resolc with full path on Windows cmd
const command = `resolc ${paths.pathToBasicSolContract} --overwrite -O3 --bin --output-dir "${paths.pathToOutputDir}"`; // potential issue on resolc with full path on Windows cmd
const result = executeCommand(command);
it("Compiler run successful", () => {
@@ -54,7 +54,7 @@ describe("Default run a command from the help", () => {
//#1818
describe("Default run a command from the help", () => {
const command = `resolc ${paths.pathToBasicSolContract} -O3 --bin --asm --output-dir "${paths.pathToOutputDir}"`; // potential issue on resolc with full path on Windows cmd
const command = `resolc ${paths.pathToBasicSolContract} --overwrite -O3 --bin --asm --output-dir "${paths.pathToOutputDir}"`; // potential issue on resolc with full path on Windows cmd
const result = executeCommand(command);
it("Compiler run successful", () => {
@@ -81,8 +81,8 @@ describe("Default run a command from the help", () => {
describe("Run resolc with source debug information", () => {
const commands = [
`resolc -g ${paths.pathToBasicSolContract} --bin --asm --output-dir "${paths.pathToOutputDir}"`,
`resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --bin --asm --output-dir "${paths.pathToOutputDir}"`
`resolc -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`,
`resolc --disable-solc-optimizer -g ${paths.pathToBasicSolContract} --overwrite --bin --asm --output-dir "${paths.pathToOutputDir}"`
]; // potential issue on resolc with full path on Windows cmd`;
for (var idx in commands) {
@@ -114,8 +114,8 @@ describe("Run resolc with source debug information", () => {
describe("Run resolc with source debug information, check LLVM debug-info", () => {
const commands = [
`resolc -g ${paths.pathToBasicSolContract} --debug-output-dir="${paths.pathToOutputDir}"`,
`resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --debug-output-dir="${paths.pathToOutputDir}"`
`resolc -g ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`,
`resolc -g --disable-solc-optimizer ${paths.pathToBasicSolContract} --overwrite --debug-output-dir="${paths.pathToOutputDir}"`
]; // potential issue on resolc with full path on Windows cmd`;
for (var idx in commands) {