Add support for ewasm evmc vm.

This commit is contained in:
Alexander Arlt 2020-06-05 18:10:48 -05:00
parent 202332405f
commit 23d9b1567e
15 changed files with 512 additions and 234 deletions

View File

@ -17,6 +17,7 @@
// SPDX-License-Identifier: GPL-3.0
#include <stdexcept>
#include <iostream>
#include <test/Common.h>
#include <libsolutil/Assertions.h>
@ -57,9 +58,9 @@ boost::filesystem::path testPath()
return {};
}
std::string EVMOneEnvOrDefaultPath()
std::string envOrDefaultPath(std::string const& env_name, std::string const& lib_name)
{
if (auto path = getenv("ETH_EVMONE"))
if (auto path = getenv(env_name.c_str()))
return path;
auto const searchPath =
@ -76,7 +77,7 @@ std::string EVMOneEnvOrDefaultPath()
};
for (auto const& basePath: searchPath)
{
fs::path p = basePath / evmoneFilename;
fs::path p = basePath / lib_name;
if (fs::exists(p))
return p.string();
}
@ -92,7 +93,8 @@ CommonOptions::CommonOptions(std::string _caption):
options.add_options()
("evm-version", po::value(&evmVersionString), "which evm version to use")
("testpath", po::value<fs::path>(&this->testPath)->default_value(solidity::test::testPath()), "path to test files")
("evmonepath", po::value<fs::path>(&evmonePath)->default_value(EVMOneEnvOrDefaultPath()), "path to evmone library")
("vm", po::value<std::vector<fs::path>>(&vmPaths), "path to evmc library, can be supplied multiple times.")
("ewasm", po::bool_switch(&ewasm), "tries to automatically find an ewasm vm and enable ewasm test-execution.")
("no-smt", po::bool_switch(&disableSMT), "disable SMT checker")
("optimize", po::bool_switch(&optimize), "enables optimization")
("enforce-via-yul", po::bool_switch(&enforceViaYul), "Enforce compiling all tests via yul to see if additional tests can be activated.")
@ -141,6 +143,33 @@ bool CommonOptions::parse(int argc, char const* const* argv)
throw std::runtime_error(errorMessage.str());
}
if (vmPaths.empty())
{
std::string evmone = envOrDefaultPath("ETH_EVMONE", evmoneFilename);
if (!evmone.empty())
vmPaths.emplace_back(evmone);
else
{
std::cout << "Unable to find " << solidity::test::evmoneFilename
<< ". Please provide the path using --vm <path>." << std::endl;
std::cout << "You can download it at" << std::endl;
std::cout << solidity::test::evmoneDownloadLink << std::endl;
}
}
if (ewasm) {
std::string hera = envOrDefaultPath("ETH_HERA", heraFilename);
if (!hera.empty())
vmPaths.emplace_back(hera);
else {
std::cout << "Unable to find " << solidity::test::heraFilename
<< ". Please provide the path using --vm <path>." << std::endl;
std::cout << "You can download it at" << std::endl;
std::cout << solidity::test::heraDownloadLink << std::endl;
std::cout << "Ewasm tests disabled." << std::endl;
}
}
return true;
}

View File

@ -21,6 +21,8 @@
#include <libsolutil/Exceptions.h>
#include <liblangutil/EVMVersion.h>
#include <test/evmc/evmc.h>
#include <boost/filesystem/path.hpp>
#include <boost/noncopyable.hpp>
#include <boost/program_options.hpp>
@ -31,21 +33,27 @@ namespace solidity::test
#ifdef _WIN32
static constexpr auto evmoneFilename = "evmone.dll";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-windows-amd64.zip";
static constexpr auto heraFilename = "hera.dll";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/archive/v0.3.0.tar.gz";
#elif defined(__APPLE__)
static constexpr auto evmoneFilename = "libevmone.dylib";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-darwin-x86_64.tar.gz";
static constexpr auto heraFilename = "libhera.dylib";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.0/hera-0.3.0-darwin-x86_64.tar.gz";
#else
static constexpr auto evmoneFilename = "libevmone.so";
static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.4.1/evmone-0.4.1-linux-x86_64.tar.gz";
static constexpr auto heraFilename = "libhera.so";
static constexpr auto heraDownloadLink = "https://github.com/ewasm/hera/releases/download/v0.3.0/hera-0.3.0-linux-x86_64.tar.gz";
#endif
struct ConfigException : public util::Exception {};
struct CommonOptions: boost::noncopyable
{
boost::filesystem::path evmonePath;
std::vector<boost::filesystem::path> vmPaths;
boost::filesystem::path testPath;
bool ewasm = false;
bool optimize = false;
bool enforceViaYul = false;
bool disableSMT = false;
@ -64,8 +72,8 @@ struct CommonOptions: boost::noncopyable
CommonOptions(std::string caption = "");
virtual ~CommonOptions() {};
protected:
protected:
boost::program_options::options_description options;
private:

View File

@ -39,17 +39,18 @@ using namespace evmc::literals;
evmc::VM& EVMHost::getVM(string const& _path)
{
static evmc::VM theVM;
if (!theVM && !_path.empty())
static evmc::VM NullVM{nullptr};
static map<string, unique_ptr<evmc::VM>> vms;
if (vms.count(_path) == 0)
{
evmc_loader_error_code errorCode = {};
auto vm = evmc::VM{evmc_load_and_configure(_path.c_str(), &errorCode)};
if (vm && errorCode == EVMC_LOADER_SUCCESS)
{
if (vm.get_capabilities() & EVMC_CAPABILITY_EVM1)
theVM = std::move(vm);
if (vm.get_capabilities() & (EVMC_CAPABILITY_EVM1 | EVMC_CAPABILITY_EWASM))
vms[_path] = make_unique<evmc::VM>(evmc::VM(move(vm)));
else
cerr << "VM loaded does not support EVM1" << endl;
cerr << "VM loaded neither supports EVM1 nor EWASM" << endl;
}
else
{
@ -59,7 +60,38 @@ evmc::VM& EVMHost::getVM(string const& _path)
cerr << endl;
}
}
return theVM;
if (vms.count(_path) > 0)
return *vms[_path];
return NullVM;
}
bool EVMHost::checkVmPaths(vector<boost::filesystem::path> const& _vmPaths)
{
bool evmVmFound = false;
bool ewasmVmFound = false;
for (auto const& path: _vmPaths)
{
evmc::VM& vm = EVMHost::getVM(path.string());
if (!vm)
return false;
if (vm.has_capability(EVMC_CAPABILITY_EVM1))
{
if (evmVmFound)
throw runtime_error("Multiple evm1 evmc vms defined. Please only define one evm1 evmc vm.");
evmVmFound = true;
}
if (vm.has_capability(EVMC_CAPABILITY_EWASM))
{
if (ewasmVmFound)
throw runtime_error("Multiple ewasm evmc vms where defined. Please only define one ewasm evmc vm.");
ewasmVmFound = true;
}
}
return evmVmFound;
}
EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm):

