mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-06-11 16:11:01 +00:00
Initial wasm support
This commit is contained in:
@@ -8,8 +8,8 @@ use std::path::Path;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::solc::combined_json::contract::Contract as CombinedJsonContract;
|
||||
use crate::solc::standard_json::output::contract::Contract as StandardJsonOutputContract;
|
||||
use crate::compiler::combined_json::contract::Contract as CombinedJsonContract;
|
||||
use crate::compiler::standard_json::output::contract::Contract as StandardJsonOutputContract;
|
||||
|
||||
/// The Solidity contract build.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
||||
@@ -5,9 +5,9 @@ pub mod contract;
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::solc::combined_json::CombinedJson;
|
||||
use crate::solc::standard_json::output::Output as StandardJsonOutput;
|
||||
use crate::solc::version::Version as SolcVersion;
|
||||
use crate::compiler::combined_json::CombinedJson;
|
||||
use crate::compiler::standard_json::output::Output as StandardJsonOutput;
|
||||
use crate::compiler::version::Version as SolcVersion;
|
||||
|
||||
use self::contract::Contract;
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ use std::collections::HashSet;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::compiler::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
|
||||
use crate::evmla::ethereal_ir::entry_link::EntryLink;
|
||||
use crate::evmla::ethereal_ir::EtherealIR;
|
||||
use crate::solc::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
|
||||
|
||||
use self::data::Data;
|
||||
use self::instruction::name::Name as InstructionName;
|
||||
|
||||
@@ -20,13 +20,13 @@ use num::One;
|
||||
use num::ToPrimitive;
|
||||
use num::Zero;
|
||||
|
||||
use crate::compiler::standard_json::output::contract::evm::extra_metadata::recursive_function::RecursiveFunction;
|
||||
use crate::compiler::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
|
||||
use crate::evmla::assembly::instruction::name::Name as InstructionName;
|
||||
use crate::evmla::assembly::instruction::Instruction;
|
||||
use crate::evmla::ethereal_ir::function::block::element::stack::element::Element;
|
||||
use crate::evmla::ethereal_ir::function::block::element::stack::Stack;
|
||||
use crate::evmla::ethereal_ir::EtherealIR;
|
||||
use crate::solc::standard_json::output::contract::evm::extra_metadata::recursive_function::RecursiveFunction;
|
||||
use crate::solc::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
|
||||
|
||||
use self::block::element::stack::element::Element as StackElement;
|
||||
use self::block::element::Element as BlockElement;
|
||||
|
||||
@@ -7,8 +7,8 @@ use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::compiler::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
|
||||
use crate::evmla::assembly::instruction::Instruction;
|
||||
use crate::solc::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
|
||||
|
||||
use self::function::block::Block;
|
||||
use self::function::r#type::Type as FunctionType;
|
||||
|
||||
+42
-32
@@ -1,45 +1,55 @@
|
||||
//! Solidity to PolkaVM compiler library.
|
||||
|
||||
pub(crate) mod build;
|
||||
#[path = "solc/compiler.rs"]
|
||||
pub(crate) mod compiler;
|
||||
pub(crate) mod r#const;
|
||||
pub(crate) mod evmla;
|
||||
pub(crate) mod missing_libraries;
|
||||
pub(crate) mod process;
|
||||
pub(crate) mod project;
|
||||
pub(crate) mod solc;
|
||||
pub(crate) mod warning;
|
||||
pub(crate) mod yul;
|
||||
|
||||
pub use self::build::contract::Contract as ContractBuild;
|
||||
pub use self::build::Build;
|
||||
pub use self::compiler::combined_json::contract::Contract as SolcCombinedJsonContract;
|
||||
pub use self::compiler::combined_json::CombinedJson as SolcCombinedJson;
|
||||
pub use self::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
#[cfg(not(target_os = "emscripten"))]
|
||||
pub use self::compiler::solc::SolcCompiler;
|
||||
#[cfg(target_os = "emscripten")]
|
||||
pub use self::compiler::soljson::SoljsonCompiler;
|
||||
pub use self::compiler::standard_json::input::language::Language as SolcStandardJsonInputLanguage;
|
||||
pub use self::compiler::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata;
|
||||
pub use self::compiler::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
|
||||
pub use self::compiler::standard_json::input::settings::selection::file::flag::Flag as SolcStandardJsonInputSettingsSelectionFileFlag;
|
||||
pub use self::compiler::standard_json::input::settings::selection::file::File as SolcStandardJsonInputSettingsSelectionFile;
|
||||
pub use self::compiler::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection;
|
||||
pub use self::compiler::standard_json::input::settings::Settings as SolcStandardJsonInputSettings;
|
||||
pub use self::compiler::standard_json::input::source::Source as SolcStandardJsonInputSource;
|
||||
pub use self::compiler::standard_json::input::Input as SolcStandardJsonInput;
|
||||
pub use self::compiler::standard_json::output::contract::evm::bytecode::Bytecode as SolcStandardJsonOutputContractEVMBytecode;
|
||||
pub use self::compiler::standard_json::output::contract::evm::EVM as SolcStandardJsonOutputContractEVM;
|
||||
pub use self::compiler::standard_json::output::contract::Contract as SolcStandardJsonOutputContract;
|
||||
pub use self::compiler::standard_json::output::Output as SolcStandardJsonOutput;
|
||||
pub use self::compiler::version::Version as SolcVersion;
|
||||
pub use self::compiler::Compiler;
|
||||
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;
|
||||
pub use self::process::run as run_process;
|
||||
pub use self::process::EXECUTABLE;
|
||||
#[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::*;
|
||||
pub use self::solc::combined_json::contract::Contract as SolcCombinedJsonContract;
|
||||
pub use self::solc::combined_json::CombinedJson as SolcCombinedJson;
|
||||
pub use self::solc::pipeline::Pipeline as SolcPipeline;
|
||||
pub use self::solc::standard_json::input::language::Language as SolcStandardJsonInputLanguage;
|
||||
pub use self::solc::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata;
|
||||
pub use self::solc::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
|
||||
pub use self::solc::standard_json::input::settings::selection::file::flag::Flag as SolcStandardJsonInputSettingsSelectionFileFlag;
|
||||
pub use self::solc::standard_json::input::settings::selection::file::File as SolcStandardJsonInputSettingsSelectionFile;
|
||||
pub use self::solc::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection;
|
||||
pub use self::solc::standard_json::input::settings::Settings as SolcStandardJsonInputSettings;
|
||||
pub use self::solc::standard_json::input::source::Source as SolcStandardJsonInputSource;
|
||||
pub use self::solc::standard_json::input::Input as SolcStandardJsonInput;
|
||||
pub use self::solc::standard_json::output::contract::evm::bytecode::Bytecode as SolcStandardJsonOutputContractEVMBytecode;
|
||||
pub use self::solc::standard_json::output::contract::evm::EVM as SolcStandardJsonOutputContractEVM;
|
||||
pub use self::solc::standard_json::output::contract::Contract as SolcStandardJsonOutputContract;
|
||||
pub use self::solc::standard_json::output::Output as SolcStandardJsonOutput;
|
||||
pub use self::solc::version::Version as SolcVersion;
|
||||
pub use self::solc::Compiler as SolcCompiler;
|
||||
pub use self::warning::Warning;
|
||||
|
||||
#[cfg(target_os = "emscripten")]
|
||||
pub mod libsolc;
|
||||
#[cfg(not(target_os = "emscripten"))]
|
||||
pub mod test_utils;
|
||||
pub mod tests;
|
||||
|
||||
@@ -47,9 +57,9 @@ use std::collections::BTreeSet;
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Runs the Yul mode.
|
||||
pub fn yul(
|
||||
pub fn yul<T: Compiler>(
|
||||
input_files: &[PathBuf],
|
||||
solc: &mut SolcCompiler,
|
||||
solc: &mut T,
|
||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||
is_system_mode: bool,
|
||||
include_metadata_hash: bool,
|
||||
@@ -67,10 +77,10 @@ pub fn yul(
|
||||
let solc_validator = if is_system_mode {
|
||||
None
|
||||
} else {
|
||||
if solc.version()?.default != SolcCompiler::LAST_SUPPORTED_VERSION {
|
||||
if solc.version()?.default != compiler::LAST_SUPPORTED_VERSION {
|
||||
anyhow::bail!(
|
||||
"The Yul mode is only supported with the most recent version of the Solidity compiler: {}",
|
||||
SolcCompiler::LAST_SUPPORTED_VERSION,
|
||||
compiler::LAST_SUPPORTED_VERSION,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -122,10 +132,10 @@ pub fn llvm_ir(
|
||||
|
||||
/// Runs the standard output mode.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn standard_output(
|
||||
pub fn standard_output<T: Compiler>(
|
||||
input_files: &[PathBuf],
|
||||
libraries: Vec<String>,
|
||||
solc: &mut SolcCompiler,
|
||||
solc: &mut T,
|
||||
evm_version: Option<revive_common::EVMVersion>,
|
||||
solc_optimizer_enabled: bool,
|
||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||
@@ -213,8 +223,8 @@ pub fn standard_output(
|
||||
|
||||
/// Runs the standard JSON mode.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn standard_json(
|
||||
solc: &mut SolcCompiler,
|
||||
pub fn standard_json<T: Compiler>(
|
||||
solc: &mut T,
|
||||
detect_missing_libraries: bool,
|
||||
force_evmla: bool,
|
||||
is_system_mode: bool,
|
||||
@@ -293,11 +303,11 @@ pub fn standard_json(
|
||||
|
||||
/// Runs the combined JSON mode.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn combined_json(
|
||||
pub fn combined_json<T: Compiler>(
|
||||
format: String,
|
||||
input_files: &[PathBuf],
|
||||
libraries: Vec<String>,
|
||||
solc: &mut SolcCompiler,
|
||||
solc: &mut T,
|
||||
evm_version: Option<revive_common::EVMVersion>,
|
||||
solc_optimizer_enabled: bool,
|
||||
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
//! The Solidity compiler library.
|
||||
|
||||
use crate::Compiler;
|
||||
use crate::SoljsonCompiler;
|
||||
use libc::{c_char, size_t};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ptr;
|
||||
|
||||
static mut VERSION: Option<Box<CString>> = None;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn solidity_license() -> *const c_char {
|
||||
let license = "This is the Revive license.";
|
||||
CString::new(license).unwrap().into_raw()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn solidity_version() -> *const c_char {
|
||||
let mut solc = SoljsonCompiler { version: None };
|
||||
let version = solc.version().map(|v| v.long).unwrap_or("unknown".to_owned());
|
||||
// Store the string in a static variable
|
||||
unsafe {
|
||||
if VERSION.is_none() {
|
||||
VERSION = Some(Box::new(CString::new(version).unwrap()));
|
||||
}
|
||||
|
||||
VERSION.as_ref().map_or_else(std::ptr::null, |s| s.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn solidity_alloc(size: size_t) -> *mut c_char {
|
||||
let buffer = vec![0u8; size].into_boxed_slice();
|
||||
Box::into_raw(buffer) as *mut c_char
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn solidity_free(data: *mut c_char) {
|
||||
if !data.is_null() {
|
||||
unsafe {
|
||||
CString::from_raw(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn solidity_compile(
|
||||
input: *const c_char,
|
||||
_readCallback: Option<extern "C" fn(*mut libc::c_void, *const c_char, *const c_char, *mut *mut c_char, *mut *mut c_char)>,
|
||||
_readContext: *mut libc::c_void,
|
||||
) -> *mut c_char {
|
||||
let input = unsafe { CStr::from_ptr(input).to_str().unwrap_or("") };
|
||||
|
||||
// Mock compilation process
|
||||
let output = format!("Compiled output for: {}", input);
|
||||
|
||||
CString::new(output).unwrap().into_raw()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn solidity_reset() {
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::solc::standard_json::output::Output as StandardJsonOutput;
|
||||
use crate::solc::version::Version as SolcVersion;
|
||||
use crate::compiler::standard_json::output::Output as StandardJsonOutput;
|
||||
use crate::compiler::version::Version as SolcVersion;
|
||||
|
||||
/// The missing Solidity libraries.
|
||||
pub struct MissingLibraries {
|
||||
|
||||
@@ -1,129 +1,16 @@
|
||||
//! Process for compiling a single compilation unit.
|
||||
|
||||
pub mod input;
|
||||
#[cfg(not(target_os = "emscripten"))]
|
||||
pub mod native_process;
|
||||
pub mod output;
|
||||
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
#[cfg(target_os = "emscripten")]
|
||||
pub mod worker_process;
|
||||
|
||||
use self::input::Input;
|
||||
use self::output::Output;
|
||||
|
||||
/// The overriden executable name used when the compiler is run as a library.
|
||||
pub static EXECUTABLE: OnceCell<PathBuf> = OnceCell::new();
|
||||
|
||||
/// Read input from `stdin`, compile a contract, and write the output to `stdout`.
|
||||
pub fn run(input_file: Option<&mut std::fs::File>) -> anyhow::Result<()> {
|
||||
let mut stdin = std::io::stdin();
|
||||
let mut stdout = std::io::stdout();
|
||||
let mut stderr = std::io::stderr();
|
||||
|
||||
let mut buffer = Vec::with_capacity(16384);
|
||||
match input_file {
|
||||
Some(ins) => {
|
||||
if let Err(error) = ins.read_to_end(&mut buffer) {
|
||||
anyhow::bail!("Failed to read recursive process input file: {:?}", error);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if let Err(error) = stdin.read_to_end(&mut buffer) {
|
||||
anyhow::bail!(
|
||||
"Failed to read recursive process input from stdin: {:?}",
|
||||
error
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let input: Input = revive_common::deserialize_from_slice(buffer.as_slice())?;
|
||||
if input.enable_test_encoding {
|
||||
todo!()
|
||||
}
|
||||
let result = input.contract.compile(
|
||||
input.project,
|
||||
input.optimizer_settings,
|
||||
input.is_system_mode,
|
||||
input.include_metadata_hash,
|
||||
input.debug_config,
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(build) => {
|
||||
let output = Output::new(build);
|
||||
let json = serde_json::to_vec(&output).expect("Always valid");
|
||||
stdout
|
||||
.write_all(json.as_slice())
|
||||
.expect("Stdout writing error");
|
||||
Ok(())
|
||||
}
|
||||
Err(error) => {
|
||||
let message = error.to_string();
|
||||
stderr
|
||||
.write_all(message.as_bytes())
|
||||
.expect("Stderr writing error");
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs this process recursively to compile a single contract.
|
||||
pub fn call(input: Input) -> anyhow::Result<Output> {
|
||||
let input_json = serde_json::to_vec(&input).expect("Always valid");
|
||||
|
||||
let executable = match EXECUTABLE.get() {
|
||||
Some(executable) => executable.to_owned(),
|
||||
None => std::env::current_exe()?,
|
||||
};
|
||||
|
||||
let mut command = Command::new(executable.as_path());
|
||||
command.stdin(std::process::Stdio::piped());
|
||||
command.stdout(std::process::Stdio::piped());
|
||||
command.stderr(std::process::Stdio::piped());
|
||||
command.arg("--recursive-process");
|
||||
let process = command.spawn().map_err(|error| {
|
||||
anyhow::anyhow!("{:?} subprocess spawning error: {:?}", executable, error)
|
||||
})?;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(dbg_config) = &input.debug_config {
|
||||
let _ = dbg_config
|
||||
.dump_stage_output(&input.contract.path, Some("stage"), &input_json)
|
||||
.map_err(|error| {
|
||||
anyhow::anyhow!(
|
||||
"{:?} failed to log the recursive process output: {:?}",
|
||||
executable,
|
||||
error,
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
process
|
||||
.stdin
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("{:?} stdin getting error", executable))?
|
||||
.write_all(input_json.as_slice())
|
||||
.map_err(|error| anyhow::anyhow!("{:?} stdin writing error: {:?}", executable, error))?;
|
||||
let output = process.wait_with_output().map_err(|error| {
|
||||
anyhow::anyhow!("{:?} subprocess output error: {:?}", executable, error)
|
||||
})?;
|
||||
if !output.status.success() {
|
||||
anyhow::bail!(
|
||||
"{}",
|
||||
String::from_utf8_lossy(output.stderr.as_slice()).to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
let output: Output =
|
||||
revive_common::deserialize_from_slice(output.stdout.as_slice()).map_err(|error| {
|
||||
anyhow::anyhow!(
|
||||
"{:?} subprocess output parsing error: {}",
|
||||
executable,
|
||||
error,
|
||||
)
|
||||
})?;
|
||||
Ok(output)
|
||||
pub trait Process {
|
||||
fn run() -> anyhow::Result<()>;
|
||||
fn call(input: Input) -> anyhow::Result<Output>;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
//! Process for compiling a single compilation unit.
|
||||
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
use super::Input;
|
||||
use super::Output;
|
||||
use super::Process;
|
||||
|
||||
/// The overriden executable name used when the compiler is run as a library.
|
||||
pub static EXECUTABLE: OnceCell<PathBuf> = OnceCell::new();
|
||||
|
||||
pub struct NativeProcess;
|
||||
|
||||
impl Process for NativeProcess {
|
||||
/// Read input from `stdin`, compile a contract, and write the output to `stdout`.
|
||||
fn run() -> anyhow::Result<()> {
|
||||
let mut stdin = std::io::stdin();
|
||||
let mut stdout = std::io::stdout();
|
||||
let mut stderr = std::io::stderr();
|
||||
|
||||
let mut buffer = Vec::with_capacity(16384);
|
||||
stdin.read_to_end(&mut buffer).expect("Stdin reading error");
|
||||
|
||||
let input: Input = revive_common::deserialize_from_slice(buffer.as_slice())?;
|
||||
if input.enable_test_encoding {
|
||||
todo!()
|
||||
}
|
||||
let result = input.contract.compile(
|
||||
input.project,
|
||||
input.optimizer_settings,
|
||||
input.is_system_mode,
|
||||
input.include_metadata_hash,
|
||||
input.debug_config,
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(build) => {
|
||||
let output = Output::new(build);
|
||||
let json = serde_json::to_vec(&output).expect("Always valid");
|
||||
stdout
|
||||
.write_all(json.as_slice())
|
||||
.expect("Stdout writing error");
|
||||
Ok(())
|
||||
}
|
||||
Err(error) => {
|
||||
let message = error.to_string();
|
||||
stderr
|
||||
.write_all(message.as_bytes())
|
||||
.expect("Stderr writing error");
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs this process recursively to compile a single contract.
|
||||
fn call(input: Input) -> anyhow::Result<Output> {
|
||||
let input_json = serde_json::to_vec(&input).expect("Always valid");
|
||||
|
||||
let executable = match EXECUTABLE.get() {
|
||||
Some(executable) => executable.to_owned(),
|
||||
None => std::env::current_exe()?,
|
||||
};
|
||||
|
||||
let mut command = Command::new(executable.as_path());
|
||||
command.stdin(std::process::Stdio::piped());
|
||||
command.stdout(std::process::Stdio::piped());
|
||||
command.stderr(std::process::Stdio::piped());
|
||||
command.arg("--recursive-process");
|
||||
let process = command.spawn().map_err(|error| {
|
||||
anyhow::anyhow!("{:?} subprocess spawning error: {:?}", executable, error)
|
||||
})?;
|
||||
|
||||
process
|
||||
.stdin
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("{:?} stdin getting error", executable))?
|
||||
.write_all(input_json.as_slice())
|
||||
.map_err(|error| {
|
||||
anyhow::anyhow!("{:?} stdin writing error: {:?}", executable, error)
|
||||
})?;
|
||||
let output = process.wait_with_output().map_err(|error| {
|
||||
anyhow::anyhow!("{:?} subprocess output error: {:?}", executable, error)
|
||||
})?;
|
||||
if !output.status.success() {
|
||||
anyhow::bail!(
|
||||
"{}",
|
||||
String::from_utf8_lossy(output.stderr.as_slice()).to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
let output: Output = revive_common::deserialize_from_slice(output.stdout.as_slice())
|
||||
.map_err(|error| {
|
||||
anyhow::anyhow!(
|
||||
"{:?} subprocess output parsing error: {}",
|
||||
executable,
|
||||
error,
|
||||
)
|
||||
})?;
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
//! Process for compiling a single compilation unit using Web Workers.
|
||||
|
||||
use std::ffi::{c_char, c_void, CStr, CString};
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
|
||||
use super::Input;
|
||||
use super::Output;
|
||||
use super::Process;
|
||||
|
||||
use anyhow::Context;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Error {
|
||||
message: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Success {
|
||||
data: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "type", rename_all = "snake_case")]
|
||||
enum Response {
|
||||
Success(Success),
|
||||
Error(Error),
|
||||
}
|
||||
|
||||
pub struct WorkerProcess;
|
||||
|
||||
impl Process for WorkerProcess {
|
||||
/// Read input from `stdin`, compile a contract, and write the output to `stdout`.
|
||||
fn run() -> anyhow::Result<()> {
|
||||
let mut buffer = Vec::with_capacity(16384);
|
||||
// TODO: Init correctly stdin in emscripten - preload FS conf before module init
|
||||
let mut stdin = File::open("/in")
|
||||
.map_err(|error| anyhow::anyhow!("File /in openning error: {}", error))?;
|
||||
let mut stdout = File::create("/out")
|
||||
.map_err(|error| anyhow::anyhow!("File /out creating error: {}", error))?;
|
||||
let mut stderr = File::create("/err")
|
||||
.map_err(|error| anyhow::anyhow!("File /err creating error: {}", error))?;
|
||||
|
||||
stdin.read_to_end(&mut buffer).expect("Stdin reading error");
|
||||
|
||||
let input: Input = revive_common::deserialize_from_slice(buffer.as_slice())?;
|
||||
if input.enable_test_encoding {
|
||||
todo!()
|
||||
}
|
||||
let result = input.contract.compile(
|
||||
input.project,
|
||||
input.optimizer_settings,
|
||||
input.is_system_mode,
|
||||
input.include_metadata_hash,
|
||||
input.debug_config,
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(build) => {
|
||||
let output = Output::new(build);
|
||||
let json = serde_json::to_vec(&output).expect("Always valid");
|
||||
stdout
|
||||
.write_all(json.as_slice())
|
||||
.expect("Stdout writing error");
|
||||
Ok(())
|
||||
}
|
||||
Err(error) => {
|
||||
let message = error.to_string();
|
||||
stderr
|
||||
.write_all(message.as_bytes())
|
||||
.expect("Stderr writing error");
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs this process recursively to compile a single contract.
|
||||
fn call(input: Input) -> anyhow::Result<Output> {
|
||||
let input_json = serde_json::to_vec(&input).expect("Always valid");
|
||||
let input_str = String::from_utf8(input_json).expect("Input shall be valid");
|
||||
// Prepare the input string for the Emscripten function
|
||||
let input_cstring = CString::new(input_str).expect("CString allocation failed");
|
||||
|
||||
// Call the Emscripten function
|
||||
let output_ptr =
|
||||
unsafe { resolc_compile(input_cstring.as_ptr(), input_cstring.as_bytes().len()) };
|
||||
|
||||
// Convert the output pointer back to a Rust string
|
||||
let output_str = unsafe {
|
||||
CStr::from_ptr(output_ptr)
|
||||
.to_str()
|
||||
.with_context(|| "Failed to convert C string to Rust string")
|
||||
.map(str::to_owned)
|
||||
};
|
||||
unsafe { libc::free(output_ptr as *mut c_void) };
|
||||
let output_str = output_str?;
|
||||
let response: Response = serde_json::from_str(&output_str)
|
||||
.map_err(|error| anyhow::anyhow!("Worker output parsing error: {}", error,))?;
|
||||
match response {
|
||||
Response::Success(out) => {
|
||||
let output: Output = revive_common::deserialize_from_slice(out.data.as_bytes())
|
||||
.map_err(|error| {
|
||||
anyhow::anyhow!("resolc.js subprocess output parsing error: {}", error,)
|
||||
})?;
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
Response::Error(err) => anyhow::bail!("Worker error: {}", err.message,),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn resolc_compile(input_ptr: *const c_char, input_len: usize) -> *const c_char;
|
||||
}
|
||||
@@ -5,8 +5,8 @@ use std::collections::HashSet;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::compiler::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
|
||||
use crate::evmla::assembly::Assembly;
|
||||
use crate::solc::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
|
||||
|
||||
/// The contract EVM legacy assembly source code.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
|
||||
@@ -9,8 +9,8 @@ use std::collections::HashSet;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::compiler::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
|
||||
use crate::evmla::assembly::Assembly;
|
||||
use crate::solc::standard_json::output::contract::evm::extra_metadata::ExtraMetadata;
|
||||
use crate::yul::parser::statement::object::Object;
|
||||
|
||||
use self::evmla::EVMLA;
|
||||
|
||||
@@ -12,8 +12,8 @@ use sha3::Digest;
|
||||
use revive_llvm_context::PolkaVMWriteLLVM;
|
||||
|
||||
use crate::build::contract::Contract as ContractBuild;
|
||||
use crate::compiler::version::Version as SolcVersion;
|
||||
use crate::project::Project;
|
||||
use crate::solc::version::Version as SolcVersion;
|
||||
|
||||
use self::ir::IR;
|
||||
use self::metadata::Metadata;
|
||||
|
||||
@@ -7,19 +7,21 @@ use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::Path;
|
||||
|
||||
use rayon::iter::IntoParallelIterator;
|
||||
use rayon::iter::ParallelIterator;
|
||||
#[cfg(feature = "parallel")]
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use sha3::Digest;
|
||||
|
||||
use crate::build::contract::Contract as ContractBuild;
|
||||
use crate::build::Build;
|
||||
use crate::compiler;
|
||||
use crate::compiler::version::Version as SolcVersion;
|
||||
use crate::compiler::Compiler;
|
||||
use crate::missing_libraries::MissingLibraries;
|
||||
use crate::process::input::Input as ProcessInput;
|
||||
use crate::process::Process;
|
||||
use crate::project::contract::ir::IR;
|
||||
use crate::solc::version::Version as SolcVersion;
|
||||
use crate::solc::Compiler as SolcCompiler;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::statement::object::Object;
|
||||
|
||||
@@ -68,11 +70,14 @@ impl Project {
|
||||
debug_config: Option<revive_llvm_context::DebugConfig>,
|
||||
) -> anyhow::Result<Build> {
|
||||
let project = self.clone();
|
||||
let results: BTreeMap<String, anyhow::Result<ContractBuild>> = self
|
||||
.contracts
|
||||
.into_par_iter()
|
||||
#[cfg(feature = "parallel")]
|
||||
let iter = self.contracts.into_par_iter();
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
let iter = self.contracts.into_iter();
|
||||
|
||||
let results: BTreeMap<String, anyhow::Result<ContractBuild>> = iter
|
||||
.map(|(full_path, contract)| {
|
||||
let process_output = crate::process::call(ProcessInput::new(
|
||||
let process_input = ProcessInput::new(
|
||||
contract,
|
||||
project.clone(),
|
||||
is_system_mode,
|
||||
@@ -80,8 +85,17 @@ impl Project {
|
||||
bytecode_encoding_testing,
|
||||
optimizer_settings.clone(),
|
||||
debug_config.clone(),
|
||||
));
|
||||
|
||||
);
|
||||
let process_output = {
|
||||
#[cfg(target_os = "emscripten")]
|
||||
{
|
||||
crate::WorkerProcess::call(process_input)
|
||||
}
|
||||
#[cfg(not(target_os = "emscripten"))]
|
||||
{
|
||||
crate::NativeProcess::call(process_input)
|
||||
}
|
||||
};
|
||||
(full_path, process_output.map(|output| output.build))
|
||||
})
|
||||
.collect();
|
||||
@@ -159,9 +173,9 @@ impl Project {
|
||||
}
|
||||
|
||||
/// Parses the Yul source code file and returns the source data.
|
||||
pub fn try_from_yul_path(
|
||||
pub fn try_from_yul_path<T: Compiler>(
|
||||
path: &Path,
|
||||
solc_validator: Option<&SolcCompiler>,
|
||||
solc_validator: Option<&T>,
|
||||
) -> anyhow::Result<Self> {
|
||||
let source_code = std::fs::read_to_string(path)
|
||||
.map_err(|error| anyhow::anyhow!("Yul file {:?} reading error: {}", path, error))?;
|
||||
@@ -170,16 +184,16 @@ impl Project {
|
||||
|
||||
/// Parses the test Yul source code string and returns the source data.
|
||||
/// Only for integration testing purposes.
|
||||
pub fn try_from_yul_string(
|
||||
pub fn try_from_yul_string<T: Compiler>(
|
||||
path: &Path,
|
||||
source_code: &str,
|
||||
solc_validator: Option<&SolcCompiler>,
|
||||
solc_validator: Option<&T>,
|
||||
) -> anyhow::Result<Self> {
|
||||
if let Some(solc) = solc_validator {
|
||||
solc.validate_yul(path)?;
|
||||
}
|
||||
|
||||
let source_version = SolcVersion::new_simple(SolcCompiler::LAST_SUPPORTED_VERSION);
|
||||
let source_version = SolcVersion::new_simple(compiler::LAST_SUPPORTED_VERSION);
|
||||
let path = path.to_string_lossy().to_string();
|
||||
let source_hash = sha3::Keccak256::digest(source_code.as_bytes()).into();
|
||||
|
||||
|
||||
@@ -4,25 +4,21 @@ use std::collections::BTreeSet;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use clap::Parser;
|
||||
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, Parser)]
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(name = "The PolkaVM Solidity compiler")]
|
||||
pub struct Arguments {
|
||||
/// Print the version and exit.
|
||||
#[structopt(long = "version")]
|
||||
pub version: bool,
|
||||
|
||||
/// Print the licence and exit.
|
||||
#[structopt(long = "license")]
|
||||
pub license: 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.
|
||||
@@ -46,7 +42,7 @@ pub struct Arguments {
|
||||
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")]
|
||||
#[structopt(short = "o", long = "output-dir")]
|
||||
pub output_directory: Option<PathBuf>,
|
||||
|
||||
/// Overwrite existing files (used together with -o).
|
||||
@@ -55,7 +51,7 @@ pub struct Arguments {
|
||||
|
||||
/// 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")]
|
||||
#[structopt(short = "O", long = "optimization")]
|
||||
pub optimization: Option<char>,
|
||||
|
||||
/// Try to recompile with -Oz if the bytecode is too large.
|
||||
@@ -85,7 +81,7 @@ pub struct Arguments {
|
||||
|
||||
/// Specify addresses of deployable libraries. Syntax: `<libraryName>=<address> [, or whitespace] ...`.
|
||||
/// Addresses are interpreted as hexadecimal strings prefixed with `0x`.
|
||||
#[structopt(short = 'l', long = "libraries")]
|
||||
#[structopt(short = "l", long = "libraries")]
|
||||
pub libraries: Vec<String>,
|
||||
|
||||
/// Output a single JSON document containing the specified information.
|
||||
@@ -168,12 +164,6 @@ pub struct Arguments {
|
||||
/// Only for usage from within the compiler.
|
||||
#[structopt(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")]
|
||||
pub recursive_process_input: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for Arguments {
|
||||
@@ -185,7 +175,7 @@ impl Default for Arguments {
|
||||
impl Arguments {
|
||||
/// A shortcut constructor.
|
||||
pub fn new() -> Self {
|
||||
Self::parse()
|
||||
Self::from_args()
|
||||
}
|
||||
|
||||
/// Validates the arguments.
|
||||
@@ -195,20 +185,6 @@ impl Arguments {
|
||||
anyhow::bail!("No other options are allowed while getting the compiler version.");
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if self.recursive_process_input != None && !self.recursive_process {
|
||||
anyhow::bail!("--process-input can be only used when --recursive-process is given");
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if self.recursive_process
|
||||
&& ((self.recursive_process_input == None && std::env::args().count() > 2)
|
||||
|| (self.recursive_process_input != None && std::env::args().count() > 4))
|
||||
{
|
||||
anyhow::bail!("No other options are allowed in recursive mode.");
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
if self.recursive_process && std::env::args().count() > 2 {
|
||||
anyhow::bail!("No other options are allowed in recursive mode.");
|
||||
}
|
||||
|
||||
@@ -4,8 +4,11 @@ pub mod arguments;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use revive_solidity::Process;
|
||||
|
||||
use self::arguments::Arguments;
|
||||
|
||||
#[cfg(feature = "parallel")]
|
||||
/// The rayon worker stack size.
|
||||
const RAYON_WORKER_STACK_SIZE: usize = 16 * 1024 * 1024;
|
||||
|
||||
@@ -39,14 +42,7 @@ fn main_inner() -> anyhow::Result<()> {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if arguments.license {
|
||||
let license_mit = include_str!("../../../../LICENSE-MIT");
|
||||
let license_apache = include_str!("../../../../LICENSE-APACHE");
|
||||
|
||||
println!("{}\n{}\n", license_mit, license_apache);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[cfg(feature = "parallel")]
|
||||
rayon::ThreadPoolBuilder::new()
|
||||
.stack_size(RAYON_WORKER_STACK_SIZE)
|
||||
.build_global()
|
||||
@@ -55,13 +51,14 @@ fn main_inner() -> anyhow::Result<()> {
|
||||
revive_llvm_context::initialize_target(revive_llvm_context::Target::PVM); // TODO: pass from CLI
|
||||
|
||||
if arguments.recursive_process {
|
||||
#[cfg(debug_assertions)]
|
||||
if let Some(fname) = arguments.recursive_process_input {
|
||||
let mut infile = std::fs::File::open(fname)?;
|
||||
return revive_solidity::run_process(Some(&mut infile));
|
||||
#[cfg(target_os = "emscripten")]
|
||||
{
|
||||
return revive_solidity::WorkerProcess::run();
|
||||
}
|
||||
#[cfg(not(target_os = "emscripten"))]
|
||||
{
|
||||
return revive_solidity::NativeProcess::run();
|
||||
}
|
||||
|
||||
return revive_solidity::run_process(None);
|
||||
}
|
||||
|
||||
let debug_config = match arguments.debug_output_directory {
|
||||
@@ -83,11 +80,19 @@ fn main_inner() -> anyhow::Result<()> {
|
||||
None => None,
|
||||
};
|
||||
|
||||
let mut solc = revive_solidity::SolcCompiler::new(
|
||||
arguments
|
||||
.solc
|
||||
.unwrap_or_else(|| revive_solidity::SolcCompiler::DEFAULT_EXECUTABLE_NAME.to_owned()),
|
||||
)?;
|
||||
let mut solc = {
|
||||
#[cfg(target_os = "emscripten")]
|
||||
{
|
||||
revive_solidity::SoljsonCompiler { version: None }
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "emscripten"))]
|
||||
{
|
||||
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())?),
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
//! The Solidity compiler.
|
||||
|
||||
pub mod combined_json;
|
||||
pub mod pipeline;
|
||||
#[cfg(not(target_os = "emscripten"))]
|
||||
pub mod solc;
|
||||
#[cfg(target_os = "emscripten")]
|
||||
pub mod soljson;
|
||||
pub mod standard_json;
|
||||
pub mod version;
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use self::combined_json::CombinedJson;
|
||||
use self::pipeline::Pipeline;
|
||||
use self::standard_json::input::Input as StandardJsonInput;
|
||||
use self::standard_json::output::Output as StandardJsonOutput;
|
||||
use self::version::Version;
|
||||
|
||||
/// The first version of `solc` with the support of standard JSON interface.
|
||||
const FIRST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 4, 12);
|
||||
|
||||
/// The first version of `solc`, where Yul codegen is considered robust enough.
|
||||
const FIRST_YUL_VERSION: semver::Version = semver::Version::new(0, 8, 0);
|
||||
|
||||
/// The first version of `solc`, where `--via-ir` codegen mode is supported.
|
||||
const FIRST_VIA_IR_VERSION: semver::Version = semver::Version::new(0, 8, 13);
|
||||
|
||||
/// The last supported version of `solc`.
|
||||
pub(crate) const LAST_SUPPORTED_VERSION: semver::Version = semver::Version::new(0, 8, 26);
|
||||
|
||||
/// The Solidity compiler.
|
||||
pub trait Compiler {
|
||||
/// Compiles the Solidity `--standard-json` input into Yul IR.
|
||||
fn standard_json(
|
||||
&mut self,
|
||||
input: StandardJsonInput,
|
||||
pipeline: Pipeline,
|
||||
base_path: Option<String>,
|
||||
include_paths: Vec<String>,
|
||||
allow_paths: Option<String>,
|
||||
) -> anyhow::Result<StandardJsonOutput>;
|
||||
|
||||
/// The `solc --combined-json abi,hashes...` mirror.
|
||||
fn combined_json(
|
||||
&self,
|
||||
paths: &[PathBuf],
|
||||
combined_json_argument: &str,
|
||||
) -> anyhow::Result<CombinedJson>;
|
||||
|
||||
/// The `solc` Yul validator.
|
||||
fn validate_yul(&self, path: &Path) -> anyhow::Result<()>;
|
||||
|
||||
/// The `solc --version` mini-parser.
|
||||
fn version(&mut self) -> anyhow::Result<Version>;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
//! The Solidity compiler pipeline type.
|
||||
|
||||
use crate::solc::version::Version as SolcVersion;
|
||||
use crate::solc::Compiler as SolcCompiler;
|
||||
use crate::compiler;
|
||||
use crate::compiler::version::Version as SolcVersion;
|
||||
|
||||
/// The Solidity compiler pipeline type.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
@@ -17,7 +17,7 @@ pub enum Pipeline {
|
||||
impl Pipeline {
|
||||
/// We always use EVMLA for Solidity <=0.7, or if the user does not want to compile via Yul.
|
||||
pub fn new(solc_version: &SolcVersion, force_evmla: bool) -> Self {
|
||||
if solc_version.default < SolcCompiler::FIRST_YUL_VERSION || force_evmla {
|
||||
if solc_version.default < compiler::FIRST_YUL_VERSION || force_evmla {
|
||||
Self::EVMLA
|
||||
} else {
|
||||
Self::Yul
|
||||
|
||||
@@ -0,0 +1,287 @@
|
||||
//! The Solidity compiler.
|
||||
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::compiler::combined_json::CombinedJson;
|
||||
use crate::compiler::pipeline::Pipeline;
|
||||
use crate::compiler::standard_json::input::Input as StandardJsonInput;
|
||||
use crate::compiler::standard_json::output::Output as StandardJsonOutput;
|
||||
use crate::compiler::version::Version;
|
||||
|
||||
use super::Compiler;
|
||||
|
||||
/// The Solidity compiler.
|
||||
pub struct SolcCompiler {
|
||||
/// The binary executable name.
|
||||
pub executable: String,
|
||||
/// The lazily-initialized compiler version.
|
||||
pub version: Option<Version>,
|
||||
}
|
||||
|
||||
impl SolcCompiler {
|
||||
/// The default executable name.
|
||||
pub const DEFAULT_EXECUTABLE_NAME: &'static str = "solc";
|
||||
|
||||
/// A shortcut constructor.
|
||||
/// Different tools may use different `executable` names. For example, the integration tester
|
||||
/// uses `solc-<version>` format.
|
||||
pub fn new(executable: String) -> anyhow::Result<Self> {
|
||||
if let Err(error) = which::which(executable.as_str()) {
|
||||
anyhow::bail!(
|
||||
"The `{executable}` executable not found in ${{PATH}}: {}",
|
||||
error
|
||||
);
|
||||
}
|
||||
Ok(Self {
|
||||
executable,
|
||||
version: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Compiler for SolcCompiler {
|
||||
/// Compiles the Solidity `--standard-json` input into Yul IR.
|
||||
fn standard_json(
|
||||
&mut self,
|
||||
mut input: StandardJsonInput,
|
||||
pipeline: Pipeline,
|
||||
base_path: Option<String>,
|
||||
include_paths: Vec<String>,
|
||||
allow_paths: Option<String>,
|
||||
) -> anyhow::Result<StandardJsonOutput> {
|
||||
let version = self.version()?;
|
||||
|
||||
let mut command = std::process::Command::new(self.executable.as_str());
|
||||
command.stdin(std::process::Stdio::piped());
|
||||
command.stdout(std::process::Stdio::piped());
|
||||
command.arg("--standard-json");
|
||||
|
||||
if let Some(base_path) = base_path {
|
||||
command.arg("--base-path");
|
||||
command.arg(base_path);
|
||||
}
|
||||
for include_path in include_paths.into_iter() {
|
||||
command.arg("--include-path");
|
||||
command.arg(include_path);
|
||||
}
|
||||
if let Some(allow_paths) = allow_paths {
|
||||
command.arg("--allow-paths");
|
||||
command.arg(allow_paths);
|
||||
}
|
||||
|
||||
input.normalize(&version.default);
|
||||
|
||||
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default();
|
||||
|
||||
let input_json = serde_json::to_vec(&input).expect("Always valid");
|
||||
|
||||
let process = command.spawn().map_err(|error| {
|
||||
anyhow::anyhow!("{} subprocess spawning error: {:?}", self.executable, error)
|
||||
})?;
|
||||
process
|
||||
.stdin
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow::anyhow!("{} stdin getting error", self.executable))?
|
||||
.write_all(input_json.as_slice())
|
||||
.map_err(|error| {
|
||||
anyhow::anyhow!("{} stdin writing error: {:?}", self.executable, error)
|
||||
})?;
|
||||
|
||||
let output = process.wait_with_output().map_err(|error| {
|
||||
anyhow::anyhow!("{} subprocess output error: {:?}", self.executable, error)
|
||||
})?;
|
||||
if !output.status.success() {
|
||||
anyhow::bail!(
|
||||
"{} error: {}",
|
||||
self.executable,
|
||||
String::from_utf8_lossy(output.stderr.as_slice()).to_string()
|
||||
);
|
||||
}
|
||||
|
||||
let mut output: StandardJsonOutput =
|
||||
revive_common::deserialize_from_slice(output.stdout.as_slice()).map_err(|error| {
|
||||
anyhow::anyhow!(
|
||||
"{} subprocess output parsing error: {}\n{}",
|
||||
self.executable,
|
||||
error,
|
||||
revive_common::deserialize_from_slice::<serde_json::Value>(
|
||||
output.stdout.as_slice()
|
||||
)
|
||||
.map(|json| serde_json::to_string_pretty(&json).expect("Always valid"))
|
||||
.unwrap_or_else(
|
||||
|_| String::from_utf8_lossy(output.stdout.as_slice()).to_string()
|
||||
),
|
||||
)
|
||||
})?;
|
||||
output.preprocess_ast(&version, pipeline, suppressed_warnings.as_slice())?;
|
||||
output.remove_evm();
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
/// The `solc --combined-json abi,hashes...` mirror.
|
||||
fn combined_json(
|
||||
&self,
|
||||
paths: &[PathBuf],
|
||||
combined_json_argument: &str,
|
||||
) -> anyhow::Result<CombinedJson> {
|
||||
let mut command = std::process::Command::new(self.executable.as_str());
|
||||
command.args(paths);
|
||||
|
||||
let mut combined_json_flags = Vec::new();
|
||||
let mut combined_json_fake_flag_pushed = false;
|
||||
let mut filtered_flags = Vec::with_capacity(3);
|
||||
for flag in combined_json_argument.split(',') {
|
||||
match flag {
|
||||
flag @ "asm" | flag @ "bin" | flag @ "bin-runtime" => filtered_flags.push(flag),
|
||||
flag => combined_json_flags.push(flag),
|
||||
}
|
||||
}
|
||||
if combined_json_flags.is_empty() {
|
||||
combined_json_flags.push("ast");
|
||||
combined_json_fake_flag_pushed = true;
|
||||
}
|
||||
command.arg("--combined-json");
|
||||
command.arg(combined_json_flags.join(","));
|
||||
|
||||
let output = command.output().map_err(|error| {
|
||||
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()));
|
||||
anyhow::bail!(
|
||||
"{} error: {}",
|
||||
self.executable,
|
||||
String::from_utf8_lossy(output.stdout.as_slice()).to_string()
|
||||
);
|
||||
}
|
||||
|
||||
let mut combined_json: CombinedJson =
|
||||
revive_common::deserialize_from_slice(output.stdout.as_slice()).map_err(|error| {
|
||||
anyhow::anyhow!(
|
||||
"{} subprocess output parsing error: {}\n{}",
|
||||
self.executable,
|
||||
error,
|
||||
revive_common::deserialize_from_slice::<serde_json::Value>(
|
||||
output.stdout.as_slice()
|
||||
)
|
||||
.map(|json| serde_json::to_string_pretty(&json).expect("Always valid"))
|
||||
.unwrap_or_else(
|
||||
|_| String::from_utf8_lossy(output.stdout.as_slice()).to_string()
|
||||
),
|
||||
)
|
||||
})?;
|
||||
for filtered_flag in filtered_flags.into_iter() {
|
||||
for (_path, contract) in combined_json.contracts.iter_mut() {
|
||||
match filtered_flag {
|
||||
"asm" => contract.asm = Some(serde_json::Value::Null),
|
||||
"bin" => contract.bin = Some("".to_owned()),
|
||||
"bin-runtime" => contract.bin_runtime = Some("".to_owned()),
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
if combined_json_fake_flag_pushed {
|
||||
combined_json.source_list = None;
|
||||
combined_json.sources = None;
|
||||
}
|
||||
combined_json.remove_evm();
|
||||
|
||||
Ok(combined_json)
|
||||
}
|
||||
|
||||
/// The `solc` Yul validator.
|
||||
fn validate_yul(&self, path: &Path) -> anyhow::Result<()> {
|
||||
let mut command = std::process::Command::new(self.executable.as_str());
|
||||
command.arg("--strict-assembly");
|
||||
command.arg(path);
|
||||
|
||||
let output = command.output().map_err(|error| {
|
||||
anyhow::anyhow!("{} subprocess error: {:?}", self.executable, error)
|
||||
})?;
|
||||
if !output.status.success() {
|
||||
anyhow::bail!(
|
||||
"{} error: {}",
|
||||
self.executable,
|
||||
String::from_utf8_lossy(output.stderr.as_slice()).to_string()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The `solc --version` mini-parser.
|
||||
fn version(&mut self) -> anyhow::Result<Version> {
|
||||
if let Some(version) = self.version.as_ref() {
|
||||
return Ok(version.to_owned());
|
||||
}
|
||||
|
||||
let mut command = std::process::Command::new(self.executable.as_str());
|
||||
command.arg("--version");
|
||||
let output = command.output().map_err(|error| {
|
||||
anyhow::anyhow!("{} subprocess error: {:?}", self.executable, error)
|
||||
})?;
|
||||
if !output.status.success() {
|
||||
anyhow::bail!(
|
||||
"{} error: {}",
|
||||
self.executable,
|
||||
String::from_utf8_lossy(output.stderr.as_slice()).to_string()
|
||||
);
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8_lossy(output.stdout.as_slice());
|
||||
let long = stdout
|
||||
.lines()
|
||||
.nth(1)
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("{} version parsing: not enough lines", self.executable)
|
||||
})?
|
||||
.split(' ')
|
||||
.nth(1)
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"{} version parsing: not enough words in the 2nd line",
|
||||
self.executable
|
||||
)
|
||||
})?
|
||||
.to_owned();
|
||||
let default: semver::Version = long
|
||||
.split('+')
|
||||
.next()
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("{} version parsing: metadata dropping", self.executable)
|
||||
})?
|
||||
.parse()
|
||||
.map_err(|error| anyhow::anyhow!("{} version parsing: {}", self.executable, error))?;
|
||||
|
||||
let l2_revision: Option<semver::Version> = stdout
|
||||
.lines()
|
||||
.nth(2)
|
||||
.and_then(|line| line.split(' ').nth(1))
|
||||
.and_then(|line| line.split('-').nth(1))
|
||||
.and_then(|version| version.parse().ok());
|
||||
|
||||
let version = Version::new(long, default, l2_revision);
|
||||
if version.default < super::FIRST_SUPPORTED_VERSION {
|
||||
anyhow::bail!(
|
||||
"`solc` versions <{} are not supported, found {}",
|
||||
super::FIRST_SUPPORTED_VERSION,
|
||||
version.default
|
||||
);
|
||||
}
|
||||
if version.default > super::LAST_SUPPORTED_VERSION {
|
||||
anyhow::bail!(
|
||||
"`solc` versions >{} are not supported, found {}",
|
||||
super::LAST_SUPPORTED_VERSION,
|
||||
version.default
|
||||
);
|
||||
}
|
||||
|
||||
self.version = Some(version.clone());
|
||||
|
||||
Ok(version)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
//! The Solidity compiler.
|
||||
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::compiler::combined_json::CombinedJson;
|
||||
use crate::compiler::pipeline::Pipeline;
|
||||
use crate::compiler::standard_json::input::Input as StandardJsonInput;
|
||||
use crate::compiler::standard_json::output::Output as StandardJsonOutput;
|
||||
use crate::compiler::version::Version;
|
||||
use anyhow::Context;
|
||||
use std::ffi::{c_char, c_void, CStr, CString};
|
||||
|
||||
use super::Compiler;
|
||||
|
||||
extern "C" {
|
||||
fn soljson_version() -> *const c_char;
|
||||
fn soljson_compile(inputPtr: *const c_char, inputLen: usize) -> *const c_char;
|
||||
}
|
||||
|
||||
fn get_soljson_version() -> anyhow::Result<String> {
|
||||
unsafe {
|
||||
let version_ptr = soljson_version();
|
||||
let version = CStr::from_ptr(version_ptr)
|
||||
.to_str()
|
||||
.with_context(|| "Failed to convert C string to Rust string")
|
||||
.map(str::to_owned);
|
||||
libc::free(version_ptr as *mut c_void);
|
||||
Ok(version?)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compile_standard_json(input: String) -> anyhow::Result<String> {
|
||||
let c_input = CString::new(input).unwrap();
|
||||
let c_input_len = c_input.as_bytes().len();
|
||||
|
||||
unsafe {
|
||||
let output_ptr = soljson_compile(c_input.as_ptr(), c_input_len);
|
||||
let output_json = CStr::from_ptr(output_ptr)
|
||||
.to_str()
|
||||
.with_context(|| "Failed to convert C string to Rust string")
|
||||
.map(str::to_owned);
|
||||
libc::free(output_ptr as *mut c_void);
|
||||
Ok(output_json?)
|
||||
}
|
||||
}
|
||||
|
||||
/// The Solidity compiler.
|
||||
pub struct SoljsonCompiler {
|
||||
/// The lazily-initialized compiler version.
|
||||
pub version: Option<Version>,
|
||||
}
|
||||
|
||||
impl Compiler for SoljsonCompiler {
|
||||
/// Compiles the Solidity `--standard-json` input into Yul IR.
|
||||
fn standard_json(
|
||||
&mut self,
|
||||
mut input: StandardJsonInput,
|
||||
pipeline: Pipeline,
|
||||
_base_path: Option<String>,
|
||||
_include_paths: Vec<String>,
|
||||
_allow_paths: Option<String>,
|
||||
) -> anyhow::Result<StandardJsonOutput> {
|
||||
let version = self.version()?;
|
||||
let suppressed_warnings = input.suppressed_warnings.take().unwrap_or_default();
|
||||
|
||||
let input_json = serde_json::to_string(&input).expect("Always valid");
|
||||
let out = compile_standard_json(input_json)?;
|
||||
let mut output: StandardJsonOutput = revive_common::deserialize_from_slice(out.as_bytes())
|
||||
.map_err(|error| {
|
||||
anyhow::anyhow!(
|
||||
"Soljson output parsing error: {}\n{}",
|
||||
error,
|
||||
revive_common::deserialize_from_slice::<serde_json::Value>(out.as_bytes())
|
||||
.map(|json| serde_json::to_string_pretty(&json).expect("Always valid"))
|
||||
.unwrap_or_else(|_| String::from_utf8_lossy(out.as_bytes()).to_string()),
|
||||
)
|
||||
})?;
|
||||
output.preprocess_ast(&version, pipeline, suppressed_warnings.as_slice())?;
|
||||
output.remove_evm();
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
fn combined_json(
|
||||
&self,
|
||||
_paths: &[PathBuf],
|
||||
_combined_json_argument: &str,
|
||||
) -> anyhow::Result<CombinedJson> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn validate_yul(&self, _path: &Path) -> anyhow::Result<()> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
fn version(&mut self) -> anyhow::Result<Version> {
|
||||
let version = get_soljson_version()?;
|
||||
let long = version.clone();
|
||||
let default: semver::Version = version
|
||||
.split('+')
|
||||
.next()
|
||||
.ok_or_else(|| anyhow::anyhow!("Soljson version parsing: metadata dropping"))?
|
||||
.parse()
|
||||
.map_err(|error| anyhow::anyhow!("Soljson version parsing: {}", error))?;
|
||||
|
||||
let l2_revision: Option<semver::Version> = version
|
||||
.split('-')
|
||||
.nth(1)
|
||||
.and_then(|version| version.parse().ok());
|
||||
|
||||
let version = Version::new(long, default, l2_revision);
|
||||
if version.default < super::FIRST_SUPPORTED_VERSION {
|
||||
anyhow::bail!(
|
||||
"`Soljson` versions <{} are not supported, found {}",
|
||||
super::FIRST_SUPPORTED_VERSION,
|
||||
version.default
|
||||
);
|
||||
}
|
||||
if version.default > super::LAST_SUPPORTED_VERSION {
|
||||
anyhow::bail!(
|
||||
"`Soljson` versions >{} are not supported, found {}",
|
||||
super::LAST_SUPPORTED_VERSION,
|
||||
version.default
|
||||
);
|
||||
}
|
||||
|
||||
self.version = Some(version.clone());
|
||||
|
||||
Ok(version)
|
||||
}
|
||||
}
|
||||
@@ -8,15 +8,15 @@ use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use rayon::iter::IntoParallelIterator;
|
||||
use rayon::iter::ParallelIterator;
|
||||
#[cfg(feature = "parallel")]
|
||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::solc::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata;
|
||||
use crate::solc::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
|
||||
use crate::solc::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection;
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::standard_json::input::settings::metadata::Metadata as SolcStandardJsonInputSettingsMetadata;
|
||||
use crate::compiler::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
|
||||
use crate::compiler::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection;
|
||||
use crate::warning::Warning;
|
||||
|
||||
use self::language::Language;
|
||||
@@ -64,8 +64,12 @@ impl Input {
|
||||
via_ir: bool,
|
||||
suppressed_warnings: Option<Vec<Warning>>,
|
||||
) -> anyhow::Result<Self> {
|
||||
let sources = paths
|
||||
.into_par_iter()
|
||||
#[cfg(feature = "parallel")]
|
||||
let iter = paths.into_par_iter(); // Parallel iterator
|
||||
|
||||
#[cfg(not(feature = "parallel"))]
|
||||
let iter = paths.iter(); // Sequential iterator
|
||||
let sources = iter
|
||||
.map(|path| {
|
||||
let source = Source::try_from(path.as_path()).unwrap_or_else(|error| {
|
||||
panic!("Source code file {path:?} reading error: {error}")
|
||||
@@ -106,8 +110,12 @@ impl Input {
|
||||
via_ir: bool,
|
||||
suppressed_warnings: Option<Vec<Warning>>,
|
||||
) -> anyhow::Result<Self> {
|
||||
let sources = sources
|
||||
.into_par_iter()
|
||||
#[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();
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
/// The `solc --standard-json` expected output selection flag.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::collections::HashSet;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
use self::flag::Flag as SelectionFlag;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ pub mod file;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
use self::file::File as FileSelection;
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@ use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use sha3::Digest;
|
||||
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::version::Version as SolcVersion;
|
||||
use crate::evmla::assembly::instruction::Instruction;
|
||||
use crate::evmla::assembly::Assembly;
|
||||
use crate::project::contract::ir::IR as ProjectContractIR;
|
||||
use crate::project::contract::Contract as ProjectContract;
|
||||
use crate::project::Project;
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::solc::version::Version as SolcVersion;
|
||||
use crate::warning::Warning;
|
||||
use crate::yul::lexer::Lexer;
|
||||
use crate::yul::parser::statement::object::Object;
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::solc::standard_json::output::error::Error as SolcStandardJsonOutputError;
|
||||
use crate::solc::version::Version as SolcVersion;
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::standard_json::output::error::Error as SolcStandardJsonOutputError;
|
||||
use crate::compiler::version::Version as SolcVersion;
|
||||
use crate::warning::Warning;
|
||||
|
||||
/// The `solc --standard-json` output source.
|
||||
|
||||
@@ -1,33 +1,20 @@
|
||||
//! Common utility used for in frontend and integration tests.
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::solc::SolcCompiler;
|
||||
use crate::compiler::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
|
||||
use crate::compiler::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection;
|
||||
use crate::compiler::standard_json::input::Input as SolcStandardJsonInput;
|
||||
use crate::compiler::standard_json::output::contract::evm::bytecode::DeployedBytecode;
|
||||
use crate::compiler::standard_json::output::Output as SolcStandardJsonOutput;
|
||||
use crate::compiler::Compiler;
|
||||
use crate::project::Project;
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::solc::standard_json::input::settings::optimizer::Optimizer as SolcStandardJsonInputSettingsOptimizer;
|
||||
use crate::solc::standard_json::input::settings::selection::Selection as SolcStandardJsonInputSettingsSelection;
|
||||
use crate::solc::standard_json::input::Input as SolcStandardJsonInput;
|
||||
use crate::solc::standard_json::output::contract::evm::bytecode::DeployedBytecode;
|
||||
use crate::solc::standard_json::output::Output as SolcStandardJsonOutput;
|
||||
use crate::solc::Compiler as SolcCompiler;
|
||||
use crate::warning::Warning;
|
||||
|
||||
static PVM_BLOB_CACHE: Lazy<Mutex<HashMap<CachedBlob, Vec<u8>>>> = Lazy::new(Default::default);
|
||||
static EVM_BLOB_CACHE: Lazy<Mutex<HashMap<CachedBlob, Vec<u8>>>> = Lazy::new(Default::default);
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
struct CachedBlob {
|
||||
contract_name: String,
|
||||
solc_optimizer_enabled: bool,
|
||||
pipeline: SolcPipeline,
|
||||
}
|
||||
|
||||
/// Checks if the required executables are present in `${PATH}`.
|
||||
fn check_dependencies() {
|
||||
for executable in [
|
||||
@@ -76,7 +63,8 @@ pub fn build_solidity_with_options(
|
||||
|
||||
inkwell::support::enable_llvm_pretty_stack_trace();
|
||||
revive_llvm_context::initialize_target(revive_llvm_context::Target::PVM);
|
||||
let _ = crate::process::EXECUTABLE.set(PathBuf::from(crate::r#const::DEFAULT_EXECUTABLE_NAME));
|
||||
let _ = crate::process::native_process::EXECUTABLE
|
||||
.set(PathBuf::from(crate::r#const::DEFAULT_EXECUTABLE_NAME));
|
||||
|
||||
let mut solc = SolcCompiler::new(SolcCompiler::DEFAULT_EXECUTABLE_NAME.to_owned())?;
|
||||
let solc_version = solc.version()?;
|
||||
@@ -125,7 +113,8 @@ pub fn build_solidity_with_options_evm(
|
||||
|
||||
inkwell::support::enable_llvm_pretty_stack_trace();
|
||||
revive_llvm_context::initialize_target(revive_llvm_context::Target::PVM);
|
||||
let _ = crate::process::EXECUTABLE.set(PathBuf::from(crate::r#const::DEFAULT_EXECUTABLE_NAME));
|
||||
let _ = crate::process::native_process::EXECUTABLE
|
||||
.set(PathBuf::from(crate::r#const::DEFAULT_EXECUTABLE_NAME));
|
||||
|
||||
let mut solc = SolcCompiler::new(SolcCompiler::DEFAULT_EXECUTABLE_NAME.to_owned())?;
|
||||
let solc_version = solc.version()?;
|
||||
@@ -176,7 +165,8 @@ pub fn build_solidity_and_detect_missing_libraries(
|
||||
|
||||
inkwell::support::enable_llvm_pretty_stack_trace();
|
||||
revive_llvm_context::initialize_target(revive_llvm_context::Target::PVM);
|
||||
let _ = crate::process::EXECUTABLE.set(PathBuf::from(crate::r#const::DEFAULT_EXECUTABLE_NAME));
|
||||
let _ = crate::process::native_process::EXECUTABLE
|
||||
.set(PathBuf::from(crate::r#const::DEFAULT_EXECUTABLE_NAME));
|
||||
|
||||
let mut solc = SolcCompiler::new(SolcCompiler::DEFAULT_EXECUTABLE_NAME.to_owned())?;
|
||||
let solc_version = solc.version()?;
|
||||
@@ -221,8 +211,11 @@ pub fn build_yul(source_code: &str) -> anyhow::Result<()> {
|
||||
revive_llvm_context::initialize_target(revive_llvm_context::Target::PVM);
|
||||
let optimizer_settings = revive_llvm_context::OptimizerSettings::none();
|
||||
|
||||
let project =
|
||||
Project::try_from_yul_string(PathBuf::from("test.yul").as_path(), source_code, None)?;
|
||||
let project = Project::try_from_yul_string::<SolcCompiler>(
|
||||
PathBuf::from("test.yul").as_path(),
|
||||
source_code,
|
||||
None,
|
||||
)?;
|
||||
let _build = project.compile(optimizer_settings, false, false, false, None)?;
|
||||
|
||||
Ok(())
|
||||
@@ -274,91 +267,3 @@ pub fn check_solidity_warning(
|
||||
|
||||
Ok(contains_warning)
|
||||
}
|
||||
|
||||
/// Compile the blob of `contract_name` found in given `source_code`.
|
||||
/// The `solc` optimizer will be enabled
|
||||
pub fn compile_blob(contract_name: &str, source_code: &str) -> Vec<u8> {
|
||||
compile_blob_with_options(contract_name, source_code, true, SolcPipeline::Yul)
|
||||
}
|
||||
|
||||
/// Compile the EVM bin-runtime of `contract_name` found in given `source_code`.
|
||||
/// The `solc` optimizer will be enabled
|
||||
pub fn compile_evm_bin_runtime(contract_name: &str, source_code: &str) -> Vec<u8> {
|
||||
let pipeline = SolcPipeline::Yul;
|
||||
let solc_optimizer_enabled = true;
|
||||
let id = CachedBlob {
|
||||
contract_name: contract_name.to_owned(),
|
||||
pipeline,
|
||||
solc_optimizer_enabled,
|
||||
};
|
||||
|
||||
if let Some(blob) = EVM_BLOB_CACHE.lock().unwrap().get(&id) {
|
||||
return blob.clone();
|
||||
}
|
||||
|
||||
let file_name = "contract.sol";
|
||||
let contracts = build_solidity_with_options_evm(
|
||||
[(file_name.into(), source_code.into())].into(),
|
||||
Default::default(),
|
||||
None,
|
||||
pipeline,
|
||||
solc_optimizer_enabled,
|
||||
)
|
||||
.expect("source should compile");
|
||||
let bin_runtime = &contracts
|
||||
.get(contract_name)
|
||||
.unwrap_or_else(|| panic!("contract '{}' didn't produce bin-runtime", contract_name))
|
||||
.object;
|
||||
|
||||
let blob = hex::decode(bin_runtime).expect("bin-runtime shold be hex encoded");
|
||||
|
||||
EVM_BLOB_CACHE.lock().unwrap().insert(id, blob.clone());
|
||||
|
||||
blob
|
||||
}
|
||||
|
||||
/// Compile the blob of `contract_name` found in given `source_code`.
|
||||
pub fn compile_blob_with_options(
|
||||
contract_name: &str,
|
||||
source_code: &str,
|
||||
solc_optimizer_enabled: bool,
|
||||
pipeline: SolcPipeline,
|
||||
) -> Vec<u8> {
|
||||
let id = CachedBlob {
|
||||
contract_name: contract_name.to_owned(),
|
||||
solc_optimizer_enabled,
|
||||
pipeline,
|
||||
};
|
||||
|
||||
if let Some(blob) = PVM_BLOB_CACHE.lock().unwrap().get(&id) {
|
||||
return blob.clone();
|
||||
}
|
||||
|
||||
let file_name = "contract.sol";
|
||||
let contracts = build_solidity_with_options(
|
||||
[(file_name.into(), source_code.into())].into(),
|
||||
Default::default(),
|
||||
None,
|
||||
pipeline,
|
||||
revive_llvm_context::OptimizerSettings::cycles(),
|
||||
solc_optimizer_enabled,
|
||||
)
|
||||
.expect("source should compile")
|
||||
.contracts
|
||||
.expect("source should contain at least one contract");
|
||||
|
||||
let bytecode = contracts[file_name][contract_name]
|
||||
.evm
|
||||
.as_ref()
|
||||
.expect("source should produce EVM output")
|
||||
.bytecode
|
||||
.as_ref()
|
||||
.expect("source should produce assembly text")
|
||||
.object
|
||||
.as_str();
|
||||
let blob = hex::decode(bytecode).expect("hex encoding should always be valid");
|
||||
|
||||
PVM_BLOB_CACHE.lock().unwrap().insert(id, blob.clone());
|
||||
|
||||
blob
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
pub const MAIN_CODE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
#[test]
|
||||
fn yul() {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
pub const LIBRARY_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::warning::Warning;
|
||||
|
||||
pub const ECRECOVER_TEST_SOURCE: &str = r#"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
pub const SOURCE_CODE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
pub const CALLEE_TEST_SOURCE: &str = r#"
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "runtimeCode is not supported")]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::solc::pipeline::Pipeline as SolcPipeline;
|
||||
use crate::compiler::pipeline::Pipeline as SolcPipeline;
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "The `CODECOPY` instruction is not supported")]
|
||||
|
||||
Reference in New Issue
Block a user