Separate compilation and linker phases (#376)

Separate between compilation and linker phases to allow deploy time
linking and back-porting era compiler changes to fix #91. Unlinked
contract binaries (caused by missing libraries or missing factory
dependencies in turn) are emitted as raw ELF object.

Few drive by fixes:
- #98
- A compiler panic on missing libraries definitions.
- Fixes some incosistent type forwarding in JSON output (empty string
vs. null object).
- Remove the unused fallback for size optimization setting.
- Remove the broken `--lvm-ir`  mode.
- CI workflow fixes.

---------

Signed-off-by: Cyrill Leutwiler <bigcyrill@hotmail.com>
Signed-off-by: xermicus <bigcyrill@hotmail.com>
Signed-off-by: xermicus <cyrill@parity.io>
This commit is contained in:
xermicus
2025-09-27 20:52:22 +02:00
committed by GitHub
parent 13faedf08a
commit 94ec34c4d5
169 changed files with 6288 additions and 5206 deletions
+1 -6
View File
@@ -1,7 +1,5 @@
//! The tests for running resolc with asm option.
#![cfg(test)]
use crate::tests::cli::utils;
const ASM_OPTION: &str = "--asm";
@@ -30,10 +28,7 @@ fn fails_without_input_file() {
utils::assert_command_failure(&resolc_result, "Omitting an input file");
let output = resolc_result.stderr.to_lowercase();
assert!(
output.contains("no input sources specified") || output.contains("compilation aborted"),
"Expected the output to contain a specific error message."
);
assert!(output.contains("no input sources specified"));
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
+16 -32
View File
@@ -1,8 +1,6 @@
//! The tests for running resolc with combined JSON option.
#![cfg(test)]
use revive_common;
use revive_solc_json_interface::CombinedJsonInvalidSelectorMessage;
use crate::tests::cli::utils;
@@ -53,10 +51,9 @@ fn fails_with_invalid_json_argument() {
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_failure(&resolc_result, "Providing an invalid json argument");
assert!(
resolc_result.stdout.contains("Invalid option"),
"Expected the output to contain a specific error message."
);
assert!(resolc_result
.stderr
.contains(CombinedJsonInvalidSelectorMessage));
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
@@ -73,16 +70,12 @@ fn fails_with_multiple_json_arguments() {
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_failure(&resolc_result, "Providing multiple json arguments");
assert!(
resolc_result
.stderr
.contains("reading error: No such file or directory"),
"Expected the output to contain a specific error message."
);
assert!(resolc_result
.stderr
.contains(&format!("Error: \"{}\" is not found.", JSON_ARGUMENTS[1])),);
// FIX: Resolc exit code == 101
// let solc_result = utils::execute_solc(arguments);
// utils::assert_equal_exit_codes(&solc_result, &resolc_result);
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
}
#[test]
@@ -91,12 +84,9 @@ fn fails_without_json_argument() {
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_failure(&resolc_result, "Omitting a JSON argument");
assert!(
resolc_result.stderr.contains(
"a value is required for '--combined-json <COMBINED_JSON>' but none was supplied"
),
"Expected the output to contain a specific error message."
);
assert!(resolc_result.stderr.contains(
"a value is required for '--combined-json <COMBINED_JSON>' but none was supplied"
));
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
@@ -108,10 +98,7 @@ fn fails_without_solidity_input_file() {
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_failure(&resolc_result, "Omitting a Solidity input file");
assert!(
resolc_result.stderr.contains("No input sources specified"),
"Expected the output to contain a specific error message."
);
assert!(resolc_result.stderr.contains("Error: No input files given"),);
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
@@ -124,12 +111,9 @@ fn fails_with_yul_input_file() {
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_failure(&resolc_result, "Providing a Yul input file");
assert!(
resolc_result
.stderr
.contains("ParserError: Expected identifier"),
"Expected the output to contain a specific error message."
);
assert!(resolc_result
.stderr
.contains("Error: Expected identifier but got 'StringLiteral'"));
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
+64
View File
@@ -0,0 +1,64 @@
use crate::tests::cli::utils::{assert_command_success, execute_resolc, DEPENDENCY_CONTRACT_PATH};
/// Test deploy time linking a contract with unresolved factory dependencies.
#[test]
fn deploy_time_linking_works() {
let temp_dir = tempfile::TempDir::new().unwrap();
let output_directory = temp_dir.path().to_path_buf();
let source_path = temp_dir.path().to_path_buf().join("dependency.sol");
std::fs::copy(DEPENDENCY_CONTRACT_PATH, &source_path).unwrap();
assert_command_success(
&execute_resolc(&[
source_path.to_str().unwrap(),
"--bin",
"-o",
&output_directory.to_string_lossy(),
]),
"Missing libraries should compile fine",
);
let dependency_blob_path = temp_dir
.path()
.to_path_buf()
.join("dependency.sol:Dependency.pvm");
let blob_path = temp_dir
.path()
.to_path_buf()
.join("dependency.sol:TestAssert.pvm");
let output = execute_resolc(&[
"--link",
blob_path.to_str().unwrap(),
dependency_blob_path.to_str().unwrap(),
]);
assert_command_success(&output, "The linker mode with missing library should work");
assert!(output.stdout.contains("still unresolved"));
let assert_library_path = format!(
"{}:Assert=0x0000000000000000000000000000000000000001",
source_path.to_str().unwrap()
);
let assert_ne_library_path = format!(
"{}:AssertNe=0x0000000000000000000000000000000000000002",
source_path.to_str().unwrap()
);
let output = execute_resolc(&[
"--link",
"--libraries",
&assert_library_path,
"--libraries",
&assert_ne_library_path,
blob_path.to_str().unwrap(),
dependency_blob_path.to_str().unwrap(),
]);
assert_command_success(&output, "The linker mode with all library should work");
assert!(!output.stdout.contains("still unresolved"));
}
#[test]
fn emits_unlinked_binary_warning() {
let output = execute_resolc(&[DEPENDENCY_CONTRACT_PATH, "--bin"]);
assert_command_success(&output, "Missing libraries should compile fine");
assert!(output.stderr.contains("is unlinked"));
}
@@ -0,0 +1,15 @@
use crate::tests::cli::utils::{
assert_command_success, execute_resolc, RESOLC_YUL_FLAG, YUL_CONTRACT_PATH,
};
#[test]
fn llvm_arguments_work_with_yul_input() {
let output_with_argument = execute_resolc(&[
RESOLC_YUL_FLAG,
YUL_CONTRACT_PATH,
"--llvm-arg=-riscv-soften-spills'",
"--bin",
]);
assert_command_success(&output_with_argument, "Providing LLVM arguments");
assert!(output_with_argument.success);
}
+3 -3
View File
@@ -1,9 +1,9 @@
//! The CLI tests.
#![cfg(test)]
//! The `resolc` CLI tests.
mod asm;
mod combined_json;
mod linker;
mod llvm_arguments;
mod optimization;
mod output_dir;
mod standard_json;
+26 -21
View File
@@ -1,10 +1,9 @@
//! The tests for running resolc with explicit optimization.
#![cfg(test)]
use revive_common;
use crate::tests::cli::{utils, yul};
use crate::tests::cli::utils::{
self, assert_command_failure, assert_command_success, assert_equal_exit_codes, execute_resolc,
execute_solc, RESOLC_YUL_FLAG, SOLIDITY_CONTRACT_PATH, YUL_MEMSET_CONTRACT_PATH,
};
const LEVELS: &[char] = &['0', '1', '2', '3', 's', 'z'];
@@ -12,11 +11,7 @@ const LEVELS: &[char] = &['0', '1', '2', '3', 's', 'z'];
fn runs_with_valid_level() {
for level in LEVELS {
let optimization_argument = format!("-O{level}");
let arguments = &[
utils::YUL_MEMSET_CONTRACT_PATH,
yul::YUL_OPTION,
&optimization_argument,
];
let arguments = &[YUL_MEMSET_CONTRACT_PATH, "--yul", &optimization_argument];
let resolc_result = utils::execute_resolc(arguments);
assert!(
resolc_result.success,
@@ -37,17 +32,27 @@ fn runs_with_valid_level() {
#[test]
fn fails_with_invalid_level() {
let arguments = &[utils::YUL_MEMSET_CONTRACT_PATH, yul::YUL_OPTION, "-O9"];
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_failure(&resolc_result, "Providing an invalid optimization level");
let arguments = &[YUL_MEMSET_CONTRACT_PATH, RESOLC_YUL_FLAG, "-O9"];
let resolc_result = execute_resolc(arguments);
assert_command_failure(&resolc_result, "Providing an invalid optimization level");
assert!(
resolc_result
.stderr
.contains("Unexpected optimization option"),
"Expected the output to contain a specific error message."
);
assert!(resolc_result
.stderr
.contains("Unexpected optimization option"));
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
let solc_result = execute_solc(arguments);
assert_equal_exit_codes(&solc_result, &resolc_result);
}
#[test]
fn disable_solc_optimzer() {
let arguments = &[SOLIDITY_CONTRACT_PATH, "--bin", "--disable-solc-optimizer"];
let disabled = execute_resolc(arguments);
assert_command_success(&disabled, "Disabling the solc optimizer");
let arguments = &[SOLIDITY_CONTRACT_PATH, "--bin"];
let enabled = execute_resolc(arguments);
assert_command_success(&disabled, "Enabling the solc optimizer");
assert_ne!(enabled.stdout, disabled.stdout);
}
+35 -42
View File
@@ -1,52 +1,41 @@
//! The tests for running resolc with output directory option.
#![cfg(test)]
use std::path::Path;
use tempfile::tempdir;
use crate::tests::cli::utils;
const OUTPUT_DIRECTORY: &str = "src/tests/cli/artifacts";
const OUTPUT_BIN_FILE_PATH: &str = "src/tests/cli/artifacts/contract.sol:C.pvm";
const OUTPUT_ASM_FILE_PATH: &str = "src/tests/cli/artifacts/contract.sol:C.pvmasm";
const OUTPUT_LLVM_OPTIMIZED_FILE_PATH: &str =
"src/tests/cli/artifacts/src_tests_cli_contracts_solidity_contract.sol.C.optimized.ll";
const OUTPUT_BIN_FILE_PATH: &str = "contract.sol:C.pvm";
const OUTPUT_ASM_FILE_PATH: &str = "contract.sol:C.pvmasm";
const OUTPUT_LLVM_OPTIMIZED_FILE_PATH: &str = "src_tests_data_solidity_contract.sol.C.optimized.ll";
const OUTPUT_LLVM_UNOPTIMIZED_FILE_PATH: &str =
"src/tests/cli/artifacts/src_tests_cli_contracts_solidity_contract.sol.C.unoptimized.ll";
fn file_exists(path: &str) -> bool {
Path::new(path).try_exists().unwrap()
}
fn file_is_empty(path: &str) -> bool {
Path::new(path).metadata().unwrap().len() == 0
}
"src_tests_data_solidity_contract.sol.C.unoptimized.ll";
fn assert_valid_output_file(
result: &utils::CommandResult,
output_file_type: &str,
output_file_path: &str,
debug_output_directory: &Path,
output_file_name: &str,
) {
utils::assert_command_success(result, "Providing an output directory");
assert!(
result.stderr.contains("Compiler run successful"),
"Expected the compiler output to contain a success message.",
);
assert!(result.stderr.contains("Compiler run successful"),);
assert!(
file_exists(output_file_path),
"Expected the {output_file_type} output file `{output_file_path}` to exist."
);
let file = debug_output_directory.to_path_buf().join(output_file_name);
assert!(
!file_is_empty(output_file_path),
"Expected the {output_file_type} output file `{output_file_path}` to not be empty."
assert!(file.exists(), "Artifact should exist: {}", file.display());
assert_ne!(
file.metadata().unwrap().len(),
0,
"Artifact shouldn't be empty: {}",
file.display()
);
}
#[test]
fn writes_to_file() {
let temp_dir = tempdir().unwrap();
let arguments = &[
utils::SOLIDITY_CONTRACT_PATH,
"--overwrite",
@@ -54,15 +43,16 @@ fn writes_to_file() {
"--bin",
"--asm",
"--output-dir",
OUTPUT_DIRECTORY,
temp_dir.path().to_str().unwrap(),
];
let result = utils::execute_resolc(arguments);
assert_valid_output_file(&result, "--bin", OUTPUT_BIN_FILE_PATH);
assert_valid_output_file(&result, "--asm", OUTPUT_ASM_FILE_PATH);
assert_valid_output_file(&result, temp_dir.path(), OUTPUT_BIN_FILE_PATH);
assert_valid_output_file(&result, temp_dir.path(), OUTPUT_ASM_FILE_PATH);
}
#[test]
fn writes_debug_info_to_file_unoptimized() {
let temp_dir = tempdir().unwrap();
let arguments = &[
utils::SOLIDITY_CONTRACT_PATH,
"-g",
@@ -71,15 +61,16 @@ fn writes_debug_info_to_file_unoptimized() {
"--bin",
"--asm",
"--output-dir",
OUTPUT_DIRECTORY,
temp_dir.path().to_str().unwrap(),
];
let result = utils::execute_resolc(arguments);
assert_valid_output_file(&result, "--bin", OUTPUT_BIN_FILE_PATH);
assert_valid_output_file(&result, "--asm", OUTPUT_ASM_FILE_PATH);
assert_valid_output_file(&result, temp_dir.path(), OUTPUT_BIN_FILE_PATH);
assert_valid_output_file(&result, temp_dir.path(), OUTPUT_ASM_FILE_PATH);
}
#[test]
fn writes_debug_info_to_file_optimized() {
let temp_dir = tempdir().unwrap();
let arguments = &[
utils::SOLIDITY_CONTRACT_PATH,
"-g",
@@ -87,36 +78,38 @@ fn writes_debug_info_to_file_optimized() {
"--bin",
"--asm",
"--output-dir",
OUTPUT_DIRECTORY,
temp_dir.path().to_str().unwrap(),
];
let result = utils::execute_resolc(arguments);
assert_valid_output_file(&result, "--bin", OUTPUT_BIN_FILE_PATH);
assert_valid_output_file(&result, "--asm", OUTPUT_ASM_FILE_PATH);
assert_valid_output_file(&result, temp_dir.path(), OUTPUT_BIN_FILE_PATH);
assert_valid_output_file(&result, temp_dir.path(), OUTPUT_ASM_FILE_PATH);
}
#[test]
fn writes_llvm_debug_info_to_file_unoptimized() {
let temp_dir = tempdir().unwrap();
let arguments = &[
utils::SOLIDITY_CONTRACT_PATH,
"-g",
"--disable-solc-optimizer",
"--overwrite",
"--debug-output-dir",
OUTPUT_DIRECTORY,
temp_dir.path().to_str().unwrap(),
];
let result = utils::execute_resolc(arguments);
assert_valid_output_file(&result, "llvm", OUTPUT_LLVM_UNOPTIMIZED_FILE_PATH);
assert_valid_output_file(&result, temp_dir.path(), OUTPUT_LLVM_UNOPTIMIZED_FILE_PATH);
}
#[test]
fn writes_llvm_debug_info_to_file_optimized() {
let temp_dir = tempdir().unwrap();
let arguments = &[
utils::SOLIDITY_CONTRACT_PATH,
"-g",
"--overwrite",
"--debug-output-dir",
OUTPUT_DIRECTORY,
temp_dir.path().to_str().unwrap(),
];
let result = utils::execute_resolc(arguments);
assert_valid_output_file(&result, "llvm", OUTPUT_LLVM_OPTIMIZED_FILE_PATH);
assert_valid_output_file(&result, temp_dir.path(), OUTPUT_LLVM_OPTIMIZED_FILE_PATH);
}
+8 -9
View File
@@ -1,24 +1,23 @@
//! The tests for running resolc with standard JSON option.
#![cfg(test)]
use crate::tests::cli::utils;
use crate::tests::cli::utils::{
assert_command_success, assert_equal_exit_codes, execute_resolc_with_stdin_input,
execute_solc_with_stdin_input, STANDARD_JSON_CONTRACTS_PATH,
};
const JSON_OPTION: &str = "--standard-json";
#[test]
fn runs_with_valid_input_file() {
let arguments = &[JSON_OPTION];
let resolc_result =
utils::execute_resolc_with_stdin_input(arguments, utils::STANDARD_JSON_CONTRACTS_PATH);
utils::assert_command_success(&resolc_result, "Providing a valid input file to stdin");
let resolc_result = execute_resolc_with_stdin_input(arguments, STANDARD_JSON_CONTRACTS_PATH);
assert_command_success(&resolc_result, "Providing a valid input file to stdin");
assert!(
resolc_result.stdout.contains("contracts"),
"Expected the output to contain a `contracts` field."
);
let solc_result =
utils::execute_solc_with_stdin_input(arguments, utils::STANDARD_JSON_CONTRACTS_PATH);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
let solc_result = execute_solc_with_stdin_input(arguments, STANDARD_JSON_CONTRACTS_PATH);
assert_equal_exit_codes(&solc_result, &resolc_result);
}
+2 -11
View File
@@ -1,20 +1,14 @@
//! The tests for running resolc when expecting usage output.
#![cfg(test)]
use crate::tests::cli::utils;
#[test]
#[ignore = "Fix: 'resolc --help' should exit with success exit code"]
fn shows_usage_with_help() {
let arguments = &["--help"];
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_success(&resolc_result, "Providing the `--help` option");
assert!(
resolc_result.stdout.contains("Usage: resolc"),
"Expected the output to contain usage information."
);
assert!(resolc_result.stdout.contains("Usage: resolc"));
let solc_result = utils::execute_solc(arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
@@ -25,10 +19,7 @@ fn fails_without_options() {
let resolc_result = utils::execute_resolc(&[]);
utils::assert_command_failure(&resolc_result, "Omitting options");
assert!(
resolc_result.stderr.contains("Usage: resolc"),
"Expected the output to contain usage information."
);
assert!(resolc_result.stderr.contains("Usage: resolc"));
let solc_result = utils::execute_solc(&[]);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
+28 -10
View File
@@ -5,15 +5,28 @@ use std::{
process::{Command, Stdio},
};
use revive_common;
use crate::SolcCompiler;
pub const SOLIDITY_CONTRACT_PATH: &str = "src/tests/cli/contracts/solidity/contract.sol";
pub const YUL_CONTRACT_PATH: &str = "src/tests/cli/contracts/yul/contract.yul";
pub const YUL_MEMSET_CONTRACT_PATH: &str = "src/tests/cli/contracts/yul/memset.yul";
/// The simple Solidity contract test fixture path.
pub const SOLIDITY_CONTRACT_PATH: &str = "src/tests/data/solidity/contract.sol";
/// The dependency Solidity contract test fixture path.
pub const DEPENDENCY_CONTRACT_PATH: &str = "src/tests/data/solidity/dependency.sol";
/// The simple YUL contract test fixture path.
pub const YUL_CONTRACT_PATH: &str = "src/tests/data/yul/contract.yul";
/// The memeset YUL contract test fixture path.
pub const YUL_MEMSET_CONTRACT_PATH: &str = "src/tests/data/yul/memset.yul";
/// The standard JSON contracts test fixture path.
///
pub const STANDARD_JSON_CONTRACTS_PATH: &str =
"src/tests/cli/contracts/standard_json/solidity_contracts.json";
"src/tests/data/standard_json/solidity_contracts.json";
/// The `resolc` YUL mode flag.
pub const RESOLC_YUL_FLAG: &str = "--yul";
/// The `--yul` option was deprecated in Solidity 0.8.27 in favor of `--strict-assembly`.
/// See section `--strict-assembly vs. --yul` in https://soliditylang.org/blog/2024/09/04/solidity-0.8.27-release-announcement/
pub const SOLC_YUL_FLAG: &str = "--strict-assembly";
/// The result of executing a command.
pub struct CommandResult {
@@ -52,6 +65,14 @@ fn execute_command(
arguments: &[&str],
stdin_file_path: Option<&str>,
) -> CommandResult {
println!(
"executing command: '{command} {}{}'",
arguments.join(" "),
stdin_file_path
.map(|argument| format!("< {argument}"))
.unwrap_or_default()
);
let stdin_config = match stdin_file_path {
Some(path) => Stdio::from(File::open(path).unwrap()),
None => Stdio::null(),
@@ -73,10 +94,7 @@ fn execute_command(
}
pub fn assert_equal_exit_codes(solc_result: &CommandResult, resolc_result: &CommandResult) {
assert_eq!(
solc_result.code, resolc_result.code,
"Expected solc and resolc to have the same exit code."
);
assert_eq!(solc_result.code, resolc_result.code,);
}
pub fn assert_command_success(result: &CommandResult, error_message_prefix: &str) {
+21 -33
View File
@@ -1,44 +1,32 @@
//! The tests for running resolc with yul option.
#![cfg(test)]
use crate::tests::cli::utils;
pub const YUL_OPTION: &str = "--yul";
/// The `--yul` option was deprecated in Solidity 0.8.27 in favor of `--strict-assembly`.
/// See section `--strict-assembly vs. --yul` in https://soliditylang.org/blog/2024/09/04/solidity-0.8.27-release-announcement/
const SOLC_YUL_OPTION: &str = "--strict-assembly";
use crate::tests::cli::utils::{
assert_command_success, assert_equal_exit_codes, execute_resolc, execute_solc, RESOLC_YUL_FLAG,
SOLC_YUL_FLAG, YUL_CONTRACT_PATH,
};
#[test]
fn runs_with_valid_input_file() {
let arguments = &[utils::YUL_CONTRACT_PATH, YUL_OPTION];
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_success(&resolc_result, "Providing a valid input file");
let resolc_result = execute_resolc(&[YUL_CONTRACT_PATH, RESOLC_YUL_FLAG]);
assert_command_success(&resolc_result, "Providing a valid input file");
assert!(
resolc_result
.stderr
.contains("Compiler run successful. No output requested"),
"Expected the output to contain a success message."
);
assert!(resolc_result
.stderr
.contains("Compiler run successful. No output requested"));
let solc_arguments = &[utils::YUL_CONTRACT_PATH, SOLC_YUL_OPTION];
let solc_result = utils::execute_solc(solc_arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
let solc_result = execute_solc(&[YUL_CONTRACT_PATH, SOLC_YUL_FLAG]);
assert_equal_exit_codes(&solc_result, &resolc_result);
}
/// While the `solc` Solidity mode requires output selection,
/// the strict-assembly mode does not.
///
/// `resolc` exhibits consistent behavior for both modes.
#[test]
fn fails_without_input_file() {
let arguments = &[YUL_OPTION];
let resolc_result = utils::execute_resolc(arguments);
utils::assert_command_failure(&resolc_result, "Omitting an input file");
assert!(
resolc_result.stderr.contains("The input file is missing"),
"Expected the output to contain a specific error message."
);
let solc_arguments = &[SOLC_YUL_OPTION];
let solc_result = utils::execute_solc(solc_arguments);
utils::assert_equal_exit_codes(&solc_result, &resolc_result);
fn runs_without_input_file() {
let resolc_result = execute_resolc(&[RESOLC_YUL_FLAG]);
assert_command_success(&resolc_result, "Omitting an input file");
assert!(resolc_result
.stderr
.contains("Compiler run successful. No output requested"));
}
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
library Assert {
function equal(uint256 a, uint256 b) public pure returns (bool result) {
result = (a == b);
}
}
library AssertNe {
function notEqual(uint256 a, uint256 b) public pure returns (bool result) {
result = (a != b);
}
}
contract TestAssert {
constructor() payable {
new Dependency();
}
function checkEquality(uint256 a, uint256 b) public pure returns (string memory) {
Assert.equal(a, b);
return "Values are equal";
}
}
contract Dependency {
function checkNotEquality(uint256 a, uint256 b) public pure returns (string memory) {
AssertNe.notEqual(a, b);
return "Values are not equal";
}
}
@@ -0,0 +1,29 @@
{
"language": "Yul",
"sources": {
"Test": {
"content": "object \"Return\" { code { { return(0, 0) } } object \"Return_deployed\" { code { { return(0, 0) } } } }"
}
},
"settings": {
"optimizer": {
"enabled": true
},
"metadata": {
"useLiteralContent": true
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"evm.methodIdentifiers",
"evm.bytecode"
]
}
},
"libraries": {}
}
}
@@ -0,0 +1,31 @@
{
"language": "Yul",
"sources": {
"Test": {
"urls": [
"src/tests/data/yul/Test.yul"
]
}
},
"settings": {
"optimizer": {
"enabled": true
},
"metadata": {
"useLiteralContent": true
},
"outputSelection": {
"*": {
"": [
"ast"
],
"*": [
"abi",
"evm.methodIdentifiers",
"evm.bytecode"
]
}
},
"libraries": {}
}
}
+16
View File
@@ -0,0 +1,16 @@
object "Return" {
code {
{
return(0, 0)
}
}
object "Return_deployed" {
code {
{
mstore(0, 42)
return(0, 32)
}
}
}
}
-251
View File
@@ -1,251 +0,0 @@
//! The Solidity compiler unit tests for messages.
#![cfg(test)]
use std::collections::BTreeMap;
use revive_solc_json_interface::warning::Warning;
pub const ECRECOVER_TEST_SOURCE: &str = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ECRecoverExample {
function recoverAddress(
bytes32 messageHash,
uint8 v,
bytes32 r,
bytes32 s
) public pure returns (address) {
return ecrecover(messageHash, v, r, s);
}
}
"#;
#[test]
fn ecrecover() {
assert!(
super::check_solidity_warning(
ECRECOVER_TEST_SOURCE,
"Warning: It looks like you are using 'ecrecover' to validate a signature of a user account.",
BTreeMap::new(),
false,
None,
).expect("Test failure")
);
}
#[test]
fn ecrecover_suppressed() {
assert!(
!super::check_solidity_warning(
ECRECOVER_TEST_SOURCE,
"Warning: It looks like you are using 'ecrecover' to validate a signature of a user account.",
BTreeMap::new(),
false,
Some(vec![Warning::EcRecover]),
).expect("Test failure")
);
}
pub const SEND_TEST_SOURCE: &str = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SendExample {
address payable public recipient;
constructor(address payable _recipient) {
recipient = _recipient;
}
function forwardEther() external payable {
bool success = recipient.send(msg.value);
require(success, "Failed to send Ether");
}
}
"#;
pub const BALANCE_CALLS_MESSAGE: &str =
"Warning: It looks like you are using '<address payable>.send/transfer(<X>)'";
#[test]
fn send() {
assert!(super::check_solidity_warning(
SEND_TEST_SOURCE,
BALANCE_CALLS_MESSAGE,
BTreeMap::new(),
false,
None,
)
.expect("Test failure"));
}
#[test]
fn send_suppressed() {
assert!(!super::check_solidity_warning(
SEND_TEST_SOURCE,
BALANCE_CALLS_MESSAGE,
BTreeMap::new(),
false,
Some(vec![Warning::SendTransfer]),
)
.expect("Test failure"));
}
pub const TRANSFER_TEST_SOURCE: &str = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TransferExample {
address payable public recipient;
constructor(address payable _recipient) {
recipient = _recipient;
}
function forwardEther() external payable {
recipient.transfer(msg.value);
}
}
"#;
#[test]
fn transfer() {
assert!(super::check_solidity_warning(
TRANSFER_TEST_SOURCE,
BALANCE_CALLS_MESSAGE,
BTreeMap::new(),
false,
None,
)
.expect("Test failure"));
}
#[test]
fn transfer_suppressed() {
assert!(!super::check_solidity_warning(
TRANSFER_TEST_SOURCE,
BALANCE_CALLS_MESSAGE,
BTreeMap::new(),
false,
Some(vec![Warning::SendTransfer]),
)
.expect("Test failure"));
}
pub const EXTCODESIZE_TEST_SOURCE: &str = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ExternalCodeSize {
function getExternalCodeSize(address target) public view returns (uint256) {
uint256 codeSize;
assembly {
codeSize := extcodesize(target)
}
return codeSize;
}
}
"#;
#[test]
fn extcodesize() {
assert!(super::check_solidity_warning(
EXTCODESIZE_TEST_SOURCE,
"Warning: Your code or one of its dependencies uses the 'extcodesize' instruction,",
BTreeMap::new(),
false,
None,
)
.expect("Test failure"));
}
#[test]
fn extcodesize_suppressed() {
assert!(!super::check_solidity_warning(
EXTCODESIZE_TEST_SOURCE,
"Warning: Your code or one of its dependencies uses the 'extcodesize' instruction,",
BTreeMap::new(),
false,
Some(vec![Warning::ExtCodeSize]),
)
.expect("Test failure"));
}
pub const TX_ORIGIN_TEST_SOURCE: &str = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TxOriginExample {
function isOriginSender() public view returns (bool) {
return tx.origin == msg.sender;
}
}
"#;
#[test]
fn tx_origin() {
assert!(super::check_solidity_warning(
TX_ORIGIN_TEST_SOURCE,
"Warning: You are checking for 'tx.origin' in your code, which might lead to",
BTreeMap::new(),
false,
None,
)
.expect("Test failure"));
}
#[test]
fn tx_origin_suppressed() {
assert!(!super::check_solidity_warning(
TX_ORIGIN_TEST_SOURCE,
"Warning: You are checking for 'tx.origin' in your code, which might lead to",
BTreeMap::new(),
false,
Some(vec![Warning::TxOrigin]),
)
.expect("Test failure"));
}
pub const TX_ORIGIN_ASSEMBLY_TEST_SOURCE: &str = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TxOriginExample {
function isOriginSender() public view returns (bool) {
address txOrigin;
address sender = msg.sender;
assembly {
txOrigin := origin() // Get the transaction origin using the 'origin' instruction
}
return txOrigin == sender;
}
}
"#;
#[test]
fn tx_origin_assembly() {
assert!(super::check_solidity_warning(
TX_ORIGIN_ASSEMBLY_TEST_SOURCE,
"Warning: You are checking for 'tx.origin' in your code, which might lead to",
BTreeMap::new(),
false,
None,
)
.expect("Test failure"));
}
#[test]
fn tx_origin_assembly_suppressed() {
assert!(!super::check_solidity_warning(
TX_ORIGIN_ASSEMBLY_TEST_SOURCE,
"Warning: You are checking for 'tx.origin' in your code, which might lead to",
BTreeMap::new(),
false,
Some(vec![Warning::TxOrigin]),
)
.expect("Test failure"));
}
+2 -11
View File
@@ -1,15 +1,6 @@
//! The Solidity compiler unit tests.
//! The Solidity compiler tests.
#![cfg(test)]
mod cli;
mod factory_dependency;
mod ir_artifacts;
mod libraries;
mod messages;
mod optimizer;
mod remappings;
mod runtime_code;
mod unsupported_opcodes;
pub(crate) use super::test_utils::*;
mod unit;
-50
View File
@@ -1,50 +0,0 @@
//! The Solidity compiler unit tests for remappings.
#![cfg(test)]
use std::collections::BTreeMap;
use std::collections::BTreeSet;
pub const CALLEE_TEST_SOURCE: &str = r#"
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.16;
contract Callable {
function f(uint a) public pure returns(uint) {
return a * 2;
}
}
"#;
pub const CALLER_TEST_SOURCE: &str = r#"
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.16;
import "libraries/default/callable.sol";
contract Main {
function main(Callable callable) public returns(uint) {
return callable.f(5);
}
}
"#;
#[test]
fn default() {
let mut sources = BTreeMap::new();
sources.insert("./test.sol".to_owned(), CALLER_TEST_SOURCE.to_owned());
sources.insert("./callable.sol".to_owned(), CALLEE_TEST_SOURCE.to_owned());
let mut remappings = BTreeSet::new();
remappings.insert("libraries/default/=./".to_owned());
super::build_solidity(
sources,
BTreeMap::new(),
Some(remappings),
revive_llvm_context::OptimizerSettings::cycles(),
)
.expect("Test failure");
}
-33
View File
@@ -1,33 +0,0 @@
//! The Solidity compiler unit tests for runtime code.
#![cfg(test)]
use std::collections::BTreeMap;
#[test]
#[should_panic(expected = "runtimeCode is not supported")]
fn default() {
let source_code = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract A {}
contract Test {
function main() public pure returns(bytes memory) {
return type(A).runtimeCode;
}
}
"#;
let mut sources = BTreeMap::new();
sources.insert("test.sol".to_owned(), source_code.to_owned());
super::build_solidity(
sources,
BTreeMap::new(),
None,
revive_llvm_context::OptimizerSettings::cycles(),
)
.expect("Test failure");
}
@@ -1,10 +1,10 @@
//! The Solidity compiler unit tests for factory dependencies.
#![cfg(test)]
use crate::test_utils::{build_solidity, sources};
use std::collections::BTreeMap;
pub const MAIN_CODE: &str = r#"
#[test]
fn default() {
let caller_code = r#"
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.16;
@@ -18,10 +18,9 @@ contract Main {
callable.set(10);
return callable.get();
}
}
"#;
}"#;
pub const CALLABLE_CODE: &str = r#"
let callee_code = r#"
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.16;
@@ -36,35 +35,22 @@ contract Callable {
function get() external view returns(uint256) {
return value;
}
}
"#;
}"#;
#[test]
fn default() {
let mut sources = BTreeMap::new();
sources.insert("main.sol".to_owned(), MAIN_CODE.to_owned());
sources.insert("callable.sol".to_owned(), CALLABLE_CODE.to_owned());
let output = super::build_solidity(
sources,
BTreeMap::new(),
None,
revive_llvm_context::OptimizerSettings::cycles(),
)
.expect("Build failure");
let output = build_solidity(sources(&[
("main.sol", caller_code),
("callable.sol", callee_code),
]))
.unwrap();
assert_eq!(
output
.contracts
.as_ref()
.expect("Missing field `contracts`")
.get("main.sol")
.expect("Missing file `main.sol`")
.get("Main")
.expect("Missing contract `main.sol:Main`")
.factory_dependencies
.as_ref()
.expect("Missing field `factory_dependencies`")
.len(),
1,
"Expected 1 factory dependency in `main.sol:Main`"
@@ -72,15 +58,11 @@ fn default() {
assert_eq!(
output
.contracts
.as_ref()
.expect("Missing field `contracts`")
.get("callable.sol")
.expect("Missing file `callable.sol`")
.get("Callable")
.expect("Missing contract `callable.sol:Callable`")
.factory_dependencies
.as_ref()
.expect("Missing field `factory_dependencies`")
.len(),
0,
"Expected 0 factory dependencies in `callable.sol:Callable`"
@@ -1,13 +1,11 @@
//! The Solidity compiler unit tests for IR artifacts.
//! The tests check if the IR artifacts are kept in the final output.
#![cfg(test)]
use std::collections::BTreeMap;
use crate::test_utils::{build_solidity, sources};
#[test]
fn yul() {
let source_code = r#"
let code = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
@@ -18,28 +16,17 @@ contract Test {
}
"#;
let mut sources = BTreeMap::new();
sources.insert("test.sol".to_owned(), source_code.to_owned());
let build = super::build_solidity(
sources,
BTreeMap::new(),
None,
revive_llvm_context::OptimizerSettings::cycles(),
)
.expect("Test failure");
let build = build_solidity(sources(&[("test.sol", code)])).expect("Test failure");
assert!(
build
!build
.contracts
.as_ref()
.expect("Always exists")
.get("test.sol")
.expect("Always exists")
.get("Test")
.expect("Always exists")
.ir_optimized
.is_some(),
.is_empty(),
"Yul IR is missing"
);
}
@@ -1,10 +1,10 @@
//! The Solidity compiler unit tests for libraries.
#![cfg(test)]
use revive_solc_json_interface::SolcStandardJsonInputSettingsLibraries;
use std::collections::BTreeMap;
use crate::test_utils::build_solidity_and_detect_missing_libraries;
pub const LIBRARY_TEST_SOURCE: &str = r#"
pub const CODE: &str = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
@@ -28,29 +28,21 @@ contract SimpleContract {
}
return sum;
}
}
"#;
}"#;
#[test]
fn not_specified() {
let mut sources = BTreeMap::new();
sources.insert("test.sol".to_owned(), LIBRARY_TEST_SOURCE.to_owned());
let output =
super::build_solidity_and_detect_missing_libraries(sources.clone(), BTreeMap::new())
.expect("Test failure");
build_solidity_and_detect_missing_libraries(&[("test.sol", CODE)], Default::default())
.unwrap();
assert!(
output
.contracts
.as_ref()
.expect("Always exists")
.get("test.sol")
.expect("Always exists")
.get("SimpleContract")
.expect("Always exists")
.missing_libraries
.as_ref()
.expect("Always exists")
.contains("test.sol:SimpleLibrary"),
"Missing library not detected"
);
@@ -58,32 +50,25 @@ fn not_specified() {
#[test]
fn specified() {
let mut sources = BTreeMap::new();
sources.insert("test.sol".to_owned(), LIBRARY_TEST_SOURCE.to_owned());
let mut libraries = BTreeMap::new();
let mut libraries = SolcStandardJsonInputSettingsLibraries::default();
libraries
.as_inner_mut()
.entry("test.sol".to_string())
.or_insert_with(BTreeMap::new)
.or_default()
.entry("SimpleLibrary".to_string())
.or_insert("0x00000000000000000000000000000000DEADBEEF".to_string());
let output =
super::build_solidity_and_detect_missing_libraries(sources.clone(), libraries.clone())
.expect("Test failure");
build_solidity_and_detect_missing_libraries(&[("test.sol", CODE)], libraries.clone())
.unwrap();
assert!(
output
.contracts
.as_ref()
.expect("Always exists")
.get("test.sol")
.expect("Always exists")
.get("SimpleContract")
.expect("Always exists")
.missing_libraries
.as_ref()
.cloned()
.unwrap_or_default()
.is_empty(),
"The list of missing libraries must be empty"
);
+116
View File
@@ -0,0 +1,116 @@
//! The Solidity compiler unit tests for messages.
use revive_llvm_context::OptimizerSettings;
use revive_solc_json_interface::{ResolcWarning, SolcStandardJsonOutput};
use crate::test_utils::{build_solidity, build_solidity_with_options, sources};
pub const SEND_TEST_SOURCE: &str = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SendExample {
address payable public recipient;
constructor(address payable _recipient) {
recipient = _recipient;
}
function forwardEther() external payable {
bool success = recipient.send(msg.value);
require(success, "Failed to send Ether");
}
}"#;
pub const TRANSFER_TEST_SOURCE: &str = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TransferExample {
address payable public recipient;
constructor(address payable _recipient) {
recipient = _recipient;
}
function forwardEther() external payable {
recipient.transfer(msg.value);
}
}"#;
pub const TX_ORIGIN_TEST_SOURCE: &str = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TxOriginExample {
function isOriginSender() public view returns (bool) {
return tx.origin == msg.sender;
}
}"#;
fn contains_warning(build: SolcStandardJsonOutput, warning: ResolcWarning) -> bool {
build
.errors
.iter()
.any(|error| error.is_warning() && error.message.contains(warning.as_message()))
}
#[test]
fn send() {
let build = build_solidity(sources(&[("test.sol", SEND_TEST_SOURCE)])).unwrap();
assert!(contains_warning(build, ResolcWarning::SendAndTransfer));
}
#[test]
fn send_suppressed() {
let build = build_solidity_with_options(
sources(&[("test.sol", SEND_TEST_SOURCE)]),
Default::default(),
Default::default(),
OptimizerSettings::cycles(),
true,
vec![ResolcWarning::SendAndTransfer],
)
.unwrap();
assert!(!contains_warning(build, ResolcWarning::SendAndTransfer));
}
#[test]
fn transfer() {
let build = build_solidity(sources(&[("test.sol", TRANSFER_TEST_SOURCE)])).unwrap();
assert!(contains_warning(build, ResolcWarning::SendAndTransfer));
}
#[test]
fn transfer_suppressed() {
let build = build_solidity_with_options(
sources(&[("test.sol", TRANSFER_TEST_SOURCE)]),
Default::default(),
Default::default(),
OptimizerSettings::cycles(),
true,
vec![ResolcWarning::SendAndTransfer],
)
.unwrap();
assert!(!contains_warning(build, ResolcWarning::SendAndTransfer))
}
#[test]
fn tx_origin() {
let build = build_solidity(sources(&[("test.sol", TX_ORIGIN_TEST_SOURCE)])).unwrap();
assert!(contains_warning(build, ResolcWarning::TxOrigin));
}
#[test]
fn tx_origin_suppressed() {
let build = build_solidity_with_options(
sources(&[("test.sol", TX_ORIGIN_TEST_SOURCE)]),
Default::default(),
Default::default(),
OptimizerSettings::cycles(),
true,
vec![ResolcWarning::TxOrigin],
)
.unwrap();
assert!(!contains_warning(build, ResolcWarning::TxOrigin))
}
+11
View File
@@ -0,0 +1,11 @@
//! The Solidity compiler unit tests.
mod factory_dependency;
mod ir_artifacts;
mod libraries;
mod messages;
mod optimizer;
mod remappings;
mod runtime_code;
mod standard_json;
mod unsupported_opcodes;
@@ -1,10 +1,12 @@
//! The Solidity compiler unit tests for the optimizer.
#![cfg(test)]
use crate::test_utils::{build_solidity, build_solidity_with_options, sources};
use std::collections::BTreeMap;
pub const SOURCE_CODE: &str = r#"
#[test]
fn optimizer() {
let source = &[(
"test.sol",
r#"
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
@@ -40,40 +42,31 @@ contract Test {
}
return h;
}
}
"#;
}"#,
)];
#[test]
fn optimizer() {
let mut sources = BTreeMap::new();
sources.insert("test.sol".to_owned(), SOURCE_CODE.to_owned());
let build_unoptimized = super::build_solidity(
sources.clone(),
BTreeMap::new(),
None,
let build_unoptimized = build_solidity_with_options(
sources(source),
Default::default(),
Default::default(),
revive_llvm_context::OptimizerSettings::none(),
true,
Default::default(),
)
.expect("Build failure");
let build_optimized_for_cycles = super::build_solidity(
sources.clone(),
BTreeMap::new(),
None,
revive_llvm_context::OptimizerSettings::cycles(),
)
.expect("Build failure");
let build_optimized_for_size = super::build_solidity(
sources,
BTreeMap::new(),
None,
.unwrap();
let build_optimized_for_cycles = build_solidity(sources(source)).unwrap();
let build_optimized_for_size = build_solidity_with_options(
sources(source),
Default::default(),
Default::default(),
revive_llvm_context::OptimizerSettings::size(),
true,
Default::default(),
)
.expect("Build failure");
.unwrap();
let size_when_unoptimized = build_unoptimized
.contracts
.as_ref()
.expect("Missing field `contracts`")
.get("test.sol")
.expect("Missing file `test.sol`")
.get("Test")
@@ -88,8 +81,6 @@ fn optimizer() {
.len();
let size_when_optimized_for_cycles = build_optimized_for_cycles
.contracts
.as_ref()
.expect("Missing field `contracts`")
.get("test.sol")
.expect("Missing file `test.sol`")
.get("Test")
@@ -104,8 +95,6 @@ fn optimizer() {
.len();
let size_when_optimized_for_size = build_optimized_for_size
.contracts
.as_ref()
.expect("Missing field `contracts`")
.get("test.sol")
.expect("Missing file `test.sol`")
.get("Test")
@@ -0,0 +1,40 @@
//! The Solidity compiler unit tests for remappings.
use crate::test_utils::{build_solidity_with_options, sources};
#[test]
fn default() {
let callee_code = r#"
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.16;
contract Callable {
function f(uint a) public pure returns(uint) {
return a * 2;
}
}"#;
let caller_code = r#"
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.16;
import "libraries/default/callable.sol";
contract Main {
function main(Callable callable) public returns(uint) {
return callable.f(5);
}
}"#;
build_solidity_with_options(
sources(&[("./test.sol", caller_code), ("./callable.sol", callee_code)]),
Default::default(),
["libraries/default/=./".to_owned()].into(),
revive_llvm_context::OptimizerSettings::cycles(),
true,
Default::default(),
)
.unwrap();
}
@@ -0,0 +1,30 @@
//! The Solidity compiler unit tests for runtime code.
use crate::test_utils::{build_solidity, sources};
#[test]
fn default() {
let code = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract A {}
contract Test {
function main() public pure returns(bytes memory) {
return type(A).runtimeCode;
}
}
"#;
build_solidity(sources(&[("test.sol", code)]))
.unwrap()
.errors
.iter()
.find(|error| {
error
.to_string()
.contains("Error: Deploy and runtime code are merged in PVM")
})
.unwrap();
}
@@ -0,0 +1,53 @@
use std::path::PathBuf;
use revive_solc_json_interface::SolcStandardJsonInput;
use crate::test_utils::build_yul_standard_json;
#[test]
fn standard_json_yul_solc() {
let solc_input = SolcStandardJsonInput::try_from(Some(
PathBuf::from("src/tests/data/standard_json/yul_solc.json").as_path(),
))
.unwrap();
let solc_output = build_yul_standard_json(solc_input).unwrap();
assert!(!solc_output
.contracts
.get("Test")
.expect("The `Test` contract is missing")
.get("Return")
.expect("The `Return` contract is missing")
.evm
.as_ref()
.expect("The `evm` field is missing")
.bytecode
.as_ref()
.expect("The `bytecode` field is missing")
.object
.is_empty())
}
#[test]
fn standard_json_yul_solc_urls() {
let solc_input = SolcStandardJsonInput::try_from(Some(
PathBuf::from("src/tests/data/standard_json/yul_solc_urls.json").as_path(),
))
.unwrap();
let solc_output = build_yul_standard_json(solc_input).unwrap();
assert!(!solc_output
.contracts
.get("Test")
.expect("The `Test` contract is missing")
.get("Return")
.expect("The `Return` contract is missing")
.evm
.as_ref()
.expect("The `evm` field is missing")
.bytecode
.as_ref()
.expect("The `bytecode` field is missing")
.object
.is_empty())
}
@@ -1,13 +1,11 @@
//! The Solidity compiler unit tests for unsupported opcodes.
#![cfg(test)]
use std::collections::BTreeMap;
use crate::test_utils::{build_solidity, build_yul, sources};
#[test]
#[should_panic(expected = "The `CODECOPY` instruction is not supported")]
fn codecopy_yul_runtime() {
let source_code = r#"
let code = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
@@ -22,22 +20,15 @@ contract FixedCodeCopy {
return code;
}
}
"#;
}"#;
let mut sources = BTreeMap::new();
sources.insert("test.sol".to_owned(), source_code.to_owned());
super::build_solidity(
sources,
BTreeMap::new(),
None,
revive_llvm_context::OptimizerSettings::cycles(),
)
.expect("Test failure");
build_solidity(sources(&[("test.sol", code)])).unwrap();
}
pub const CALLCODE_TEST_SOURCE: &str = r#"
#[test]
#[should_panic(expected = "The `CALLCODE` instruction is not supported")]
fn callcode_yul() {
let solidity = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
@@ -57,28 +48,15 @@ contract CallcodeTest {
return success;
}
}
"#;
}"#;
#[test]
#[should_panic(expected = "The `CALLCODE` instruction is not supported")]
fn callcode_yul() {
let mut sources = BTreeMap::new();
sources.insert("test.sol".to_owned(), CALLCODE_TEST_SOURCE.to_owned());
super::build_solidity(
sources,
BTreeMap::new(),
None,
revive_llvm_context::OptimizerSettings::cycles(),
)
.expect("Test failure");
build_solidity(sources(&[("test.sol", solidity)])).unwrap();
}
#[test]
#[should_panic(expected = "The `PC` instruction is not supported")]
fn pc_yul() {
let source_code = r#"
let code = r#"
object "ProgramCounter" {
code {
datacopy(0, dataoffset("ProgramCounter_deployed"), datasize("ProgramCounter_deployed"))
@@ -94,13 +72,15 @@ object "ProgramCounter" {
sstore(0, pcValue)
}
}
}
"#;
}"#;
super::build_yul(source_code).expect("Test failure");
build_yul(&[("test.sol", code)]).unwrap();
}
pub const EXTCODECOPY_TEST_SOURCE: &str = r#"
#[test]
#[should_panic(expected = "The `EXTCODECOPY` instruction is not supported")]
fn extcodecopy_yul() {
let code = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
@@ -114,25 +94,15 @@ contract ExternalCodeCopy {
return code;
}
}"#;
build_solidity(sources(&[("test.sol", code)])).unwrap();
}
"#;
#[test]
#[should_panic(expected = "The `EXTCODECOPY` instruction is not supported")]
fn extcodecopy_yul() {
let mut sources = BTreeMap::new();
sources.insert("test.sol".to_owned(), EXTCODECOPY_TEST_SOURCE.to_owned());
super::build_solidity(
sources,
BTreeMap::new(),
None,
revive_llvm_context::OptimizerSettings::cycles(),
)
.expect("Test failure");
}
pub const SELFDESTRUCT_TEST_SOURCE: &str = r#"
#[should_panic(expected = "The `SELFDESTRUCT` instruction is not supported")]
fn selfdestruct_yul() {
let solidity = r#"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
@@ -147,20 +117,7 @@ contract MinimalDestructible {
require(msg.sender == owner, "Only the owner can call this function.");
selfdestruct(owner);
}
}
"#;
}"#;
#[test]
#[should_panic(expected = "The `SELFDESTRUCT` instruction is not supported")]
fn selfdestruct_yul() {
let mut sources = BTreeMap::new();
sources.insert("test.sol".to_owned(), SELFDESTRUCT_TEST_SOURCE.to_owned());
super::build_solidity(
sources,
BTreeMap::new(),
None,
revive_llvm_context::OptimizerSettings::cycles(),
)
.expect("Test failure");
build_solidity(sources(&[("test.sol", solidity)])).unwrap();
}