View File

@ -30,6 +30,8 @@
#include <libsolutil/FixedHash.h>
#include <boost/filesystem.hpp>
namespace solidity::test
{
using Address = util::h160;
@ -40,12 +42,17 @@ public:
using MockedHost::get_code_size;
using MockedHost::get_balance;
/// Tries to dynamically load libevmone. @returns nullptr on failure.
/// The path has to be provided for the first successful run and will be ignored
/// afterwards.
/// Tries to dynamically load an evmc vm supporting evm1 or ewasm and caches the loaded VM.
/// @returns vmc::VM(nullptr) on failure.
static evmc::VM& getVM(std::string const& _path = {});
explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm = getVM());
/// Tries to load all defined evmc vm shared libraries.
/// @param _vmPaths paths to multiple evmc shared libraries.
/// @throw Exception if multiple evm1 or multiple ewasm evmc vms where loaded.
/// @returns true, if an evmc vm was supporting evm1 loaded properly.
static bool checkVmPaths(std::vector<boost::filesystem::path> const& _vmPaths);
explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm);
void reset() { accounts.clear(); m_currentAddress = {}; }
void newBlock()
@ -71,6 +78,12 @@ public:
static util::h256 convertFromEVMC(evmc::bytes32 const& _data);
static evmc::bytes32 convertToEVMC(util::h256 const& _data);
/// @returns true, if the evmc VM has the given capability.
bool hasCapability(evmc_capabilities capability) const noexcept
{
return m_vm.has_capability(capability);
}
private:
evmc::address m_currentAddress = {};

View File

