mirror of
https://github.com/pezkuwichain/revive.git
synced 2026-04-22 04:27:58 +00:00
Initial wasm support
This commit is contained in:
Generated
+271
-8281
File diff suppressed because it is too large
Load Diff
+3
-28
@@ -2,30 +2,7 @@
|
||||
resolver = "2"
|
||||
members = ["crates/*"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
authors = [
|
||||
"Cyrill Leutwiler <cyrill@parity.io>",
|
||||
"Parity Technologies <admin@parity.io>",
|
||||
]
|
||||
license = "MIT/Apache-2.0"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/xermicus/revive"
|
||||
|
||||
[workspace.dependencies]
|
||||
revive-benchmarks = { version = "0.1.0", path = "crates/benchmarks" }
|
||||
revive-builtins = { version = "0.1.0", path = "crates/builtins" }
|
||||
revive-common = { version = "0.1.0", path = "crates/common" }
|
||||
revive-differential = { version = "0.1.0", path = "crates/differential" }
|
||||
revive-integration = { version = "0.1.0", path = "crates/integration" }
|
||||
revive-linker = { version = "0.1.0", path = "crates/linker" }
|
||||
lld-sys = { version = "0.1.0", path = "crates/lld-sys" }
|
||||
revive-llvm-context = { version = "0.1.0", path = "crates/llvm-context" }
|
||||
pallet-contracts-pvm-llapi = { version = "0.1.0", path = "crates/pallet-contracts-pvm-llapi" }
|
||||
revive-runner = { version = "0.1.0", path = "crates/runner" }
|
||||
revive-solidity = { version = "0.1.0", path = "crates/solidity" }
|
||||
revive-stdlib = { version = "0.1.0", path = "crates/stdlib" }
|
||||
|
||||
hex = "0.4"
|
||||
petgraph = "0.6"
|
||||
cc = "1.0"
|
||||
@@ -48,7 +25,7 @@ thiserror = "1.0"
|
||||
which = "5.0"
|
||||
path-slash = "0.2"
|
||||
rayon = "1.8"
|
||||
clap = { version = "4", default-features = false, features = ["derive"] }
|
||||
structopt = { version = "0.3", default-features = false }
|
||||
rand = "0.8"
|
||||
polkavm-common = { git = "https://github.com/koute/polkavm.git", rev = "360029e" }
|
||||
polkavm-linker = { git = "https://github.com/koute/polkavm.git", rev = "360029e" }
|
||||
@@ -60,16 +37,14 @@ env_logger = { version = "0.10.0", default-features = false }
|
||||
serde_stacker = "0.1"
|
||||
criterion = { version = "0.5", features = ["html_reports"] }
|
||||
log = { version = "0.4" }
|
||||
codec = { version = "3.6.12", default-features = false, package = "parity-scale-codec" }
|
||||
scale-info = { version = "2.11.1", default-features = false }
|
||||
polkadot-sdk = { git = "https://github.com/paritytech/polkadot-sdk", rev = "559fa1db0594a81d5dbf343613ba2f3fc16708da" }
|
||||
|
||||
# Benchmarking against EVM
|
||||
primitive-types = { version = "0.12", features = ["codec"] }
|
||||
evm-interpreter = { git = "https://github.com/xermicus/evm.git", branch = "separate-compilation" }
|
||||
|
||||
[workspace.dependencies.inkwell]
|
||||
version = "0.5"
|
||||
git = "https://github.com/TheDan64/inkwell.git"
|
||||
rev = "6c0fb56b3554e939f9ca61b465043d6a84fb7b95"
|
||||
default-features = false
|
||||
features = ["serde", "llvm18-0", "no-libffi-linking", "target-riscv"]
|
||||
|
||||
|
||||
@@ -1,10 +1,25 @@
|
||||
.PHONY: install format test test-solidity test-cli test-integration test-workspace clean docs docs-build
|
||||
|
||||
RUSTFLAGS_EMSCRIPTEN := \
|
||||
-Clink-arg=-sEXPORTED_FUNCTIONS=_main,_free,_malloc \
|
||||
-Clink-arg=-sNO_INVOKE_RUN \
|
||||
-Clink-arg=-sEXIT_RUNTIME \
|
||||
-Clink-arg=-sINITIAL_MEMORY=64MB \
|
||||
-Clink-arg=-sTOTAL_MEMORY=3GB \
|
||||
-Clink-arg=-sALLOW_MEMORY_GROWTH \
|
||||
-Clink-arg=-sEXPORTED_RUNTIME_METHODS=FS,callMain,stringToNewUTF8,cwrap \
|
||||
-Clink-arg=-sMODULARIZE \
|
||||
-Clink-arg=-sEXPORT_ES6 \
|
||||
-Clink-arg=--js-library=js/soljson_interface.js
|
||||
|
||||
install: install-bin install-npm
|
||||
|
||||
install-bin:
|
||||
cargo install --path crates/solidity
|
||||
|
||||
install-wasm:
|
||||
RUSTFLAGS='$(RUSTFLAGS_EMSCRIPTEN)' cargo install --target wasm32-unknown-emscripten --path crates/solidity
|
||||
|
||||
install-npm:
|
||||
npm install && npm fund
|
||||
|
||||
|
||||
@@ -29,6 +29,21 @@ resolc --version
|
||||
|
||||
`revive` requires a build of LLVM 18.1.4 or later including `compiler-rt`. Use the provided [build-llvm.sh](build-llvm.sh) build script to compile a compatible LLVM build locally in `$PWD/llvm18.0` (don't forget to add that to `$PATH` afterwards).
|
||||
|
||||
### Cross-compilation to WASM
|
||||
|
||||
Cross-compiles the Revive recompiler to WASM to enable it to run in a Node.js or browser environment.
|
||||
|
||||
Install [emscripten](https://emscripten.org/docs/getting_started/downloads.html). Tested on version 3.1.64.
|
||||
To build resolc.js execute:
|
||||
|
||||
```bash
|
||||
export EMSDK_ROOT=<PATH_TO_EMSCRIPTEN_SDK>
|
||||
bash emscripten-build-llvm.sh
|
||||
source $EMSDK_ROOT/emsdk_env.sh
|
||||
export LLVM_LINK_PREFIX=${PWD}/llvm18.0-emscripten
|
||||
make install-wasm
|
||||
```
|
||||
|
||||
### Development
|
||||
|
||||
Please consult the [Makefile](Makefile) targets to learn how to run tests and benchmarks.
|
||||
|
||||
+17
-29
@@ -5,25 +5,14 @@ set -euo pipefail
|
||||
INSTALL_DIR="${PWD}/llvm18.0"
|
||||
mkdir -p ${INSTALL_DIR}
|
||||
|
||||
|
||||
# Clone LLVM 18 (any revision after commit bd32aaa is supposed to work)
|
||||
if [ ! -d "llvm-project" ]; then
|
||||
git clone --depth 1 --branch release/18.x https://github.com/llvm/llvm-project.git
|
||||
fi
|
||||
|
||||
./clone-llvm.sh
|
||||
|
||||
# Build LLVM, clang
|
||||
LLVM_SRC_PREFIX=${PWD}/llvm-project
|
||||
LLVM_SRC_DIR=${LLVM_SRC_PREFIX}/llvm
|
||||
LLVM_BUILD_DIR=${PWD}/build/llvm
|
||||
if [ ! -d ${LLVM_BUILD_DIR} ] ; then
|
||||
mkdir -p ${LLVM_BUILD_DIR}
|
||||
fi
|
||||
cd llvm-project
|
||||
|
||||
cmake -G Ninja \
|
||||
-S ${LLVM_SRC_DIR} \
|
||||
-B ${LLVM_BUILD_DIR} \
|
||||
-DLLVM_ENABLE_ASSERTIONS=On \
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake -G Ninja -DLLVM_ENABLE_ASSERTIONS=On \
|
||||
-DLLVM_ENABLE_TERMINFO=Off \
|
||||
-DLLVM_ENABLE_LIBXML2=Off \
|
||||
-DLLVM_ENABLE_ZLIB=Off \
|
||||
@@ -31,17 +20,17 @@ cmake -G Ninja \
|
||||
-DLLVM_TARGETS_TO_BUILD='RISCV' \
|
||||
-DLLVM_ENABLE_ZSTD=Off \
|
||||
-DCMAKE_BUILD_TYPE=MinSizeRel \
|
||||
-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}
|
||||
-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \
|
||||
../llvm
|
||||
|
||||
ninja
|
||||
ninja install
|
||||
|
||||
cmake --build ${LLVM_BUILD_DIR}
|
||||
cmake --install ${LLVM_BUILD_DIR}
|
||||
|
||||
# Build compiler builtins
|
||||
COMPILER_RT_SRC_DIR=${LLVM_SRC_PREFIX}/compiler-rt
|
||||
COMPILER_RT_BUILD_DIR=${PWD}/build/compiler-rt
|
||||
if [ ! -d ${COMPILER_RT_BUILD_DIR} ] ; then
|
||||
mkdir -p ${COMPILER_RT_BUILD_DIR}
|
||||
fi
|
||||
cd ../compiler-rt
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
build_compiler_rt() {
|
||||
case "$1" in
|
||||
@@ -52,8 +41,6 @@ build_compiler_rt() {
|
||||
CFLAGS="--target=riscv${1} -march=rv${1}em -mabi=${TARGET_ABI} -mcpu=generic-rv${1} -nostdlib -nodefaultlibs"
|
||||
|
||||
cmake -G Ninja \
|
||||
-S ${COMPILER_RT_SRC_DIR} \
|
||||
-B ${COMPILER_RT_BUILD_DIR} \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR} \
|
||||
-DCOMPILER_RT_BUILD_BUILTINS=ON \
|
||||
@@ -78,10 +65,11 @@ build_compiler_rt() {
|
||||
-DCOMPILER_RT_TEST_COMPILER=${INSTALL_DIR}/bin/clang \
|
||||
-DCMAKE_CXX_FLAGS="${CFLAGS}" \
|
||||
-DCMAKE_SYSTEM_NAME=unknown \
|
||||
-DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON
|
||||
-DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON \
|
||||
..
|
||||
|
||||
cmake --build ${COMPILER_RT_BUILD_DIR}
|
||||
cmake --install ${COMPILER_RT_BUILD_DIR}
|
||||
ninja
|
||||
ninja install
|
||||
}
|
||||
|
||||
build_compiler_rt 32
|
||||
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Clone LLVM 18 (any revision after commit bd32aaa is supposed to work)
|
||||
if [ ! -d "llvm-project" ]; then
|
||||
git clone --depth 1 --branch release/18.x https://github.com/llvm/llvm-project.git
|
||||
fi
|
||||
@@ -1,11 +1,10 @@
|
||||
[package]
|
||||
name = "revive-benchmarks"
|
||||
version.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
description = "revive compiler benchmarks"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = [
|
||||
"Cyrill Leutwiler <cyrill@parity.io>",
|
||||
]
|
||||
|
||||
[features]
|
||||
default = ["bench-pvm-interpreter"]
|
||||
@@ -17,8 +16,8 @@ bench-extensive = []
|
||||
[dependencies]
|
||||
hex = { workspace = true }
|
||||
polkavm = { workspace = true }
|
||||
revive-integration = { workspace = true }
|
||||
revive-differential = { workspace = true, optional = true }
|
||||
revive-integration = { path = "../integration" }
|
||||
revive-differential = { path = "../differential", optional = true }
|
||||
alloy-primitives = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use alloy_primitives::U256;
|
||||
use criterion::{
|
||||
criterion_group, criterion_main, measurement::Measurement, BenchmarkGroup, BenchmarkId,
|
||||
Criterion,
|
||||
@@ -149,43 +148,28 @@ fn bench_triangle_number(c: &mut Criterion) {
|
||||
fn bench_fibonacci_recurisve(c: &mut Criterion) {
|
||||
let group = group(c, "FibonacciRecursive");
|
||||
#[cfg(feature = "bench-extensive")]
|
||||
let parameters = [24, 27, 31, 36, 39]
|
||||
.iter()
|
||||
.map(|p| U256::from(*p))
|
||||
.collect::<Vec<_>>();
|
||||
let parameters = &[24, 27, 31, 36, 39];
|
||||
#[cfg(not(feature = "bench-extensive"))]
|
||||
let parameters = [12, 16, 20]
|
||||
.iter()
|
||||
.map(|p| U256::from(*p))
|
||||
.collect::<Vec<_>>();
|
||||
let parameters = &[12, 16, 20];
|
||||
|
||||
bench(group, ¶meters, ¶meters, Contract::fib_recursive);
|
||||
bench(group, parameters, parameters, Contract::fib_recursive);
|
||||
}
|
||||
|
||||
fn bench_fibonacci_iterative(c: &mut Criterion) {
|
||||
let group = group(c, "FibonacciIterative");
|
||||
#[cfg(feature = "bench-extensive")]
|
||||
let parameters = [256, 162500, 650000, 6500000, 100000000, 400000000]
|
||||
.iter()
|
||||
.map(|p| U256::from(*p))
|
||||
.collect::<Vec<_>>();
|
||||
let parameters = &[256, 162500, 650000, 6500000, 100000000, 400000000];
|
||||
#[cfg(not(feature = "bench-extensive"))]
|
||||
let parameters = [64, 128, 256]
|
||||
.iter()
|
||||
.map(|p| U256::from(*p))
|
||||
.collect::<Vec<_>>();
|
||||
let parameters = &[64, 128, 256];
|
||||
|
||||
bench(group, ¶meters, ¶meters, Contract::fib_iterative);
|
||||
bench(group, parameters, parameters, Contract::fib_iterative);
|
||||
}
|
||||
|
||||
fn bench_fibonacci_binet(c: &mut Criterion) {
|
||||
let group = group(c, "FibonacciBinet");
|
||||
let parameters = [64, 128, 256]
|
||||
.iter()
|
||||
.map(|p| U256::from(*p))
|
||||
.collect::<Vec<_>>();
|
||||
let parameters = &[64, 128, 256];
|
||||
|
||||
bench(group, ¶meters, ¶meters, Contract::fib_binet);
|
||||
bench(group, parameters, parameters, Contract::fib_binet);
|
||||
}
|
||||
|
||||
fn bench_sha1(c: &mut Criterion) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use alloy_primitives::U256;
|
||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||
|
||||
use revive_integration::cases::Contract;
|
||||
@@ -118,9 +117,9 @@ fn bench_fibonacci_recursive(c: &mut Criterion) {
|
||||
c,
|
||||
"PrepareFibonacciRecursive",
|
||||
#[cfg(feature = "bench-evm")]
|
||||
Contract::fib_recursive(U256::ZERO).evm_runtime,
|
||||
Contract::fib_recursive(0).evm_runtime,
|
||||
#[cfg(any(feature = "bench-pvm-interpreter", feature = "bench-pvm"))]
|
||||
Contract::fib_recursive(U256::ZERO).pvm_runtime,
|
||||
Contract::fib_recursive(0).pvm_runtime,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -129,9 +128,9 @@ fn bench_fibonacci_iterative(c: &mut Criterion) {
|
||||
c,
|
||||
"PrepareFibonacciIterative",
|
||||
#[cfg(feature = "bench-evm")]
|
||||
Contract::fib_iterative(U256::ZERO).evm_runtime,
|
||||
Contract::fib_iterative(0).evm_runtime,
|
||||
#[cfg(any(feature = "bench-pvm-interpreter", feature = "bench-pvm"))]
|
||||
Contract::fib_iterative(U256::ZERO).pvm_runtime,
|
||||
Contract::fib_iterative(0).pvm_runtime,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -140,9 +139,9 @@ fn bench_fibonacci_binet(c: &mut Criterion) {
|
||||
c,
|
||||
"PrepareFibonacciBinet",
|
||||
#[cfg(feature = "bench-evm")]
|
||||
Contract::fib_binet(U256::ZERO).evm_runtime,
|
||||
Contract::fib_binet(0).evm_runtime,
|
||||
#[cfg(any(feature = "bench-pvm-interpreter", feature = "bench-pvm"))]
|
||||
Contract::fib_binet(U256::ZERO).pvm_runtime,
|
||||
Contract::fib_binet(0).pvm_runtime,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,7 @@ pub fn prepare_pvm(
|
||||
config.set_sandbox(Some(SandboxKind::Linux));
|
||||
|
||||
let (instance, export_index) = mock_runtime::prepare(code, Some(config));
|
||||
let transaction = State::default()
|
||||
.transaction()
|
||||
.with_default_account(code)
|
||||
.calldata(input);
|
||||
let transaction = State::default().transaction().calldata(input);
|
||||
|
||||
(transaction, instance, export_index)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
[package]
|
||||
name = "revive-builtins"
|
||||
version.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
description = "compiler builtins for the revive compiler"
|
||||
|
||||
[features]
|
||||
riscv-64 = []
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
[package]
|
||||
name = "revive-common"
|
||||
version.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
version = "0.1.0"
|
||||
authors = [
|
||||
"Cyrill Leutwiler <cyrill@parity.io>",
|
||||
"Oleksandr Zarudnyi <a.zarudnyy@matterlabs.dev>",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
description = "Shared constants of the revive compiler"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -7,9 +7,9 @@ pub const BYTE_LENGTH_BYTE: usize = 1;
|
||||
pub const BYTE_LENGTH_X32: usize = 4;
|
||||
|
||||
/// Native stack alignment size in bytes
|
||||
#[cfg(not(feature = "riscv-64"))]
|
||||
#[cfg(not(feautre = "riscv-64"))]
|
||||
pub const BYTE_LENGTH_STACK_ALIGN: usize = 4;
|
||||
#[cfg(feature = "riscv-64")]
|
||||
#[cfg(feautre = "riscv-64")]
|
||||
pub const BYTE_LENGTH_STACK_ALIGN: usize = 8;
|
||||
|
||||
/// The x86_64 word byte-length.
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
[package]
|
||||
name = "revive-differential"
|
||||
version.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
authors.workspace = true
|
||||
repository.workspace = true
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
evm-interpreter = { workspace = true }
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
[package]
|
||||
name = "revive-integration"
|
||||
version.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
description = "revive compiler integration test cases"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
polkavm = { workspace = true }
|
||||
@@ -14,11 +12,12 @@ alloy-sol-types = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
env_logger = { workspace = true }
|
||||
log = { workspace = true }
|
||||
once_cell = { workspace = true }
|
||||
|
||||
revive-solidity = { workspace = true }
|
||||
revive-differential = { workspace = true }
|
||||
revive-llvm-context = { workspace = true }
|
||||
revive-common = { workspace = true }
|
||||
revive-solidity = { path = "../solidity" }
|
||||
revive-differential = { path = "../differential" }
|
||||
revive-llvm-context = { path = "../llvm-context" }
|
||||
revive-common = { path = "../common" }
|
||||
|
||||
[dev-dependencies]
|
||||
sha1 = { workspace = true }
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
"Baseline": 934,
|
||||
"Computation": 4360,
|
||||
"DivisionArithmetics": 39824,
|
||||
"ERC20": 46471,
|
||||
"ERC20": 52825,
|
||||
"Events": 1749,
|
||||
"FibonacciIterative": 2973,
|
||||
"Flipper": 3563,
|
||||
"SHA1": 32543
|
||||
"SHA1": 32623
|
||||
}
|
||||
+375
-73
@@ -1,8 +1,6 @@
|
||||
use alloy_primitives::{Address, I256, U256};
|
||||
use alloy_sol_types::{sol, SolCall, SolConstructor};
|
||||
|
||||
use revive_solidity::test_utils::*;
|
||||
|
||||
use crate::mock_runtime::{CallOutput, State};
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -13,90 +11,45 @@ pub struct Contract {
|
||||
pub calldata: Vec<u8>,
|
||||
}
|
||||
|
||||
macro_rules! case {
|
||||
// Arguments:
|
||||
// 1. The file name, expect to live under "../contracts/"
|
||||
// 2. The Solidity contract name
|
||||
// 3. The derived Solidity function call name
|
||||
// 4. The method name on [Contract]
|
||||
// 5. Any parameters to the Solidity functions
|
||||
($file_name:literal, $contract_name:ident, $contract_method:ident, $method_name:ident, $( $v:ident: $t:ty ),* ) => {
|
||||
impl Contract {
|
||||
pub fn $method_name($($v: $t),*) -> Self {
|
||||
let code = include_str!(concat!("../contracts/", $file_name));
|
||||
let args = $contract_name::$contract_method::new(($($v,)*)).abi_encode();
|
||||
let name = stringify!($contract_name);
|
||||
Contract::build(args, name, code)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Arguments:
|
||||
// 1. The file name, expect to live under "../contracts/"
|
||||
// 2. The Solidity contract name
|
||||
// 3. Raw Calldata
|
||||
// 4. The method name on [Contract]
|
||||
($file_name:literal, $contract_name:literal, $calldata:expr, $method_name:ident) => {
|
||||
impl Contract {
|
||||
pub fn $method_name() -> Self {
|
||||
let code = include_str!(concat!("../contracts/", $file_name));
|
||||
Contract::build($calldata, $contract_name, code)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case!("Create.sol", "CreateA", vec![0; 4], create_a);
|
||||
case!("Create.sol", "CreateB", vec![0; 4], create_b);
|
||||
|
||||
sol!(contract Baseline { function baseline() public payable; });
|
||||
case!("Baseline.sol", Baseline, baselineCall, baseline,);
|
||||
|
||||
sol!(contract Flipper {
|
||||
constructor (bool);
|
||||
|
||||
function flip() public;
|
||||
});
|
||||
case!("flipper.sol", Flipper, flipCall, flipper,);
|
||||
case!("flipper.sol", Flipper, constructorCall, flipper_constructor, coin: bool);
|
||||
|
||||
sol!(contract Computation {
|
||||
function odd_product(int32 n) public pure returns (int64);
|
||||
function triangle_number(int64 n) public pure returns (int64 sum);
|
||||
});
|
||||
case!("Computation.sol", Computation, odd_productCall, odd_product, n: i32);
|
||||
case!("Computation.sol", Computation, triangle_numberCall, triangle_number, n: i64);
|
||||
|
||||
sol!(
|
||||
contract FibonacciRecursive {
|
||||
function fib3(uint n) public pure returns (uint);
|
||||
}
|
||||
);
|
||||
case!("Fibonacci.sol", FibonacciRecursive, fib3Call, fib_recursive, n: U256);
|
||||
|
||||
sol!(
|
||||
contract FibonacciIterative {
|
||||
function fib3(uint n) external pure returns (uint b);
|
||||
}
|
||||
);
|
||||
case!("Fibonacci.sol", FibonacciIterative, fib3Call, fib_iterative, n: U256);
|
||||
|
||||
sol!(
|
||||
contract FibonacciBinet {
|
||||
function fib3(uint n) external pure returns (uint a);
|
||||
}
|
||||
);
|
||||
case!("Fibonacci.sol", FibonacciBinet, fib3Call, fib_binet, n: U256);
|
||||
|
||||
sol!(
|
||||
contract SHA1 {
|
||||
function sha1(bytes memory data) public pure returns (bytes20 ret);
|
||||
}
|
||||
);
|
||||
case!("SHA1.sol", SHA1, sha1Call, sha1, pre: Vec<u8>);
|
||||
|
||||
sol!(
|
||||
contract ERC20 {
|
||||
interface IERC20 {
|
||||
function totalSupply() external view returns (uint);
|
||||
|
||||
function balanceOf(address account) external view returns (uint);
|
||||
@@ -120,7 +73,6 @@ sol!(
|
||||
event Approval(address indexed owner, address indexed spender, uint value);
|
||||
}
|
||||
);
|
||||
case!("ERC20.sol", ERC20, totalSupplyCall, erc20,);
|
||||
|
||||
sol!(
|
||||
contract Block {
|
||||
@@ -129,8 +81,6 @@ sol!(
|
||||
function number() public view returns (uint ret);
|
||||
}
|
||||
);
|
||||
case!("Block.sol", Block, numberCall, block_number,);
|
||||
case!("Block.sol", Block, timestampCall, block_timestamp,);
|
||||
|
||||
sol!(
|
||||
contract Context {
|
||||
@@ -139,8 +89,6 @@ sol!(
|
||||
function caller() public pure returns (address);
|
||||
}
|
||||
);
|
||||
case!("Context.sol", Context, address_thisCall, context_address,);
|
||||
case!("Context.sol", Context, callerCall, context_caller,);
|
||||
|
||||
sol!(
|
||||
contract DivisionArithmetics {
|
||||
@@ -153,17 +101,12 @@ sol!(
|
||||
function smod(int n, int d) public pure returns (int r);
|
||||
}
|
||||
);
|
||||
case!("DivisionArithmetics.sol", DivisionArithmetics, divCall, division_arithmetics_div, n: U256, d: U256);
|
||||
case!("DivisionArithmetics.sol", DivisionArithmetics, sdivCall, division_arithmetics_sdiv, n: I256, d: I256);
|
||||
case!("DivisionArithmetics.sol", DivisionArithmetics, modCall, division_arithmetics_mod, n: U256, d: U256);
|
||||
case!("DivisionArithmetics.sol", DivisionArithmetics, smodCall, division_arithmetics_smod, n: I256, d: I256);
|
||||
|
||||
sol!(
|
||||
contract MStore8 {
|
||||
function mStore8(uint value) public pure returns (uint256 word);
|
||||
}
|
||||
);
|
||||
case!("mStore8.sol", MStore8, mStore8Call, mstore8, value: U256);
|
||||
|
||||
sol!(
|
||||
contract Events {
|
||||
@@ -173,7 +116,12 @@ sol!(
|
||||
function emitEvent(uint topics) public;
|
||||
}
|
||||
);
|
||||
case!("Events.sol", Events, emitEventCall, event, topics: U256);
|
||||
|
||||
sol!(
|
||||
contract CreateB {
|
||||
fallback() external payable;
|
||||
}
|
||||
);
|
||||
|
||||
sol!(
|
||||
contract ExtCode {
|
||||
@@ -182,15 +130,12 @@ sol!(
|
||||
function CodeSize() public pure returns (uint ret);
|
||||
}
|
||||
);
|
||||
case!("ExtCode.sol", ExtCode, ExtCodeSizeCall, ext_code_size, address: Address);
|
||||
case!("ExtCode.sol", ExtCode, CodeSizeCall, code_size,);
|
||||
|
||||
sol!(
|
||||
contract MCopy {
|
||||
function memcpy(bytes memory payload) public pure returns (bytes memory);
|
||||
}
|
||||
);
|
||||
case!("MCopy.sol", MCopy, memcpyCall, memcpy, payload: Vec<u8>);
|
||||
|
||||
sol!(
|
||||
contract Call {
|
||||
@@ -204,30 +149,24 @@ sol!(
|
||||
) public payable returns (bytes memory);
|
||||
}
|
||||
);
|
||||
case!("Call.sol", Call, value_transferCall, call_value_transfer, destination: Address);
|
||||
case!("Call.sol", Call, callCall, call_call, destination: Address, payload: Vec<u8>);
|
||||
case!("Call.sol", "Call", vec![], call_constructor);
|
||||
|
||||
sol!(
|
||||
contract Value {
|
||||
function balance_of(address _address) public view returns (uint ret);
|
||||
}
|
||||
);
|
||||
case!("Value.sol", Value, balance_ofCall, value_balance_of, address: Address);
|
||||
|
||||
sol!(
|
||||
contract Bitwise {
|
||||
function opByte(uint i, uint x) public payable returns (uint ret);
|
||||
}
|
||||
);
|
||||
case!("Bitwise.sol", Bitwise, opByteCall, bitwise_byte, index: U256, value: U256);
|
||||
|
||||
sol!(
|
||||
contract Storage {
|
||||
function transient(uint value) public returns (uint ret);
|
||||
}
|
||||
);
|
||||
case!("Storage.sol", Storage, transientCall, storage_transient, value: U256);
|
||||
|
||||
impl Contract {
|
||||
/// Execute the contract.
|
||||
@@ -248,12 +187,375 @@ impl Contract {
|
||||
.call()
|
||||
}
|
||||
|
||||
fn build(calldata: Vec<u8>, name: &'static str, code: &str) -> Self {
|
||||
pub fn baseline() -> Self {
|
||||
let code = include_str!("../contracts/Baseline.sol");
|
||||
let name = "Baseline";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: compile_blob(name, code),
|
||||
calldata,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Baseline::baselineCall::new(()).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn odd_product(n: i32) -> Self {
|
||||
let code = include_str!("../contracts/Computation.sol");
|
||||
let name = "Computation";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Computation::odd_productCall::new((n,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn triangle_number(n: i64) -> Self {
|
||||
let code = include_str!("../contracts/Computation.sol");
|
||||
let name = "Computation";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Computation::triangle_numberCall::new((n,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fib_recursive(n: u32) -> Self {
|
||||
let code = include_str!("../contracts/Fibonacci.sol");
|
||||
let name = "FibonacciRecursive";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: FibonacciRecursive::fib3Call::new((U256::from(n),)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fib_iterative(n: u32) -> Self {
|
||||
let code = include_str!("../contracts/Fibonacci.sol");
|
||||
let name = "FibonacciIterative";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: FibonacciIterative::fib3Call::new((U256::from(n),)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fib_binet(n: u32) -> Self {
|
||||
let code = include_str!("../contracts/Fibonacci.sol");
|
||||
let name = "FibonacciBinet";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: FibonacciBinet::fib3Call::new((U256::from(n),)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sha1(pre: Vec<u8>) -> Self {
|
||||
let code = include_str!("../contracts/SHA1.sol");
|
||||
let name = "SHA1";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: SHA1::sha1Call::new((pre,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flipper() -> Self {
|
||||
let code = include_str!("../contracts/flipper.sol");
|
||||
let name = "Flipper";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Flipper::flipCall::new(()).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flipper_constructor(coin: bool) -> Self {
|
||||
let code = include_str!("../contracts/flipper.sol");
|
||||
let name = "Flipper";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Flipper::constructorCall::new((coin,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn erc20() -> Self {
|
||||
let code = include_str!("../contracts/ERC20.sol");
|
||||
let name = "ERC20";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: IERC20::totalSupplyCall::new(()).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn block_number() -> Self {
|
||||
let code = include_str!("../contracts/Block.sol");
|
||||
let name = "Block";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Block::numberCall::new(()).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn block_timestamp() -> Self {
|
||||
let code = include_str!("../contracts/Block.sol");
|
||||
let name = "Block";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Block::timestampCall::new(()).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn context_address() -> Self {
|
||||
let code = include_str!("../contracts/Context.sol");
|
||||
let name = "Context";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Context::address_thisCall::new(()).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn context_caller() -> Self {
|
||||
let code = include_str!("../contracts/Context.sol");
|
||||
let name = "Context";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Context::callerCall::new(()).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn division_arithmetics_div(n: U256, d: U256) -> Self {
|
||||
let code = include_str!("../contracts/DivisionArithmetics.sol");
|
||||
let name = "DivisionArithmetics";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: DivisionArithmetics::divCall::new((n, d)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn division_arithmetics_sdiv(n: I256, d: I256) -> Self {
|
||||
let code = include_str!("../contracts/DivisionArithmetics.sol");
|
||||
let name = "DivisionArithmetics";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: DivisionArithmetics::sdivCall::new((n, d)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn division_arithmetics_mod(n: U256, d: U256) -> Self {
|
||||
let code = include_str!("../contracts/DivisionArithmetics.sol");
|
||||
let name = "DivisionArithmetics";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: DivisionArithmetics::modCall::new((n, d)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn division_arithmetics_smod(n: I256, d: I256) -> Self {
|
||||
let code = include_str!("../contracts/DivisionArithmetics.sol");
|
||||
let name = "DivisionArithmetics";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: DivisionArithmetics::smodCall::new((n, d)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mstore8(value: U256) -> Self {
|
||||
let code = include_str!("../contracts/mStore8.sol");
|
||||
let name = "MStore8";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: MStore8::mStore8Call::new((value,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(topics: U256) -> Self {
|
||||
let code = include_str!("../contracts/Events.sol");
|
||||
let name = "Events";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Events::emitEventCall::new((topics,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_a() -> Self {
|
||||
let code = include_str!("../contracts/Create.sol");
|
||||
let name = "CreateA";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: vec![0; 4],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_b() -> Self {
|
||||
let code = include_str!("../contracts/Create.sol");
|
||||
let name = "CreateB";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: vec![0; 4],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ext_code_size(address: Address) -> Self {
|
||||
let code = include_str!("../contracts/ExtCode.sol");
|
||||
let name = "ExtCode";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: ExtCode::ExtCodeSizeCall::new((address,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn code_size() -> Self {
|
||||
let code = include_str!("../contracts/ExtCode.sol");
|
||||
let name = "ExtCode";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: ExtCode::CodeSizeCall::new(()).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn memcpy(payload: Vec<u8>) -> Self {
|
||||
let code = include_str!("../contracts/MCopy.sol");
|
||||
let name = "MCopy";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: MCopy::memcpyCall::new((payload,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_value_transfer(destination: Address) -> Self {
|
||||
let code = include_str!("../contracts/Call.sol");
|
||||
let name = "Call";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Call::value_transferCall::new((destination,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_call(callee: Address, payload: Vec<u8>) -> Self {
|
||||
let code = include_str!("../contracts/Call.sol");
|
||||
let name = "Call";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Call::callCall::new((callee, payload)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn call_constructor() -> Self {
|
||||
let code = include_str!("../contracts/Call.sol");
|
||||
let name = "Call";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn value_balance_of(address: Address) -> Self {
|
||||
let code = include_str!("../contracts/Value.sol");
|
||||
let name = "Value";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Value::balance_ofCall::new((address,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bitwise_byte(index: U256, value: U256) -> Self {
|
||||
let code = include_str!("../contracts/Bitwise.sol");
|
||||
let name = "Bitwise";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Bitwise::opByteCall::new((index, value)).abi_encode(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn storage_transient(value: U256) -> Self {
|
||||
let code = include_str!("../contracts/Storage.sol");
|
||||
let name = "Storage";
|
||||
|
||||
Self {
|
||||
name,
|
||||
evm_runtime: crate::compile_evm_bin_runtime(name, code),
|
||||
pvm_runtime: crate::compile_blob(name, code),
|
||||
calldata: Storage::transientCall::new((value,)).abi_encode(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -303,7 +605,7 @@ mod tests {
|
||||
Contract::baseline as fn() -> Contract,
|
||||
Contract::flipper as fn() -> Contract,
|
||||
(|| Contract::odd_product(0)) as fn() -> Contract,
|
||||
(|| Contract::fib_iterative(U256::ZERO)) as fn() -> Contract,
|
||||
(|| Contract::fib_iterative(0)) as fn() -> Contract,
|
||||
Contract::erc20 as fn() -> Contract,
|
||||
(|| Contract::sha1(Vec::new())) as fn() -> Contract,
|
||||
(|| Contract::division_arithmetics_div(U256::ZERO, U256::ZERO)) as fn() -> Contract,
|
||||
|
||||
@@ -4,12 +4,120 @@ use mock_runtime::{CallOutput, State};
|
||||
|
||||
use crate::mock_runtime::{Event, ReturnFlags};
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
|
||||
pub mod cases;
|
||||
pub mod mock_runtime;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub(crate) static PVM_BLOB_CACHE: Lazy<Mutex<HashMap<CompiledBlobId, Vec<u8>>>> =
|
||||
Lazy::new(Default::default);
|
||||
pub(crate) static EVM_BLOB_CACHE: Lazy<Mutex<HashMap<CompiledBlobId, Vec<u8>>>> =
|
||||
Lazy::new(Default::default);
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
struct CompiledBlobId {
|
||||
contract_name: String,
|
||||
solc_optimizer_enabled: bool,
|
||||
pipeline: revive_solidity::SolcPipeline,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
revive_solidity::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 = revive_solidity::SolcPipeline::Yul;
|
||||
let solc_optimizer_enabled = true;
|
||||
let id = CompiledBlobId {
|
||||
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 = revive_solidity::test_utils::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: revive_solidity::SolcPipeline,
|
||||
) -> Vec<u8> {
|
||||
let id = CompiledBlobId {
|
||||
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 = revive_solidity::test_utils::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
|
||||
}
|
||||
|
||||
pub fn assert_success(contract: &Contract, differential: bool) -> (State, CallOutput) {
|
||||
let (state, output) = contract.execute();
|
||||
assert_eq!(output.flags, ReturnFlags::Success);
|
||||
|
||||
@@ -5,8 +5,6 @@ use alloy_sol_types::{sol, SolCall, SolValue};
|
||||
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
|
||||
use sha1::Digest;
|
||||
|
||||
use revive_solidity::test_utils::*;
|
||||
|
||||
use crate::{
|
||||
assert_success,
|
||||
cases::Contract,
|
||||
@@ -18,9 +16,9 @@ fn fibonacci() {
|
||||
let parameter = 6;
|
||||
|
||||
for contract in [
|
||||
Contract::fib_recursive(U256::from(parameter)),
|
||||
Contract::fib_iterative(U256::from(parameter)),
|
||||
Contract::fib_binet(U256::from(parameter)),
|
||||
Contract::fib_recursive(parameter),
|
||||
Contract::fib_iterative(parameter),
|
||||
Contract::fib_binet(parameter),
|
||||
] {
|
||||
let (_, output) = assert_success(&contract, true);
|
||||
let received = U256::from_be_bytes::<32>(output.data.try_into().unwrap());
|
||||
@@ -64,7 +62,7 @@ fn hash_keccak_256() {
|
||||
hash = keccak256(bytes(_pre));
|
||||
}
|
||||
}"#;
|
||||
let code = compile_blob("TestSha3", source);
|
||||
let code = crate::compile_blob("TestSha3", source);
|
||||
|
||||
let param = "hello";
|
||||
let input = TestSha3::testCall::new((param.to_string(),)).abi_encode();
|
||||
@@ -84,7 +82,7 @@ fn hash_keccak_256() {
|
||||
|
||||
#[test]
|
||||
fn erc20() {
|
||||
let _ = compile_blob("ERC20", include_str!("../contracts/ERC20.sol"));
|
||||
let _ = crate::compile_blob("ERC20", include_str!("../contracts/ERC20.sol"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -111,7 +109,7 @@ fn msize_plain() {
|
||||
function mSize() public pure returns (uint);
|
||||
}
|
||||
);
|
||||
let code = compile_blob_with_options(
|
||||
let code = crate::compile_blob_with_options(
|
||||
"MSize",
|
||||
include_str!("../contracts/MSize.sol"),
|
||||
false,
|
||||
@@ -140,7 +138,7 @@ fn transferred_value() {
|
||||
function value() public payable returns (uint);
|
||||
}
|
||||
);
|
||||
let code = compile_blob("Value", include_str!("../contracts/Value.sol"));
|
||||
let code = crate::compile_blob("Value", include_str!("../contracts/Value.sol"));
|
||||
|
||||
let (_, output) = State::default()
|
||||
.transaction()
|
||||
@@ -164,7 +162,7 @@ fn msize_non_word_sized_access() {
|
||||
function mStore100() public pure returns (uint);
|
||||
}
|
||||
);
|
||||
let code = compile_blob_with_options(
|
||||
let code = crate::compile_blob_with_options(
|
||||
"MSize",
|
||||
include_str!("../contracts/MSize.sol"),
|
||||
false,
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
[package]
|
||||
name = "revive-linker"
|
||||
version.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
description = "revive compiler linker utils"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[features]
|
||||
riscv-64 = []
|
||||
@@ -18,5 +16,5 @@ polkavm-common = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
|
||||
revive-builtins = { workspace = true }
|
||||
lld-sys = { workspace = true }
|
||||
revive-builtins = { path = "../builtins" }
|
||||
lld-sys = { path = "../lld-sys" }
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
[package]
|
||||
name = "lld-sys"
|
||||
version.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
authors.workspace = true
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
description = "bindings for ld.lld core"
|
||||
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libc = { workspace = true }
|
||||
|
||||
+111
-6
@@ -1,5 +1,19 @@
|
||||
fn llvm_config(arg: &str) -> String {
|
||||
let output = std::process::Command::new("llvm-config")
|
||||
use std::{
|
||||
env,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
const LLVM_LINK_PREFIX: &str = "LLVM_LINK_PREFIX";
|
||||
|
||||
fn locate_llvm_config() -> PathBuf {
|
||||
let prefix = env::var_os(LLVM_LINK_PREFIX)
|
||||
.map(|p| PathBuf::from(p).join("bin"))
|
||||
.unwrap_or_default();
|
||||
prefix.join("llvm-config")
|
||||
}
|
||||
|
||||
fn llvm_config(llvm_config_path: &Path, arg: &str) -> String {
|
||||
let output = std::process::Command::new(llvm_config_path)
|
||||
.args([arg])
|
||||
.output()
|
||||
.unwrap_or_else(|_| panic!("`llvm-config {arg}` failed"));
|
||||
@@ -8,8 +22,11 @@ fn llvm_config(arg: &str) -> String {
|
||||
.unwrap_or_else(|_| panic!("output of `llvm-config {arg}` should be utf8"))
|
||||
}
|
||||
|
||||
fn set_rustc_link_flags() {
|
||||
println!("cargo:rustc-link-search=native={}", llvm_config("--libdir"));
|
||||
fn set_rustc_link_flags(llvm_config_path: &Path) {
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}",
|
||||
llvm_config(llvm_config_path, "--libdir")
|
||||
);
|
||||
|
||||
for lib in [
|
||||
"lldELF",
|
||||
@@ -22,6 +39,90 @@ fn set_rustc_link_flags() {
|
||||
"LLVMTargetParser",
|
||||
"LLVMBinaryFormat",
|
||||
"LLVMDemangle",
|
||||
// Required by `llvm-sys`
|
||||
"LLVMWindowsManifest",
|
||||
"LLVMXRay",
|
||||
"LLVMLibDriver",
|
||||
"LLVMDlltoolDriver",
|
||||
"LLVMTextAPIBinaryReader",
|
||||
"LLVMCoverage",
|
||||
"LLVMLineEditor",
|
||||
"LLVMRISCVTargetMCA",
|
||||
"LLVMRISCVDisassembler",
|
||||
"LLVMRISCVAsmParser",
|
||||
"LLVMRISCVCodeGen",
|
||||
"LLVMRISCVDesc",
|
||||
"LLVMRISCVInfo",
|
||||
"LLVMOrcDebugging",
|
||||
"LLVMOrcJIT",
|
||||
"LLVMWindowsDriver",
|
||||
"LLVMMCJIT",
|
||||
"LLVMJITLink",
|
||||
"LLVMInterpreter",
|
||||
"LLVMExecutionEngine",
|
||||
"LLVMRuntimeDyld",
|
||||
"LLVMOrcTargetProcess",
|
||||
"LLVMOrcShared",
|
||||
"LLVMDWP",
|
||||
"LLVMDebugInfoLogicalView",
|
||||
"LLVMDebugInfoGSYM",
|
||||
"LLVMOption",
|
||||
"LLVMObjectYAML",
|
||||
"LLVMObjCopy",
|
||||
"LLVMMCA",
|
||||
"LLVMMCDisassembler",
|
||||
"LLVMPasses",
|
||||
"LLVMHipStdPar",
|
||||
"LLVMCFGuard",
|
||||
"LLVMCoroutines",
|
||||
"LLVMipo",
|
||||
"LLVMVectorize",
|
||||
"LLVMInstrumentation",
|
||||
"LLVMFrontendOpenMP",
|
||||
"LLVMFrontendOffloading",
|
||||
"LLVMFrontendOpenACC",
|
||||
"LLVMFrontendHLSL",
|
||||
"LLVMFrontendDriver",
|
||||
"LLVMExtensions",
|
||||
"LLVMDWARFLinkerParallel",
|
||||
"LLVMDWARFLinkerClassic",
|
||||
"LLVMDWARFLinker",
|
||||
"LLVMGlobalISel",
|
||||
"LLVMMIRParser",
|
||||
"LLVMAsmPrinter",
|
||||
"LLVMSelectionDAG",
|
||||
"LLVMCodeGen",
|
||||
"LLVMTarget",
|
||||
"LLVMObjCARCOpts",
|
||||
"LLVMCodeGenTypes",
|
||||
"LLVMIRPrinter",
|
||||
"LLVMInterfaceStub",
|
||||
"LLVMFileCheck",
|
||||
"LLVMFuzzMutate",
|
||||
"LLVMScalarOpts",
|
||||
"LLVMInstCombine",
|
||||
"LLVMAggressiveInstCombine",
|
||||
"LLVMTransformUtils",
|
||||
"LLVMBitWriter",
|
||||
"LLVMAnalysis",
|
||||
"LLVMProfileData",
|
||||
"LLVMSymbolize",
|
||||
"LLVMDebugInfoBTF",
|
||||
"LLVMDebugInfoPDB",
|
||||
"LLVMDebugInfoMSF",
|
||||
"LLVMDebugInfoDWARF",
|
||||
"LLVMObject",
|
||||
"LLVMTextAPI",
|
||||
"LLVMMCParser",
|
||||
"LLVMIRReader",
|
||||
"LLVMAsmParser",
|
||||
"LLVMMC",
|
||||
"LLVMDebugInfoCodeView",
|
||||
"LLVMBitReader",
|
||||
"LLVMFuzzerCLI",
|
||||
"LLVMRemarks",
|
||||
"LLVMBitstreamReader",
|
||||
"LLVMTableGen",
|
||||
] {
|
||||
println!("cargo:rustc-link-lib=static={lib}");
|
||||
}
|
||||
@@ -34,7 +135,11 @@ fn set_rustc_link_flags() {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
llvm_config("--cxxflags")
|
||||
println!("cargo:rerun-if-env-changed={}", LLVM_LINK_PREFIX);
|
||||
|
||||
let llvm_config_path = locate_llvm_config();
|
||||
|
||||
llvm_config(&llvm_config_path, "--cxxflags")
|
||||
.split_whitespace()
|
||||
.fold(&mut cc::Build::new(), |builder, flag| builder.flag(flag))
|
||||
.flag("-Wno-unused-parameter")
|
||||
@@ -42,7 +147,7 @@ fn main() {
|
||||
.file("src/linker.cpp")
|
||||
.compile("liblinker.a");
|
||||
|
||||
set_rustc_link_flags();
|
||||
set_rustc_link_flags(&llvm_config_path);
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
[package]
|
||||
name = "revive-llvm-context"
|
||||
version.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
version = "1.4.1"
|
||||
authors = [
|
||||
"Oleksandr Zarudnyi <a.zarudnyy@matterlabs.dev>",
|
||||
"Cyrill Leutwiler <cyrill@parity.io>",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
description = "Shared front end code of the revive PolkaVM compilers"
|
||||
|
||||
[lib]
|
||||
@@ -33,8 +32,10 @@ inkwell = { workspace = true }
|
||||
polkavm-disassembler = { workspace = true }
|
||||
polkavm-common = { workspace = true }
|
||||
|
||||
revive-common = { workspace = true }
|
||||
pallet-contracts-pvm-llapi = { workspace = true }
|
||||
revive-linker = { workspace = true }
|
||||
revive-builtins = { workspace = true }
|
||||
revive-stdlib = { workspace = true }
|
||||
zkevm_opcode_defs = { git = "https://github.com/matter-labs/era-zkevm_opcode_defs", branch = "v1.4.1" }
|
||||
revive-common = { path = "../common" }
|
||||
|
||||
pallet-contracts-pvm-llapi = { path = "../pallet-contracts-pvm-llapi" }
|
||||
revive-linker = { path = "../linker" }
|
||||
revive-builtins = { path = "../builtins" }
|
||||
revive-stdlib = { path = "../stdlib" }
|
||||
|
||||
@@ -15,9 +15,6 @@ pub enum IRType {
|
||||
LLVM,
|
||||
/// Whether to dump the assembly code.
|
||||
Assembly,
|
||||
/// Whether to jump JSON
|
||||
#[cfg(debug_assertions)]
|
||||
JSON,
|
||||
}
|
||||
|
||||
impl IRType {
|
||||
@@ -29,8 +26,6 @@ impl IRType {
|
||||
Self::EVMLA => revive_common::EXTENSION_EVMLA,
|
||||
Self::LLVM => revive_common::EXTENSION_LLVM_SOURCE,
|
||||
Self::Assembly => revive_common::EXTENSION_POLKAVM_ASSEMBLY,
|
||||
#[cfg(debug_assertions)]
|
||||
Self::JSON => revive_common::EXTENSION_JSON,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,22 +94,6 @@ impl DebugConfig {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Dumps the stage output as a json file suitable for use with --recursive-process
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn dump_stage_output(
|
||||
&self,
|
||||
contract_path: &str,
|
||||
contract_suffix: Option<&str>,
|
||||
stage_json: &Vec<u8>,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut file_path = self.output_directory.to_owned();
|
||||
let full_file_name = Self::full_file_name(contract_path, contract_suffix, IRType::JSON);
|
||||
file_path.push(full_file_name);
|
||||
std::fs::write(file_path, stage_json)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Creates a full file name, given the contract full path, suffix, and extension.
|
||||
fn full_file_name(contract_path: &str, suffix: Option<&str>, ir_type: IRType) -> String {
|
||||
let mut full_file_name = contract_path.replace('/', "_").replace(':', ".");
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
[package]
|
||||
name = "pallet-contracts-pvm-llapi"
|
||||
version.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
repository.authors = true
|
||||
descritption = "Implements the low level runtime API bindings with pallet contracts"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
riscv-64 = []
|
||||
|
||||
+20
-10
@@ -1,14 +1,13 @@
|
||||
[package]
|
||||
name = "revive-solidity"
|
||||
version.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
version = "1.4.0"
|
||||
authors = [
|
||||
"Oleksandr Zarudnyi <a.zarudnyy@matterlabs.dev>",
|
||||
"Cyrill Leutwiler <cyrill@parity.io>",
|
||||
]
|
||||
description = "Solidity frontend for the revive compiler"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
description = "PolkaVM Solidity frontend"
|
||||
|
||||
[[bin]]
|
||||
name = "resolc"
|
||||
@@ -18,13 +17,13 @@ path = "src/resolc/main.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true }
|
||||
structopt = { workspace = true }
|
||||
colored = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
which = { workspace = true }
|
||||
path-slash = { workspace = true }
|
||||
rayon = { workspace = true }
|
||||
rayon = { workspace = true, optional = true }
|
||||
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
@@ -38,9 +37,20 @@ sha3 = { workspace = true }
|
||||
md5 = { workspace = true }
|
||||
inkwell = { workspace = true }
|
||||
|
||||
revive-common = { workspace = true }
|
||||
revive-llvm-context = { workspace = true }
|
||||
|
||||
revive-common = { path = "../common" }
|
||||
revive-llvm-context = { path = "../llvm-context" }
|
||||
|
||||
[target.'cfg(target_env = "musl")'.dependencies]
|
||||
mimalloc = { version = "*", default-features = false }
|
||||
|
||||
[target.'cfg(target_os = "emscripten")'.dependencies]
|
||||
libc = { workspace = true }
|
||||
inkwell = { workspace = true, features = ["target-riscv", "llvm18-0-no-llvm-linking"]}
|
||||
|
||||
[features]
|
||||
default = ["parallel"]
|
||||
parallel = ["rayon"]
|
||||
|
||||
#TODO: Use pthreads -C target-feature=+atomics,+bulk-memory -Clink-arg=-pthread in compilation and enable `parallel` feature
|
||||
[target.'cfg(target_os = "emscripten")'.features]
|
||||
default = []
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
[package]
|
||||
name = "revive-stdlib"
|
||||
version.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
repository.workspace = true
|
||||
description = "revive compiler stdlib components"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
inkwell = { workspace = true, features = ["target-riscv", "no-libffi-linking", "llvm18-0"] }
|
||||
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
INSTALL_DIR="${PWD}/llvm18.0-emscripten"
|
||||
mkdir -p ${INSTALL_DIR}
|
||||
|
||||
./clone-llvm.sh
|
||||
|
||||
# Build LLVM, clang
|
||||
cd llvm-project
|
||||
|
||||
# Check if EMSDK_ROOT is defined
|
||||
if [ -z "$EMSDK_ROOT" ]; then
|
||||
echo "Error: EMSDK_ROOT is not defined."
|
||||
echo "Please set the EMSDK_ROOT environment variable to the root directory of your Emscripten SDK."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source ${EMSDK_ROOT}/emsdk_env.sh
|
||||
|
||||
LLVM_SRC=$(pwd)
|
||||
LLVM_SRC=$(realpath "$LLVM_SRC")
|
||||
LLVM_NATIVE=$LLVM_SRC/build-native
|
||||
LLVM_WASM=$LLVM_SRC/build-wasm
|
||||
|
||||
# Cross compiling llvm needs a native build of "llvm-tblgen" and "clang-tblgen"
|
||||
if [ ! -d $LLVM_NATIVE/ ]; then
|
||||
cmake -G Ninja \
|
||||
-S $LLVM_SRC/llvm/ \
|
||||
-B $LLVM_NATIVE/ \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_TARGETS_TO_BUILD=WebAssembly \
|
||||
-DLLVM_ENABLE_PROJECTS="clang"
|
||||
fi
|
||||
cmake --build $LLVM_NATIVE/ -- llvm-tblgen clang-tblgen llvm-config
|
||||
|
||||
if [ ! -d $LLVM_WASM/ ]; then
|
||||
EMCC_DEBUG=2 \
|
||||
CXXFLAGS="-Dwait4=__syscall_wait4" \
|
||||
LDFLAGS="-lnodefs.js -s NO_INVOKE_RUN -s EXIT_RUNTIME -s INITIAL_MEMORY=64MB -s ALLOW_MEMORY_GROWTH -s \
|
||||
EXPORTED_RUNTIME_METHODS=FS,callMain,NODEFS -s MODULARIZE -s EXPORT_ES6 -s WASM_BIGINT" \
|
||||
emcmake cmake -G Ninja \
|
||||
-S $LLVM_SRC/llvm/ \
|
||||
-B $LLVM_WASM/ \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DLLVM_TARGETS_TO_BUILD='RISCV' \
|
||||
-DLLVM_ENABLE_PROJECTS="clang;lld" \
|
||||
-DLLVM_ENABLE_DUMP=OFF \
|
||||
-DLLVM_ENABLE_ASSERTIONS=OFF \
|
||||
-DLLVM_ENABLE_EXPENSIVE_CHECKS=OFF \
|
||||
-DLLVM_ENABLE_BACKTRACES=OFF \
|
||||
-DLLVM_BUILD_TOOLS=OFF \
|
||||
-DLLVM_ENABLE_THREADS=OFF \
|
||||
-DLLVM_BUILD_LLVM_DYLIB=OFF \
|
||||
-DLLVM_INCLUDE_TESTS=OFF \
|
||||
-DLLVM_ENABLE_TERMINFO=Off \
|
||||
-DLLVM_ENABLE_LIBXML2=Off \
|
||||
-DLLVM_ENABLE_ZLIB=Off \
|
||||
-DLLVM_ENABLE_ZSTD=Off \
|
||||
-DLLVM_TABLEGEN=$LLVM_NATIVE/bin/llvm-tblgen \
|
||||
-DCLANG_TABLEGEN=$LLVM_NATIVE/bin/clang-tblgen \
|
||||
-DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}/
|
||||
fi
|
||||
|
||||
cmake --build $LLVM_WASM/
|
||||
cmake --install $LLVM_WASM/
|
||||
|
||||
cp $LLVM_NATIVE/bin/llvm-config $INSTALL_DIR/bin
|
||||
|
||||
echo ""
|
||||
echo "success"
|
||||
@@ -0,0 +1,49 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import { fileURLToPath } from 'url';
|
||||
import path from 'path';
|
||||
import vm from 'vm';
|
||||
import { createRequire } from 'module';
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
// Import the Emscripten module
|
||||
import ModuleFactory from './resolc.mjs';
|
||||
|
||||
async function initializeSolc() {
|
||||
// Load soljson.js
|
||||
const soljsonPath = path.join('./', 'soljson.js');
|
||||
const soljsonCode = readFileSync(soljsonPath, 'utf8');
|
||||
|
||||
// Create a new VM context and run soljson.js in it
|
||||
const soljsonContext = { Module: {} };
|
||||
vm.createContext(soljsonContext); // Create a new context
|
||||
vm.runInContext(soljsonCode, soljsonContext); // Execute soljson.js in the new context
|
||||
|
||||
// Return the initialized soljson module
|
||||
return soljsonContext.Module;
|
||||
}
|
||||
|
||||
async function runCompiler() {
|
||||
const soljson = await initializeSolc();
|
||||
const Module = await ModuleFactory();
|
||||
|
||||
// Expose soljson in the Module context
|
||||
Module.soljson = soljson;
|
||||
|
||||
// Create input Solidity source code
|
||||
const input = `
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8;
|
||||
contract Baseline {
|
||||
function baseline() public payable {}
|
||||
}`;
|
||||
|
||||
// Write the input Solidity code to the Emscripten file system
|
||||
Module.FS.writeFile('./input.sol', input);
|
||||
|
||||
// Compile the Solidity source code
|
||||
Module.callMain(['./input.sol', '-O3','--bin']);
|
||||
}
|
||||
|
||||
runCompiler().catch(err => {
|
||||
console.error('Error:', err);
|
||||
});
|
||||
+111
File diff suppressed because one or more lines are too long
@@ -0,0 +1,58 @@
|
||||
mergeInto(LibraryManager.library, {
|
||||
soljson_compile: function(inputPtr, inputLen) {
|
||||
var inputJson = UTF8ToString(inputPtr, inputLen);
|
||||
var output = Module.soljson.cwrap('solidity_compile', 'string', ['string'])(inputJson);
|
||||
return stringToNewUTF8(output)
|
||||
},
|
||||
soljson_version: function() {
|
||||
var version = Module.soljson.cwrap("solidity_version", "string", [])();
|
||||
return stringToNewUTF8(version)
|
||||
},
|
||||
resolc_compile: function(inputPtr, inputLen) {
|
||||
const { Worker } = require('worker_threads');
|
||||
const deasync = require('deasync');
|
||||
|
||||
var inputJson = UTF8ToString(inputPtr, inputLen);
|
||||
function compileWithWorker(inputJson, callback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Create a new Worker
|
||||
const worker = new Worker('./worker.js');
|
||||
|
||||
// Listen for messages from the worker
|
||||
worker.on('message', (message) => {
|
||||
resolve(message.output); // Resolve the promise with the output
|
||||
callback(null, message.output);
|
||||
worker.terminate(); // Terminate the worker after processing
|
||||
});
|
||||
|
||||
// Listen for errors from the worker
|
||||
worker.on('error', (error) => {
|
||||
reject(error);
|
||||
callback(error);
|
||||
worker.terminate();
|
||||
});
|
||||
|
||||
// Send the input JSON to the worker
|
||||
worker.postMessage(inputJson);
|
||||
});
|
||||
}
|
||||
let result = null;
|
||||
let error = null;
|
||||
|
||||
// Use deasync to block until promise resolves
|
||||
compileWithWorker(inputJson, function (err, res) {
|
||||
error = err;
|
||||
result = res;
|
||||
});
|
||||
// TODO: deasync is not present in browsers, another solution needs to be implemented
|
||||
deasync.loopWhile(() => result === null && error === null);
|
||||
|
||||
if (error) {
|
||||
const errorJson = JSON.stringify({ type: 'error', message: error.message || "Unknown error" });
|
||||
return stringToNewUTF8(errorJson)
|
||||
}
|
||||
|
||||
const resultJson = JSON.stringify({ type: 'success', data: result });
|
||||
return stringToNewUTF8(resultJson);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
// worker.js
|
||||
// nodejs version
|
||||
const { parentPort } = require('worker_threads');
|
||||
|
||||
parentPort.on('message', async (inputJson) => {
|
||||
const { default: ModuleFactory } = await import('./resolc.mjs');
|
||||
const newModule = await ModuleFactory();
|
||||
|
||||
// Create a virtual file for stdin
|
||||
newModule.FS.writeFile('/in', inputJson);
|
||||
|
||||
// Call main on the new instance
|
||||
const output = newModule.callMain(['--recursive-process']);
|
||||
|
||||
// Check the /err file content
|
||||
const errorMessage = newModule.FS.readFile('/err', { encoding: 'utf8' });
|
||||
|
||||
if (errorMessage.length > 0) {
|
||||
// If /err is not empty, throw an error with its content
|
||||
throw new Error(errorMessage);
|
||||
} else {
|
||||
// If no error, read the output file
|
||||
let outputFile = newModule.FS.readFile('/out', { encoding: 'utf8' });
|
||||
parentPort.postMessage({ output: outputFile });
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user