mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-25 21:07:56 +00:00
186 lines
5.8 KiB
Rust
186 lines
5.8 KiB
Rust
//! The contract data.
|
|
|
|
pub mod ir;
|
|
pub mod metadata;
|
|
|
|
use std::collections::HashSet;
|
|
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
use sha3::Digest;
|
|
|
|
use revive_llvm_context::PolkaVMWriteLLVM;
|
|
|
|
use crate::build::contract::Contract as ContractBuild;
|
|
use crate::project::Project;
|
|
use crate::solc::version::Version as SolcVersion;
|
|
|
|
use self::ir::IR;
|
|
use self::metadata::Metadata;
|
|
|
|
/// The contract data.
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
pub struct Contract {
|
|
/// The absolute file path.
|
|
pub path: String,
|
|
/// The IR source code data.
|
|
pub ir: IR,
|
|
/// The metadata JSON.
|
|
pub metadata_json: serde_json::Value,
|
|
}
|
|
|
|
impl Contract {
|
|
/// A shortcut constructor.
|
|
pub fn new(
|
|
path: String,
|
|
source_hash: [u8; revive_common::BYTE_LENGTH_WORD],
|
|
source_version: SolcVersion,
|
|
ir: IR,
|
|
metadata_json: Option<serde_json::Value>,
|
|
) -> Self {
|
|
let metadata_json = metadata_json.unwrap_or_else(|| {
|
|
serde_json::json!({
|
|
"source_hash": hex::encode(source_hash.as_slice()),
|
|
"source_version": serde_json::to_value(&source_version).expect("Always valid"),
|
|
})
|
|
});
|
|
|
|
Self {
|
|
path,
|
|
ir,
|
|
metadata_json,
|
|
}
|
|
}
|
|
|
|
/// Returns the contract identifier, which is:
|
|
/// - the Yul object identifier for Yul
|
|
/// - the full contract path for EVM legacy assembly
|
|
/// - the module name for LLVM IR
|
|
pub fn identifier(&self) -> &str {
|
|
match self.ir {
|
|
IR::Yul(ref yul) => yul.object.identifier.as_str(),
|
|
IR::EVMLA(ref evm) => evm.assembly.full_path(),
|
|
IR::LLVMIR(ref llvm_ir) => llvm_ir.path.as_str(),
|
|
}
|
|
}
|
|
|
|
/// Extract factory dependencies.
|
|
pub fn drain_factory_dependencies(&mut self) -> HashSet<String> {
|
|
match self.ir {
|
|
IR::Yul(ref mut yul) => yul.object.factory_dependencies.drain().collect(),
|
|
IR::EVMLA(ref mut evm) => evm.assembly.factory_dependencies.drain().collect(),
|
|
IR::LLVMIR(_) => HashSet::new(),
|
|
}
|
|
}
|
|
|
|
/// Compiles the specified contract, setting its build artifacts.
|
|
pub fn compile(
|
|
mut self,
|
|
project: Project,
|
|
optimizer_settings: revive_llvm_context::OptimizerSettings,
|
|
include_metadata_hash: bool,
|
|
debug_config: Option<revive_llvm_context::DebugConfig>,
|
|
) -> anyhow::Result<ContractBuild> {
|
|
let llvm = inkwell::context::Context::create();
|
|
let optimizer = revive_llvm_context::Optimizer::new(optimizer_settings);
|
|
|
|
let version = project.version.clone();
|
|
let identifier = self.identifier().to_owned();
|
|
|
|
let metadata = Metadata::new(
|
|
self.metadata_json.take(),
|
|
version.long.clone(),
|
|
version.l2_revision.clone(),
|
|
optimizer.settings().to_owned(),
|
|
);
|
|
let metadata_json = serde_json::to_value(&metadata).expect("Always valid");
|
|
let metadata_hash: Option<[u8; revive_common::BYTE_LENGTH_WORD]> = if include_metadata_hash
|
|
{
|
|
let metadata_string = serde_json::to_string(&metadata).expect("Always valid");
|
|
Some(sha3::Keccak256::digest(metadata_string.as_bytes()).into())
|
|
} else {
|
|
None
|
|
};
|
|
|
|
let module = match self.ir {
|
|
IR::LLVMIR(ref llvm_ir) => {
|
|
let memory_buffer =
|
|
inkwell::memory_buffer::MemoryBuffer::create_from_memory_range_copy(
|
|
llvm_ir.source.as_bytes(),
|
|
self.path.as_str(),
|
|
);
|
|
llvm.create_module_from_ir(memory_buffer)
|
|
.map_err(|error| anyhow::anyhow!(error.to_string()))?
|
|
}
|
|
_ => llvm.create_module(self.path.as_str()),
|
|
};
|
|
let mut context = revive_llvm_context::PolkaVMContext::new(
|
|
&llvm,
|
|
module,
|
|
optimizer,
|
|
Some(project),
|
|
include_metadata_hash,
|
|
debug_config,
|
|
);
|
|
context.set_solidity_data(revive_llvm_context::PolkaVMContextSolidityData::default());
|
|
match self.ir {
|
|
IR::Yul(_) => {
|
|
context.set_yul_data(Default::default());
|
|
}
|
|
IR::EVMLA(_) => {
|
|
let evmla_data = revive_llvm_context::PolkaVMContextEVMLAData::new(version.default);
|
|
context.set_evmla_data(evmla_data);
|
|
}
|
|
IR::LLVMIR(_) => {}
|
|
}
|
|
|
|
let factory_dependencies = self.drain_factory_dependencies();
|
|
|
|
self.ir.declare(&mut context).map_err(|error| {
|
|
anyhow::anyhow!(
|
|
"The contract `{}` LLVM IR generator declaration pass error: {}",
|
|
self.path,
|
|
error
|
|
)
|
|
})?;
|
|
self.ir.into_llvm(&mut context).map_err(|error| {
|
|
anyhow::anyhow!(
|
|
"The contract `{}` LLVM IR generator definition pass error: {}",
|
|
self.path,
|
|
error
|
|
)
|
|
})?;
|
|
|
|
let build = context.build(self.path.as_str(), metadata_hash)?;
|
|
|
|
Ok(ContractBuild::new(
|
|
self.path,
|
|
identifier,
|
|
build,
|
|
metadata_json,
|
|
factory_dependencies,
|
|
))
|
|
}
|
|
|
|
/// Get the list of missing deployable libraries.
|
|
pub fn get_missing_libraries(&self) -> HashSet<String> {
|
|
self.ir.get_missing_libraries()
|
|
}
|
|
}
|
|
|
|
impl<D> PolkaVMWriteLLVM<D> for Contract
|
|
where
|
|
D: revive_llvm_context::PolkaVMDependency + Clone,
|
|
{
|
|
fn declare(
|
|
&mut self,
|
|
context: &mut revive_llvm_context::PolkaVMContext<D>,
|
|
) -> anyhow::Result<()> {
|
|
self.ir.declare(context)
|
|
}
|
|
|
|
fn into_llvm(self, context: &mut revive_llvm_context::PolkaVMContext<D>) -> anyhow::Result<()> {
|
|
self.ir.into_llvm(context)
|
|
}
|
|
}
|