@ -29,6 +29,8 @@
#include <libsolutil/CommonIO.h>
#include <liblangutil/Exceptions.h>
#include <boost/test/framework.hpp>
#include <boost/algorithm/string/replace.hpp>
@ -40,27 +42,47 @@ using namespace solidity::util;
using namespace solidity::test;
ExecutionFramework::ExecutionFramework():
ExecutionFramework(solidity::test::CommonOptions::get().evmVersion())
ExecutionFramework(solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().vmPaths)
{
}
ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion):
ExecutionFramework::ExecutionFramework(langutil::EVMVersion _evmVersion, vector<boost::filesystem::path> const& _vmPaths):
m_evmVersion(_evmVersion),
m_optimiserSettings(solidity::frontend::OptimiserSettings::minimal()),
m_showMessages(solidity::test::CommonOptions::get().showMessages),
m_evmHost(make_shared<EVMHost>(m_evmVersion))
m_vmPaths(_vmPaths)
{
if (solidity::test::CommonOptions::get().optimize)
m_optimiserSettings = solidity::frontend::OptimiserSettings::standard();
for (auto const& path: m_vmPaths)
if (EVMHost::getVM(path.string()).has_capability(EVMC_CAPABILITY_EWASM))
m_supportsEwasm = true;
selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1);
}
void ExecutionFramework::selectVM(evmc_capabilities _cap)
{
m_evmcHost.reset();
for (auto const& path: m_vmPaths)
{
evmc::VM& vm = EVMHost::getVM(path.string());
if (vm.has_capability(_cap))
{
m_evmcHost = make_unique<EVMHost>(m_evmVersion, vm);
break;
}
}
solAssert(m_evmcHost != nullptr, "");
reset();
}
void ExecutionFramework::reset()
{
m_evmHost->reset();
m_evmcHost->reset();
for (size_t i = 0; i < 10; i++)
m_evmHost->accounts[EVMHost::convertToEVMC(account(i))].balance =
m_evmcHost->accounts[EVMHost::convertToEVMC(account(i))].balance =
EVMHost::convertToEVMC(u256(1) << 100);
}
@ -92,7 +114,7 @@ std::pair<bool, string> ExecutionFramework::compareAndCreateMessage(
u256 ExecutionFramework::gasLimit() const
{
return {m_evmHost->tx_context.block_gas_limit};
return {m_evmcHost->tx_context.block_gas_limit};
}
u256 ExecutionFramework::gasPrice() const
@ -100,24 +122,24 @@ u256 ExecutionFramework::gasPrice() const
// here and below we use "return u256{....}" instead of just "return {....}"
// to please MSVC and avoid unexpected
// warning C4927 : illegal conversion; more than one user - defined conversion has been implicitly applied
return u256{EVMHost::convertFromEVMC(m_evmHost->tx_context.tx_gas_price)};
return u256{EVMHost::convertFromEVMC(m_evmcHost->tx_context.tx_gas_price)};
}
u256 ExecutionFramework::blockHash(u256 const& _number) const
{
return u256{EVMHost::convertFromEVMC(
m_evmHost->get_block_hash(static_cast<int64_t>(_number & numeric_limits<uint64_t>::max()))
m_evmcHost->get_block_hash(static_cast<int64_t>(_number & numeric_limits<uint64_t>::max()))
)};
}
u256 ExecutionFramework::blockNumber() const
{
return m_evmHost->tx_context.block_number;
return m_evmcHost->tx_context.block_number;
}
void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256 const& _value)
{
m_evmHost->newBlock();
m_evmcHost->newBlock();
if (m_showMessages)
{
@ -147,7 +169,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256
}
message.gas = m_gas.convert_to<int64_t>();
evmc::result result = m_evmHost->call(message);
evmc::result result = m_evmcHost->call(message);
m_output = bytes(result.output_data, result.output_data + result.output_size);
if (_isCreation)
@ -166,7 +188,7 @@ void ExecutionFramework::sendMessage(bytes const& _data, bool _isCreation, u256
void ExecutionFramework::sendEther(Address const& _addr, u256 const& _amount)
{
m_evmHost->newBlock();
m_evmcHost->newBlock();
if (m_showMessages)
{
@ -181,12 +203,12 @@ void ExecutionFramework::sendEther(Address const& _addr, u256 const& _amount)
message.destination = EVMHost::convertToEVMC(_addr);
message.gas = m_gas.convert_to<int64_t>();
m_evmHost->call(message);
m_evmcHost->call(message);
}
size_t ExecutionFramework::currentTimestamp()
{
return static_cast<size_t>(m_evmHost->tx_context.block_timestamp);
return static_cast<size_t>(m_evmcHost->tx_context.block_timestamp);
}
size_t ExecutionFramework::blockTimestamp(u256 _block)
@ -204,32 +226,32 @@ Address ExecutionFramework::account(size_t _idx)
bool ExecutionFramework::addressHasCode(Address const& _addr)
{
return m_evmHost->get_code_size(EVMHost::convertToEVMC(_addr)) != 0;
return m_evmcHost->get_code_size(EVMHost::convertToEVMC(_addr)) != 0;
}
size_t ExecutionFramework::numLogs() const
{
return m_evmHost->recorded_logs.size();
return m_evmcHost->recorded_logs.size();
}
size_t ExecutionFramework::numLogTopics(size_t _logIdx) const
{
return m_evmHost->recorded_logs.at(_logIdx).topics.size();
return m_evmcHost->recorded_logs.at(_logIdx).topics.size();
}
h256 ExecutionFramework::logTopic(size_t _logIdx, size_t _topicIdx) const
{
return EVMHost::convertFromEVMC(m_evmHost->recorded_logs.at(_logIdx).topics.at(_topicIdx));
return EVMHost::convertFromEVMC(m_evmcHost->recorded_logs.at(_logIdx).topics.at(_topicIdx));
}
Address ExecutionFramework::logAddress(size_t _logIdx) const
{
return EVMHost::convertFromEVMC(m_evmHost->recorded_logs.at(_logIdx).creator);
return EVMHost::convertFromEVMC(m_evmcHost->recorded_logs.at(_logIdx).creator);
}
bytes ExecutionFramework::logData(size_t _logIdx) const
{
const auto& data = m_evmHost->recorded_logs.at(_logIdx).data;
const auto& data = m_evmcHost->recorded_logs.at(_logIdx).data;
// TODO: Return a copy of log data, because this is expected from REQUIRE_LOG_DATA(),
// but reference type like string_view would be preferable.
return {data.begin(), data.end()};
@ -237,13 +259,13 @@ bytes ExecutionFramework::logData(size_t _logIdx) const
u256 ExecutionFramework::balanceAt(Address const& _addr)
{
return u256(EVMHost::convertFromEVMC(m_evmHost->get_balance(EVMHost::convertToEVMC(_addr))));
return u256(EVMHost::convertFromEVMC(m_evmcHost->get_balance(EVMHost::convertToEVMC(_addr))));
}
bool ExecutionFramework::storageEmpty(Address const& _addr)
{
const auto it = m_evmHost->accounts.find(EVMHost::convertToEVMC(_addr));
if (it != m_evmHost->accounts.end())
const auto it = m_evmcHost->accounts.find(EVMHost::convertToEVMC(_addr));
if (it != m_evmcHost->accounts.end())
{
for (auto const& entry: it->second.storage)
if (!(entry.second.value == evmc::bytes32{}))

View File

@ -24,6 +24,7 @@
#pragma once
#include <test/Common.h>
#include <test/EVMHost.h>
#include <libsolidity/interface/OptimiserSettings.h>
#include <libsolidity/interface/DebugSettings.h>
@ -39,8 +40,6 @@
namespace solidity::test
{
class EVMHost;
using rational = boost::rational<bigint>;
/// An Ethereum address: 20 bytes.
/// @NOTE This is not endian-specific; it's just a bunch of bytes.
@ -55,7 +54,7 @@ class ExecutionFramework
public:
ExecutionFramework();
explicit ExecutionFramework(langutil::EVMVersion _evmVersion);
ExecutionFramework(langutil::EVMVersion _evmVersion, std::vector<boost::filesystem::path> const& _vmPaths);
virtual ~ExecutionFramework() = default;
virtual bytes const& compileAndRunWithoutCheck(
@ -255,6 +254,7 @@ private:
}
protected:
void selectVM(evmc_capabilities _cap = evmc_capabilities::EVMC_CAPABILITY_EVM1);
void reset();
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0);
@ -279,7 +279,10 @@ protected:
solidity::frontend::RevertStrings m_revertStrings = solidity::frontend::RevertStrings::Default;
solidity::frontend::OptimiserSettings m_optimiserSettings = solidity::frontend::OptimiserSettings::minimal();
bool m_showMessages = false;
std::shared_ptr<EVMHost> m_evmHost;
bool m_supportsEwasm = false;
std::unique_ptr<EVMHost> m_evmcHost;
std::vector<boost::filesystem::path> m_vmPaths;
bool m_transactionSuccessful = true;
Address m_sender = account(0);

View File

@ -39,6 +39,7 @@ public:
{
std::string filename;
langutil::EVMVersion evmVersion;
std::vector<boost::filesystem::path> vmPaths;
bool enforceCompileViaYul;
};

View File

@ -39,7 +39,6 @@
#include <test/InteractiveTests.h>
#include <test/Common.h>
#include <test/EVMHost.h>
#include <test/Common.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
@ -72,7 +71,7 @@ int registerTests(
{
int numTestsAdded = 0;
fs::path fullpath = _basepath / _path;
TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion(), _enforceViaYul};
TestCase::Config config{fullpath.string(), solidity::test::CommonOptions::get().evmVersion(), solidity::test::CommonOptions::get().vmPaths, _enforceViaYul};
if (fs::is_directory(fullpath))
{
test_suite* sub_suite = BOOST_TEST_SUITE(_path.filename().string());
@ -156,14 +155,19 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
initializeOptions();
bool disableSemantics = !solidity::test::EVMHost::getVM(solidity::test::CommonOptions::get().evmonePath.string());
if (disableSemantics)
bool disableSemantics = true;
try
{
cout << "Unable to find " << solidity::test::evmoneFilename << ". Please provide the path using -- --evmonepath <path>." << endl;
cout << "You can download it at" << endl;
cout << solidity::test::evmoneDownloadLink << endl;
cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl;
disableSemantics = !solidity::test::EVMHost::checkVmPaths(solidity::test::CommonOptions::get().vmPaths);
}
catch (std::runtime_error const& _exception)
{
cerr << "Error: " << _exception.what() << endl;
exit(1);
}
if (disableSemantics)
cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl;
// Include the interactive tests in the automatic tests as well
for (auto const& ts: g_interactiveTestsuites)
{

View File

@ -417,7 +417,7 @@ BOOST_AUTO_TEST_CASE(auction_simple)
BOOST_CHECK_EQUAL(registrar.owner(name), 0);
// "wait" until auction end
m_evmHost->tx_context.block_timestamp += m_biddingTime + 10;
m_evmcHost->tx_context.block_timestamp += m_biddingTime + 10;
// trigger auction again
registrar.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), m_sender);
@ -429,7 +429,7 @@ BOOST_AUTO_TEST_CASE(auction_bidding)
string name = "x";
unsigned startTime = 0x776347e2;
m_evmHost->tx_context.block_timestamp = startTime;
m_evmcHost->tx_context.block_timestamp = startTime;
RegistrarInterface registrar(*this);
// initiate auction
@ -437,19 +437,19 @@ BOOST_AUTO_TEST_CASE(auction_bidding)
registrar.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), 0);
// overbid self
m_evmHost->tx_context.block_timestamp = startTime + m_biddingTime - 10;
m_evmcHost->tx_context.block_timestamp = startTime + m_biddingTime - 10;
registrar.setNextValue(12);
registrar.reserve(name);
// another bid by someone else
sendEther(account(1), 10 * ether);
m_sender = account(1);
m_evmHost->tx_context.block_timestamp = startTime + 2 * m_biddingTime - 50;
m_evmcHost->tx_context.block_timestamp = startTime + 2 * m_biddingTime - 50;
registrar.setNextValue(13);
registrar.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), 0);
// end auction by first bidder (which is not highest) trying to overbid again (too late)
m_sender = account(0);
m_evmHost->tx_context.block_timestamp = startTime + 4 * m_biddingTime;
m_evmcHost->tx_context.block_timestamp = startTime + 4 * m_biddingTime;
registrar.setNextValue(20);
registrar.reserve(name);
BOOST_CHECK_EQUAL(registrar.owner(name), account(1));

View File

@ -39,8 +39,8 @@ using namespace boost::unit_test;
namespace fs = boost::filesystem;
SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion, bool enforceViaYul):
SolidityExecutionFramework(_evmVersion),
SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVersion, vector<boost::filesystem::path> const& _vmPaths, bool enforceViaYul):
SolidityExecutionFramework(_evmVersion, _vmPaths),
EVMVersionRestrictedTestCase(_filename),
m_sources(m_reader.sources()),
m_lineOffset(m_reader.lineNumber()),
@ -72,6 +72,21 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
else
BOOST_THROW_EXCEPTION(runtime_error("Invalid compileViaYul value: " + choice + "."));
string compileToEwasm = m_reader.stringSetting("compileToEwasm", "false");
if (compileToEwasm == "also")
m_runWithEwasm = true;
else if (compileToEwasm == "false")
m_runWithEwasm = false;
else
BOOST_THROW_EXCEPTION(runtime_error("Invalid compileToEwasm value: " + compileToEwasm + "."));
if (m_runWithEwasm && !m_runWithYul)
BOOST_THROW_EXCEPTION(runtime_error("Invalid compileToEwasm value: " + compileToEwasm + ", compileViaYul need to be enabled."));
// run ewasm tests only, if an ewasm evmc vm was defined
if (m_runWithEwasm && !m_supportsEwasm)
m_runWithEwasm = false;
m_runWithABIEncoderV1Only = m_reader.boolSetting("ABIEncoderV1Only", false);
if (m_runWithABIEncoderV1Only && solidity::test::CommonOptions::get().useABIEncoderV2)
m_shouldRun = false;
@ -88,156 +103,180 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)
{
TestResult result = TestResult::Success;
bool compileViaYul = m_runWithYul || m_enforceViaYul;
for (bool compileViaYul: set<bool>{!m_runWithoutYul, m_runWithYul || m_enforceViaYul})
if (m_runWithoutYul)
result = runTest(_stream, _linePrefix, _formatted, false, false);
if (compileViaYul && result == TestResult::Success)
result = runTest(_stream, _linePrefix, _formatted, true, false);
if (m_runWithEwasm && result == TestResult::Success)
result = runTest(_stream, _linePrefix, _formatted, true, true);
return result;
}
TestCase::TestResult SemanticTest::runTest(ostream& _stream, string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm)
{
try
{
try
bool success = true;
if (_compileViaYul && _compileToEwasm)
selectVM(evmc_capabilities::EVMC_CAPABILITY_EWASM);
else
selectVM(evmc_capabilities::EVMC_CAPABILITY_EVM1);
reset();
m_compileViaYul = _compileViaYul;
if (_compileToEwasm)
{
reset();
bool success = true;
soltestAssert(m_compileViaYul, "");
m_compileToEwasm = _compileToEwasm;
}
m_compileViaYul = compileViaYul;
m_compileViaYulCanBeSet = false;
m_compileViaYulCanBeSet = false;
if (compileViaYul)
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl;
if (_compileViaYul)
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl;
for (auto& test: m_tests)
test.reset();
for (auto& test: m_tests)
test.reset();
map<string, solidity::test::Address> libraries;
map<string, solidity::test::Address> libraries;
bool constructed = false;
bool constructed = false;
for (auto& test: m_tests)
for (auto& test: m_tests)
{
if (constructed)
{
if (constructed)
{
soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call.");
soltestAssert(!test.call().isConstructor, "Constructor has to be the first function call expect for library deployments.");
}
else if (test.call().isLibrary)
soltestAssert(!test.call().isLibrary, "Libraries have to be deployed before any other call.");
soltestAssert(
!test.call().isConstructor,
"Constructor has to be the first function call expect for library deployments.");
}
else if (test.call().isLibrary)
{
soltestAssert(
deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful,
"Failed to deploy library " + test.call().signature);
libraries[test.call().signature] = m_contractAddress;
continue;
}
else
{
if (test.call().isConstructor)
deploy("", test.call().value.value, test.call().arguments.rawBytes(), libraries);
else
soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract.");
constructed = true;
}
if (test.call().isConstructor)
{
if (m_transactionSuccessful == test.call().expectations.failure)
success = false;
test.setFailure(!m_transactionSuccessful);
test.setRawBytes(bytes());
}
else
{
bytes output;
if (test.call().useCallWithoutSignature)
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
else
{
soltestAssert(
deploy(test.call().signature, 0, {}, libraries) && m_transactionSuccessful,
"Failed to deploy library " + test.call().signature
m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName())
.isMember(test.call().signature),
"The function " + test.call().signature + " is not known to the compiler");
output = callContractFunctionWithValueNoEncoding(
test.call().signature, test.call().value.value, test.call().arguments.rawBytes()
);
libraries[test.call().signature] = m_contractAddress;
continue;
}
else
{
if (test.call().isConstructor)
deploy("", test.call().value.value, test.call().arguments.rawBytes(), libraries);
else
soltestAssert(deploy("", 0, bytes(), libraries), "Failed to deploy contract.");
constructed = true;
}
if (test.call().isConstructor)
{
if (m_transactionSuccessful == test.call().expectations.failure)
success = false;
if ((m_transactionSuccessful == test.call().expectations.failure)
|| (output != test.call().expectations.rawBytes()))
success = false;
test.setFailure(!m_transactionSuccessful);
test.setRawBytes(bytes());
}
else
{
bytes output;
if (test.call().useCallWithoutSignature)
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
else
{
soltestAssert(
m_allowNonExistingFunctions || m_compiler.methodIdentifiers(m_compiler.lastContractName()).isMember(test.call().signature),
"The function " + test.call().signature + " is not known to the compiler"
);
output = callContractFunctionWithValueNoEncoding(
test.call().signature,
test.call().value.value,
test.call().arguments.rawBytes()
);
}
if ((m_transactionSuccessful == test.call().expectations.failure) || (output != test.call().expectations.rawBytes()))
success = false;
test.setFailure(!m_transactionSuccessful);
test.setRawBytes(std::move(output));
test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName()));
}
test.setFailure(!m_transactionSuccessful);
test.setRawBytes(std::move(output));
test.setContractABI(m_compiler.contractABI(m_compiler.lastContractName()));
}
}
if (success && !m_runWithYul && compileViaYul)
if (success && !m_runWithYul && _compileViaYul)
{
m_compileViaYulCanBeSet = true;
AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) <<
_linePrefix << endl <<
_linePrefix << "Test can pass via Yul and marked with compileViaYul: false." << endl;
return TestResult::Failure;
}
if (!success && (m_runWithYul || !_compileViaYul))
{
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
for (auto const& test: m_tests)
{
m_compileViaYulCanBeSet = true;
AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl << _linePrefix
<< "Test can pass via Yul and marked with compileViaYul: false." << endl;
return TestResult::Failure;
ErrorReporter errorReporter;
_stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl;
_stream << errorReporter.format(_linePrefix, _formatted);
}
if (!success && (m_runWithYul || !compileViaYul))
_stream << endl;
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
for (auto const& test: m_tests)
{
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
for (auto const& test: m_tests)
{
ErrorReporter errorReporter;
_stream << test.format(errorReporter, _linePrefix, false, _formatted) << endl;
_stream << errorReporter.format(_linePrefix, _formatted);
}
ErrorReporter errorReporter;
_stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl;
_stream << errorReporter.format(_linePrefix, _formatted);
}
AnsiColorized(_stream, _formatted, {BOLD, RED})
<< _linePrefix << endl
<< _linePrefix << "Attention: Updates on the test will apply the detected format displayed." << endl;
if (_compileViaYul && m_runWithoutYul)
{
_stream << _linePrefix << endl << _linePrefix;
AnsiColorized(_stream, _formatted, {RED_BACKGROUND}) << "Note that the test passed without Yul.";
_stream << endl;
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
for (auto const& test: m_tests)
{
ErrorReporter errorReporter;
_stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl;
_stream << errorReporter.format(_linePrefix, _formatted);
}
AnsiColorized(_stream, _formatted, {BOLD, RED}) << _linePrefix << endl << _linePrefix
<< "Attention: Updates on the test will apply the detected format displayed." << endl;
if (compileViaYul && m_runWithoutYul)
{
_stream << _linePrefix << endl << _linePrefix;
AnsiColorized(_stream, _formatted, {RED_BACKGROUND})
<< "Note that the test passed without Yul.";
_stream << endl;
}
else if (!compileViaYul && m_runWithYul)
AnsiColorized(_stream, _formatted, {BOLD, YELLOW}) << _linePrefix << endl << _linePrefix
<< "Note that the test also has to pass via Yul." << endl;
return TestResult::Failure;
}
else if (!_compileViaYul && m_runWithYul)
AnsiColorized(_stream, _formatted, {BOLD, YELLOW})
<< _linePrefix << endl
<< _linePrefix << "Note that the test also has to pass via Yul." << endl;
return TestResult::Failure;
}
catch (WhiskersError const&)
{
// this is an error in Whiskers template, so should be thrown anyway
}
catch (WhiskersError const&)
{
// this is an error in Whiskers template, so should be thrown anyway
throw;
}
catch (YulException const&)
{
// this should be an error in yul compilation or translation
throw;
}
catch (boost::exception const&)
{
if (!_compileViaYul || m_runWithYul)
throw;
}
catch (YulException const&)
{
// this should be an error in yul compilation or translation
}
catch (std::exception const&)
{
if (!_compileViaYul || m_runWithYul)
throw;
}
catch (boost::exception const&)
{
if (compileViaYul && !m_runWithYul)
continue;
}
catch (...)
{
if (!_compileViaYul || m_runWithYul)
throw;
}
catch (std::exception const&)
{
if (compileViaYul && !m_runWithYul)
continue;
throw;
}
catch (...)
{
if (compileViaYul && !m_runWithYul)
continue;
throw;
}
}
return TestResult::Success;

View File

@ -40,9 +40,9 @@ class SemanticTest: public SolidityExecutionFramework, public EVMVersionRestrict
{
public:
static std::unique_ptr<TestCase> create(Config const& _options)
{ return std::make_unique<SemanticTest>(_options.filename, _options.evmVersion, _options.enforceCompileViaYul); }
{ return std::make_unique<SemanticTest>(_options.filename, _options.evmVersion, _options.vmPaths, _options.enforceCompileViaYul); }
explicit SemanticTest(std::string const& _filename, langutil::EVMVersion _evmVersion, bool _enforceViaYul = false);
explicit SemanticTest(std::string const& _filename, langutil::EVMVersion _evmVersion, std::vector<boost::filesystem::path> const& _vmPaths, bool _enforceViaYul = false);
TestResult run(std::ostream& _stream, std::string const& _linePrefix = "", bool _formatted = false) override;
void printSource(std::ostream &_stream, std::string const& _linePrefix = "", bool _formatted = false) const override;
@ -59,10 +59,12 @@ public:
/// Returns true if deployment was successful, false otherwise.
bool deploy(std::string const& _contractName, u256 const& _value, bytes const& _arguments, std::map<std::string, solidity::test::Address> const& _libraries = {});
private:
TestResult runTest(std::ostream& _stream, std::string const& _linePrefix, bool _formatted, bool _compileViaYul, bool _compileToEwasm);
SourceMap m_sources;
std::size_t m_lineOffset;
std::vector<TestFunctionCall> m_tests;
bool m_runWithYul = false;
bool m_runWithEwasm = false;
bool m_runWithoutYul = true;
bool m_enforceViaYul = false;
bool m_runWithABIEncoderV1Only = false;

View File

@ -48,18 +48,38 @@ using namespace solidity::util;
using namespace solidity::test;
using namespace solidity::langutil;
#define ALSO_VIA_YUL(CODE) \
{ \
{ CODE } \
reset(); \
m_compileViaYul = true; \
{ CODE } \
#define ALSO_VIA_YUL(CODE) \
{ \
m_doEwasmTestrun = true; \
\
m_compileViaYul = false; \
m_compileToEwasm = false; \
{ CODE } \
\
m_compileViaYul = true; \
reset(); \
{ CODE } \
\
if (m_doEwasmTestrun) \
{ \
m_compileToEwasm = true; \
reset(); \
{ CODE } \
} \
}
#define DISABLE_EWASM_TESTRUN() \
{ m_doEwasmTestrun = false; }
namespace solidity::frontend::test
{
BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityExecutionFramework)
struct SolidityEndToEndTestExecutionFramework: public SolidityExecutionFramework
{
bool m_doEwasmTestrun = false;
};
BOOST_FIXTURE_TEST_SUITE(SolidityEndToEndTest, SolidityEndToEndTestExecutionFramework)
int constexpr roundTo32(int _num)
{
@ -115,6 +135,8 @@ BOOST_AUTO_TEST_CASE(recursive_calls)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
function<u256(u256)> recursive_calls_cpp = [&recursive_calls_cpp](u256 const& n) -> u256
{
@ -140,6 +162,8 @@ BOOST_AUTO_TEST_CASE(while_loop)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
auto while_loop_cpp = [](u256 const& n) -> u256
@ -168,6 +192,8 @@ BOOST_AUTO_TEST_CASE(do_while_loop)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
auto do_while_loop_cpp = [](u256 const& n) -> u256
@ -213,6 +239,8 @@ BOOST_AUTO_TEST_CASE(do_while_loop_multiple_local_vars)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
auto do_while = [](u256 n) -> u256
@ -263,6 +291,8 @@ BOOST_AUTO_TEST_CASE(nested_loops)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
auto nested_loops_cpp = [](u256 n) -> u256
@ -329,6 +359,8 @@ BOOST_AUTO_TEST_CASE(nested_loops_multiple_local_vars)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
auto nested_loops_cpp = [](u256 n) -> u256
@ -383,6 +415,8 @@ BOOST_AUTO_TEST_CASE(for_loop_multiple_local_vars)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
auto for_loop = [](u256 n) -> u256
@ -444,6 +478,8 @@ BOOST_AUTO_TEST_CASE(nested_for_loop_multiple_local_vars)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
auto for_loop = [](u256 n) -> u256
@ -484,6 +520,8 @@ BOOST_AUTO_TEST_CASE(for_loop)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
auto for_loop_cpp = [](u256 const& n) -> u256
@ -512,6 +550,8 @@ BOOST_AUTO_TEST_CASE(for_loop_empty)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
auto for_loop_empty_cpp = []() -> u256
@ -542,6 +582,8 @@ BOOST_AUTO_TEST_CASE(for_loop_simple_init_expr)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
auto for_loop_simple_init_expr_cpp = [](u256 const& n) -> u256
@ -665,6 +707,8 @@ BOOST_AUTO_TEST_CASE(many_local_variables)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
auto f = [](u256 const& x1, u256 const& x2, u256 const& x3) -> u256
{
@ -689,6 +733,8 @@ BOOST_AUTO_TEST_CASE(short_circuiting)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
auto short_circuiting_cpp = [](u256 n) -> u256
@ -801,6 +847,8 @@ BOOST_AUTO_TEST_CASE(compound_assign)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
u256 value1;
@ -863,6 +911,8 @@ BOOST_AUTO_TEST_CASE(mapping_state)
map<u160, bool> m_voted;
};
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
Ballot ballot;
@ -936,6 +986,8 @@ BOOST_AUTO_TEST_CASE(mapping_state_inc_dec)
return --table[value++];
};
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
value = 0;
table.clear();
@ -962,6 +1014,8 @@ BOOST_AUTO_TEST_CASE(multi_level_mapping)
else return table[_x][_y] = _z;
};
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
table.clear();
@ -998,6 +1052,8 @@ BOOST_AUTO_TEST_CASE(constructor)
};
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
testContractAgainstCpp("get(uint256)", get, u256(6));
testContractAgainstCpp("get(uint256)", get, u256(7));
@ -1016,12 +1072,12 @@ BOOST_AUTO_TEST_CASE(blockchain)
}
}
)";
m_evmHost->tx_context.block_coinbase = EVMHost::convertToEVMC(Address("0x1212121212121212121212121212121212121212"));
m_evmHost->newBlock();
m_evmHost->newBlock();
m_evmHost->newBlock();
m_evmHost->newBlock();
m_evmHost->newBlock();
m_evmcHost->tx_context.block_coinbase = EVMHost::convertToEVMC(Address("0x1212121212121212121212121212121212121212"));
m_evmcHost->newBlock();
m_evmcHost->newBlock();
m_evmcHost->newBlock();
m_evmcHost->newBlock();
m_evmcHost->newBlock();
compileAndRun(sourceCode, 27);
ABI_CHECK(callContractFunctionWithValue("someInfo()", 28), encodeArgs(28, u256("0x1212121212121212121212121212121212121212"), 7));
}
@ -1038,6 +1094,8 @@ BOOST_AUTO_TEST_CASE(send_ether)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
u256 amount(250);
compileAndRun(sourceCode, amount + 1);
u160 address(23);
@ -1070,6 +1128,8 @@ BOOST_AUTO_TEST_CASE(transfer_ether)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode, 0, "B");
u160 const nonPayableRecipient = m_contractAddress;
compileAndRun(sourceCode, 0, "C");
@ -1110,6 +1170,8 @@ BOOST_AUTO_TEST_CASE(log0)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1129,6 +1191,8 @@ BOOST_AUTO_TEST_CASE(log1)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1149,6 +1213,8 @@ BOOST_AUTO_TEST_CASE(log2)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1170,6 +1236,8 @@ BOOST_AUTO_TEST_CASE(log3)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1191,6 +1259,8 @@ BOOST_AUTO_TEST_CASE(log4)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
callContractFunction("a()");
BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1212,6 +1282,8 @@ BOOST_AUTO_TEST_CASE(log_in_constructor)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
BOOST_REQUIRE_EQUAL(numLogs(), 1);
BOOST_CHECK_EQUAL(logAddress(0), m_contractAddress);
@ -1235,6 +1307,8 @@ BOOST_AUTO_TEST_CASE(selfdestruct)
u256 amount(130);
u160 address(23);
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode, amount);
ABI_CHECK(callContractFunction("a(address)", address), bytes());
BOOST_CHECK(!addressHasCode(m_contractAddress));
@ -1665,6 +1739,8 @@ BOOST_AUTO_TEST_CASE(gaslimit)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
auto result = callContractFunction("f()");
ABI_CHECK(result, encodeArgs(gasLimit()));
@ -1681,6 +1757,8 @@ BOOST_AUTO_TEST_CASE(gasprice)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("f()"), encodeArgs(gasPrice()));
)
@ -1772,6 +1850,8 @@ BOOST_AUTO_TEST_CASE(event)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
u256 value(18);
u256 id(0x1234);
@ -1800,6 +1880,8 @@ BOOST_AUTO_TEST_CASE(event_emit)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
u256 value(18);
u256 id(0x1234);
@ -1826,6 +1908,8 @@ BOOST_AUTO_TEST_CASE(event_no_arguments)
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
callContractFunction("deposit()");
BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1850,6 +1934,8 @@ BOOST_AUTO_TEST_CASE(event_access_through_base_name_emit)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
callContractFunction("f()");
BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1889,6 +1975,8 @@ BOOST_AUTO_TEST_CASE(events_with_same_name)
u160 const c_loggedAddress = m_contractAddress;
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1)));
BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1950,6 +2038,8 @@ BOOST_AUTO_TEST_CASE(events_with_same_name_inherited_emit)
u160 const c_loggedAddress = m_contractAddress;
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
ABI_CHECK(callContractFunction("deposit()"), encodeArgs(u256(1)));
BOOST_REQUIRE_EQUAL(numLogs(), 1);
@ -1985,6 +2075,8 @@ BOOST_AUTO_TEST_CASE(event_anonymous)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
callContractFunction("deposit()");
BOOST_REQUIRE_EQUAL(numLogTopics(0), 0);
@ -2002,6 +2094,8 @@ BOOST_AUTO_TEST_CASE(event_anonymous_with_topics)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
u256 value(18);
u256 id(0x1234);
@ -2028,6 +2122,8 @@ BOOST_AUTO_TEST_CASE(event_lots_of_data)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
u256 value(18);
u256 id(0x1234);
@ -2247,6 +2343,8 @@ BOOST_AUTO_TEST_CASE(event_dynamic_array_storage)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
u256 x(42);
callContractFunction("createEvent(uint256)", x);
@ -2276,6 +2374,8 @@ BOOST_AUTO_TEST_CASE(event_dynamic_array_storage_v2)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
u256 x(42);
callContractFunction("createEvent(uint256)", x);
@ -2365,6 +2465,8 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("f(uint256,uint256)", 5, 9) != encodeArgs(5, 8));
ABI_CHECK(callContractFunction("f(uint256,uint256)", 5, 9), encodeArgs(9, 8));
@ -2987,6 +3089,8 @@ BOOST_AUTO_TEST_CASE(fixed_array_cleanup)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes());
@ -3010,6 +3114,8 @@ BOOST_AUTO_TEST_CASE(short_fixed_array_cleanup)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes());
@ -3037,6 +3143,8 @@ BOOST_AUTO_TEST_CASE(dynamic_array_cleanup)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
BOOST_CHECK(storageEmpty(m_contractAddress));
ABI_CHECK(callContractFunction("fill()"), bytes());
@ -4305,6 +4413,8 @@ BOOST_AUTO_TEST_CASE(string_as_mapping_key)
};
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode, 0, "Test");
for (unsigned i = 0; i < strings.size(); i++)
ABI_CHECK(callContractFunction(
@ -5415,6 +5525,8 @@ BOOST_AUTO_TEST_CASE(no_nonpayable_circumvention_by_modifier)
}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode);
ABI_CHECK(callContractFunctionWithValue("f()", 27), encodeArgs());
BOOST_CHECK_EQUAL(balanceAt(m_contractAddress), 0);
@ -5527,6 +5639,8 @@ BOOST_AUTO_TEST_CASE(contracts_separated_with_comment)
contract C2 {}
)";
ALSO_VIA_YUL(
DISABLE_EWASM_TESTRUN()
compileAndRun(sourceCode, 0, "C1");
compileAndRun(sourceCode, 0, "C2");
)

View File

@ -43,6 +43,7 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
entry.second = addPreamble(entry.second);
m_compiler.reset();
m_compiler.enableEwasmGeneration(m_compileToEwasm);
m_compiler.setSources(sourcesWithPreamble);
m_compiler.setLibraries(_libraryAddresses);
m_compiler.setRevertStringBehaviour(m_revertStrings);
@ -63,38 +64,40 @@ bytes SolidityExecutionFramework::multiSourceCompileContract(
evmasm::LinkerObject obj;
if (m_compileViaYul)
{
// Try compiling twice: If the first run fails due to stack errors, forcefully enable
// the optimizer.
for (bool forceEnableOptimizer: {false, true})
if (m_compileToEwasm)
obj = m_compiler.ewasmObject(contractName);
else
{
OptimiserSettings optimiserSettings = m_optimiserSettings;
if (!forceEnableOptimizer && !optimiserSettings.runYulOptimiser)
// Try compiling twice: If the first run fails due to stack errors, forcefully enable
// the optimizer.
for (bool forceEnableOptimizer: {false, true})
{
// Enable some optimizations on the first run
optimiserSettings.runYulOptimiser = true;
optimiserSettings.yulOptimiserSteps = "uljmul jmul";
}
else if (forceEnableOptimizer)
optimiserSettings = OptimiserSettings::full();
OptimiserSettings optimiserSettings = m_optimiserSettings;
if (!forceEnableOptimizer && !optimiserSettings.runYulOptimiser)
{
// Enable some optimizations on the first run
optimiserSettings.runYulOptimiser = true;
optimiserSettings.yulOptimiserSteps = "uljmul jmul";
}
else if (forceEnableOptimizer)
optimiserSettings = OptimiserSettings::full();
yul::AssemblyStack asmStack(
m_evmVersion,
yul::AssemblyStack::Language::StrictAssembly,
optimiserSettings
);
bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName));
solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors");
yul::AssemblyStack
asmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, optimiserSettings);
bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName));
solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors");
try
{
asmStack.optimize();
obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode);
break;
}
catch (...)
{
if (forceEnableOptimizer || optimiserSettings == OptimiserSettings::full())
throw;
try
{
asmStack.optimize();
obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode);
break;
}
catch (...)
{
if (forceEnableOptimizer || optimiserSettings == OptimiserSettings::full())
throw;
}
}
}
}

View File

@ -43,8 +43,8 @@ class SolidityExecutionFramework: public solidity::test::ExecutionFramework
public:
SolidityExecutionFramework(): m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {}
explicit SolidityExecutionFramework(langutil::EVMVersion _evmVersion):
ExecutionFramework(_evmVersion), m_showMetadata(solidity::test::CommonOptions::get().showMetadata)
explicit SolidityExecutionFramework(langutil::EVMVersion _evmVersion, std::vector<boost::filesystem::path> const& _vmPaths):
ExecutionFramework(_evmVersion, _vmPaths), m_showMetadata(solidity::test::CommonOptions::get().showMetadata)
{}
bytes const& compileAndRunWithoutCheck(
@ -76,8 +76,10 @@ public:
/// the latter only if it is required.
static std::string addPreamble(std::string const& _sourceCode);
protected:
solidity::frontend::CompilerStack m_compiler;
bool m_compileViaYul = false;
bool m_compileToEwasm = false;
bool m_showMetadata = false;
RevertStrings m_revertStrings = RevertStrings::Default;
};

View File

@ -161,6 +161,7 @@ TestTool::Result TestTool::process()
m_test = m_testCaseCreator(TestCase::Config{
m_path.string(),
m_options.evmVersion(),
m_options.vmPaths,
m_options.enforceViaYul
});
if (m_test->shouldRun())
@ -424,14 +425,19 @@ int main(int argc, char const *argv[])
auto& options = dynamic_cast<solidity::test::IsolTestOptions const&>(solidity::test::CommonOptions::get());
bool disableSemantics = !solidity::test::EVMHost::getVM(options.evmonePath.string());
if (disableSemantics)
bool disableSemantics = true;
try
{
cout << "Unable to find " << solidity::test::evmoneFilename << ". Please provide the path using --evmonepath <path>." << endl;
cout << "You can download it at" << endl;
cout << solidity::test::evmoneDownloadLink << endl;
cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl;
disableSemantics = !solidity::test::EVMHost::checkVmPaths(options.vmPaths);
}
catch (std::runtime_error const& _exception)
{
cerr << "Error: " << _exception.what() << endl;
return 1;
}
if (disableSemantics)
cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl;
TestStats global_stats{0, 0};
cout << "Running tests..." << endl << endl;
@ -472,7 +478,7 @@ int main(int argc, char const *argv[])
cout << "." << endl;
if (disableSemantics)
cout << "\nNOTE: Skipped semantics tests because " << solidity::test::evmoneFilename << " could not be found.\n" << endl;
cout << "\nNOTE: Skipped semantics tests because no evmc vm could be found.\n" << endl;
return global_stats ? 0 : 1;
}