mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Add support for ewasm evmc vm.
This commit is contained in:
parent
e92758345b
commit
f4fc095ae0
@ -92,6 +92,7 @@ CommonOptions::CommonOptions(std::string _caption):
|
||||
("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 defined multiple times.")
|
||||
("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.")
|
||||
@ -140,6 +141,9 @@ bool CommonOptions::parse(int argc, char const* const* argv)
|
||||
throw std::runtime_error(errorMessage.str());
|
||||
}
|
||||
|
||||
if (!evmonePath.empty())
|
||||
vmPaths.emplace_back(evmonePath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -44,6 +44,7 @@ struct ConfigException : public util::Exception {};
|
||||
struct CommonOptions: boost::noncopyable
|
||||
{
|
||||
boost::filesystem::path evmonePath;
|
||||
std::vector<boost::filesystem::path> vmPaths;
|
||||
boost::filesystem::path testPath;
|
||||
bool optimize = false;
|
||||
bool enforceViaYul = false;
|
||||
|
||||
@ -38,17 +38,17 @@ using namespace evmc::literals;
|
||||
|
||||
evmc::VM& EVMHost::getVM(string const& _path)
|
||||
{
|
||||
static evmc::VM theVM;
|
||||
if (!theVM && !_path.empty())
|
||||
static std::map<string, std::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] = std::make_unique<evmc::VM>(std::move(vm));
|
||||
else
|
||||
cerr << "VM loaded does not support EVM1" << endl;
|
||||
cerr << "VM loaded does not support EVM1 or EWASM" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -58,7 +58,45 @@ evmc::VM& EVMHost::getVM(string const& _path)
|
||||
cerr << endl;
|
||||
}
|
||||
}
|
||||
return theVM;
|
||||
|
||||
if (vms.count(_path) > 0)
|
||||
return *vms[_path];
|
||||
|
||||
static evmc::VM NullVM(nullptr);
|
||||
return NullVM;
|
||||
}
|
||||
|
||||
bool EVMHost::checkVmPaths(std::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)
|
||||
{
|
||||
cerr << "Multiple evm1 evmc vms where defined. Please only define one evm1 evmc vm." << endl;
|
||||
assertThrow(false, Exception, "");
|
||||
}
|
||||
evmVmFound = true;
|
||||
}
|
||||
|
||||
if (vm.has_capability(EVMC_CAPABILITY_EWASM))
|
||||
{
|
||||
if (ewasmVmFound)
|
||||
{
|
||||
cerr << "Multiple ewasm evmc vms where defined. Please only define one ewasm evmc vm." << endl;
|
||||
assertThrow(false, Exception, "");
|
||||
}
|
||||
ewasmVmFound = true;
|
||||
}
|
||||
}
|
||||
return evmVmFound;
|
||||
}
|
||||
|
||||
EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm):
|
||||
|
||||
@ -29,6 +29,8 @@
|
||||
|
||||
#include <libsolutil/FixedHash.h>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
namespace solidity::test
|
||||
{
|
||||
using Address = util::h160;
|
||||
@ -44,6 +46,8 @@ public:
|
||||
/// afterwards.
|
||||
static evmc::VM& getVM(std::string const& _path = {});
|
||||
|
||||
static bool checkVmPaths(std::vector<boost::filesystem::path> const& _vmPaths);
|
||||
|
||||
explicit EVMHost(langutil::EVMVersion _evmVersion, evmc::VM& _vm = getVM());
|
||||
|
||||
void reset() { accounts.clear(); m_currentAddress = {}; }
|
||||
@ -70,6 +74,12 @@ public:
|
||||
static util::h256 convertFromEVMC(evmc::bytes32 const& _data);
|
||||
static evmc::bytes32 convertToEVMC(util::h256 const& _data);
|
||||
|
||||
/// Checks if the VM has the given capability.
|
||||
bool hasCapability(evmc_capabilities capability) const noexcept
|
||||
{
|
||||
return m_vm.has_capability(capability);
|
||||
}
|
||||
|
||||
private:
|
||||
evmc::address m_currentAddress = {};
|
||||
|
||||
|
||||
@ -28,6 +28,8 @@
|
||||
|
||||
#include <libsolutil/CommonIO.h>
|
||||
|
||||
#include <liblangutil/Exceptions.h>
|
||||
|
||||
#include <boost/test/framework.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
|
||||
@ -39,27 +41,54 @@ 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, std::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 : _vmPaths)
|
||||
{
|
||||
evmc::VM& vm = EVMHost::getVM(path.string());
|
||||
if (!m_evmHost && vm.has_capability(EVMC_CAPABILITY_EVM1))
|
||||
m_evmHost = make_shared<EVMHost>(m_evmVersion, vm);
|
||||
if (!m_ewasmHost && vm.has_capability(EVMC_CAPABILITY_EWASM))
|
||||
m_ewasmHost = make_shared<EVMHost>(m_evmVersion, vm);
|
||||
}
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void ExecutionFramework::reset()
|
||||
bool ExecutionFramework::supportsEwasm() const
|
||||
{
|
||||
m_evmHost->reset();
|
||||
if (m_ewasmHost)
|
||||
return m_ewasmHost->hasCapability(EVMC_CAPABILITY_EWASM);
|
||||
return false;
|
||||
}
|
||||
|
||||
void ExecutionFramework::reset(bool useEwasm)
|
||||
{
|
||||
if (useEwasm)
|
||||
{
|
||||
solAssert(supportsEwasm(), "");
|
||||
m_evmcHost = m_ewasmHost;
|
||||
}
|
||||
else
|
||||
{
|
||||
solAssert(m_evmHost != nullptr, "");
|
||||
m_evmcHost = m_evmHost;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -91,29 +120,29 @@ 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
|
||||
{
|
||||
return {EVMHost::convertFromEVMC(m_evmHost->tx_context.tx_gas_price)};
|
||||
return {EVMHost::convertFromEVMC(m_evmcHost->tx_context.tx_gas_price)};
|
||||
}
|
||||
|
||||
u256 ExecutionFramework::blockHash(u256 const& _number) const
|
||||
{
|
||||
return {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)
|
||||
{
|
||||
@ -143,7 +172,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)
|
||||
@ -162,7 +191,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)
|
||||
{
|
||||
@ -177,12 +206,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)
|
||||
@ -200,32 +229,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()};
|
||||
@ -233,13 +262,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{}))
|
||||
|
||||
@ -57,7 +57,7 @@ class ExecutionFramework
|
||||
|
||||
public:
|
||||
ExecutionFramework();
|
||||
explicit ExecutionFramework(langutil::EVMVersion _evmVersion);
|
||||
explicit ExecutionFramework(langutil::EVMVersion _evmVersion, std::vector<boost::filesystem::path> const& _vmPaths);
|
||||
virtual ~ExecutionFramework() = default;
|
||||
|
||||
virtual bytes const& compileAndRunWithoutCheck(
|
||||
@ -251,7 +251,9 @@ private:
|
||||
}
|
||||
|
||||
protected:
|
||||
void reset();
|
||||
void reset(bool useEwasm = false);
|
||||
|
||||
bool supportsEwasm() const;
|
||||
|
||||
void sendMessage(bytes const& _data, bool _isCreation, u256 const& _value = 0);
|
||||
void sendEther(Address const& _to, u256 const& _value);
|
||||
@ -275,7 +277,13 @@ 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_evmcHost;
|
||||
|
||||
std::shared_ptr<EVMHost> m_evmHost;
|
||||
std::shared_ptr<EVMHost> m_ewasmHost;
|
||||
|
||||
std::vector<boost::filesystem::path> const& m_vmPaths;
|
||||
|
||||
bool m_transactionSuccessful = true;
|
||||
Address m_sender = account(0);
|
||||
|
||||
@ -38,6 +38,7 @@ public:
|
||||
{
|
||||
std::string filename;
|
||||
langutil::EVMVersion evmVersion;
|
||||
std::vector<boost::filesystem::path> vmPaths;
|
||||
bool enforceCompileViaYul;
|
||||
};
|
||||
|
||||
|
||||
@ -70,7 +70,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());
|
||||
@ -150,7 +150,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] )
|
||||
|
||||
initializeOptions();
|
||||
|
||||
bool disableSemantics = !solidity::test::EVMHost::getVM(solidity::test::CommonOptions::get().evmonePath.string());
|
||||
bool disableSemantics = !solidity::test::EVMHost::checkVmPaths(solidity::test::CommonOptions::get().vmPaths);
|
||||
if (disableSemantics)
|
||||
{
|
||||
cout << "Unable to find " << solidity::test::evmoneFilename << ". Please provide the path using -- --evmonepath <path>." << endl;
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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, std::vector<boost::filesystem::path> const& _vmPaths, bool enforceViaYul):
|
||||
SolidityExecutionFramework(_evmVersion, _vmPaths),
|
||||
EVMVersionRestrictedTestCase(_filename),
|
||||
m_enforceViaYul(enforceViaYul)
|
||||
{
|
||||
@ -84,157 +84,164 @@ SemanticTest::SemanticTest(string const& _filename, langutil::EVMVersion _evmVer
|
||||
|
||||
TestCase::TestResult SemanticTest::run(ostream& _stream, string const& _linePrefix, bool _formatted)
|
||||
{
|
||||
|
||||
for (bool compileViaYul: set<bool>{!m_runWithoutYul, m_runWithYul || m_enforceViaYul})
|
||||
{
|
||||
try
|
||||
for (bool compileToEwasm: compileViaYul && supportsEwasm() ? set<bool>{false, true} : set<bool>{false})
|
||||
{
|
||||
reset();
|
||||
bool success = true;
|
||||
|
||||
m_compileViaYul = compileViaYul;
|
||||
m_compileViaYulCanBeSet = false;
|
||||
|
||||
if (compileViaYul)
|
||||
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Running via Yul:" << endl;
|
||||
|
||||
for (auto& test: m_tests)
|
||||
test.reset();
|
||||
|
||||
map<string, solidity::test::Address> libraries;
|
||||
|
||||
bool constructed = false;
|
||||
|
||||
for (auto& test: m_tests)
|
||||
try
|
||||
{
|
||||
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(
|
||||
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;
|
||||
}
|
||||
bool success = true;
|
||||
|
||||
if (test.call().isConstructor)
|
||||
{
|
||||
if (m_transactionSuccessful == test.call().expectations.failure)
|
||||
success = false;
|
||||
m_compileViaYul = compileViaYul;
|
||||
m_compileToEwasm = compileToEwasm;
|
||||
m_compileViaYulCanBeSet = false;
|
||||
|
||||
test.setFailure(!m_transactionSuccessful);
|
||||
test.setRawBytes(bytes());
|
||||
}
|
||||
else
|
||||
reset(m_compileToEwasm);
|
||||
|
||||
if (compileViaYul)
|
||||
AnsiColorized(_stream, _formatted, {BOLD, CYAN})
|
||||
<< _linePrefix << "Running via Yul" << (m_compileToEwasm ? " (ewasm):" : ":") << endl;
|
||||
|
||||
for (auto& test: m_tests)
|
||||
test.reset();
|
||||
|
||||
map<string, solidity::test::Address> libraries;
|
||||
|
||||
bool constructed = false;
|
||||
|
||||
for (auto& test: m_tests)
|
||||
{
|
||||
bytes output;
|
||||
if (test.call().useCallWithoutSignature)
|
||||
output = callLowLevel(test.call().arguments.rawBytes(), test.call().value.value);
|
||||
else
|
||||
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(
|
||||
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()
|
||||
);
|
||||
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 ((m_transactionSuccessful == test.call().expectations.failure) || (output != test.call().expectations.rawBytes()))
|
||||
success = false;
|
||||
if (test.call().isConstructor)
|
||||
{
|
||||
if (m_transactionSuccessful == test.call().expectations.failure)
|
||||
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(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");
|
||||
|
||||
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;
|
||||
}
|
||||
output = callContractFunctionWithValueNoEncoding(
|
||||
test.call().signature, test.call().value.value, test.call().arguments.rawBytes());
|
||||
}
|
||||
|
||||
if (!success && (m_runWithYul || !compileViaYul))
|
||||
{
|
||||
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);
|
||||
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()));
|
||||
}
|
||||
}
|
||||
_stream << endl;
|
||||
AnsiColorized(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:" << endl;
|
||||
for (auto const& test: m_tests)
|
||||
|
||||
if (success && !m_runWithYul && compileViaYul)
|
||||
{
|
||||
ErrorReporter errorReporter;
|
||||
_stream << test.format(errorReporter, _linePrefix, true, _formatted) << endl;
|
||||
_stream << errorReporter.format(_linePrefix, _formatted);
|
||||
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;
|
||||
}
|
||||
AnsiColorized(_stream, _formatted, {BOLD, RED}) << _linePrefix << endl << _linePrefix
|
||||
<< "Attention: Updates on the test will apply the detected format displayed." << endl;
|
||||
if (compileViaYul && m_runWithoutYul)
|
||||
|
||||
if (!success && (m_runWithYul || !compileViaYul))
|
||||
{
|
||||
_stream << _linePrefix << endl << _linePrefix;
|
||||
AnsiColorized(_stream, _formatted, {RED_BACKGROUND})
|
||||
<< "Note that the test passed without Yul.";
|
||||
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);
|
||||
}
|
||||
_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
|
||||
throw;
|
||||
}
|
||||
catch (YulException const&)
|
||||
{
|
||||
// this should be an error in yul compilation or translation
|
||||
throw;
|
||||
}
|
||||
catch (boost::exception const&)
|
||||
{
|
||||
if (compileViaYul && !m_runWithYul)
|
||||
continue;
|
||||
throw;
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
if (compileViaYul && !m_runWithYul)
|
||||
continue;
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (compileViaYul && !m_runWithYul)
|
||||
continue;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
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)
|
||||
continue;
|
||||
throw;
|
||||
}
|
||||
catch (std::exception const&)
|
||||
{
|
||||
if (compileViaYul && !m_runWithYul)
|
||||
continue;
|
||||
throw;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (compileViaYul && !m_runWithYul)
|
||||
continue;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return TestResult::Success;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -53,6 +53,13 @@ using namespace solidity::langutil;
|
||||
reset(); \
|
||||
m_compileViaYul = true; \
|
||||
{ CODE } \
|
||||
if (supportsEwasm()) \
|
||||
{ \
|
||||
reset(true); \
|
||||
m_compileViaYul = true; \
|
||||
m_compileToEwasm = true; \
|
||||
{ CODE } \
|
||||
} \
|
||||
}
|
||||
|
||||
namespace solidity::frontend::test
|
||||
@ -1015,12 +1022,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));
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@ bytes SolidityExecutionFramework::compileContract(
|
||||
sourceCode += "pragma experimental ABIEncoderV2;\n";
|
||||
sourceCode += _sourceCode;
|
||||
m_compiler.reset();
|
||||
m_compiler.enableEwasmGeneration(m_compileToEwasm);
|
||||
m_compiler.setSources({{"", sourceCode}});
|
||||
m_compiler.setLibraries(_libraryAddresses);
|
||||
m_compiler.setRevertStringBehaviour(m_revertStrings);
|
||||
@ -65,18 +66,21 @@ bytes SolidityExecutionFramework::compileContract(
|
||||
evmasm::LinkerObject obj;
|
||||
if (m_compileViaYul)
|
||||
{
|
||||
yul::AssemblyStack asmStack(
|
||||
m_evmVersion,
|
||||
yul::AssemblyStack::Language::StrictAssembly,
|
||||
// Ignore optimiser settings here because we need Yul optimisation to
|
||||
// get code that does not exhaust the stack.
|
||||
OptimiserSettings::full()
|
||||
);
|
||||
bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName));
|
||||
solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors");
|
||||
|
||||
asmStack.optimize();
|
||||
obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode);
|
||||
if (m_compileToEwasm)
|
||||
obj = m_compiler.ewasmObject(contractName);
|
||||
else
|
||||
{
|
||||
yul::AssemblyStack asmStack(
|
||||
m_evmVersion,
|
||||
yul::AssemblyStack::Language::StrictAssembly,
|
||||
// Ignore optimiser settings here because we need Yul optimisation to
|
||||
// get code that does not exhaust the stack.
|
||||
OptimiserSettings::full());
|
||||
bool analysisSuccessful = asmStack.parseAndAnalyze("", m_compiler.yulIROptimized(contractName));
|
||||
solAssert(analysisSuccessful, "Code that passed analysis in CompilerStack can't have errors");
|
||||
asmStack.optimize();
|
||||
obj = std::move(*asmStack.assemble(yul::AssemblyStack::Machine::EVM).bytecode);
|
||||
}
|
||||
}
|
||||
else
|
||||
obj = m_compiler.object(contractName);
|
||||
|
||||
@ -42,8 +42,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(
|
||||
@ -66,11 +66,12 @@ public:
|
||||
);
|
||||
|
||||
protected:
|
||||
|
||||
solidity::frontend::CompilerStack m_compiler;
|
||||
bool m_compileViaYul = false;
|
||||
bool m_compileToEwasm = false;
|
||||
bool m_showMetadata = false;
|
||||
RevertStrings m_revertStrings = RevertStrings::Default;
|
||||
|
||||
};
|
||||
|
||||
} // end namespaces
|
||||
|
||||
@ -164,6 +164,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())
|
||||
@ -427,7 +428,7 @@ 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());
|
||||
bool disableSemantics = !solidity::test::EVMHost::checkVmPaths(options.vmPaths);
|
||||
if (disableSemantics)
|
||||
{
|
||||
cout << "Unable to find " << solidity::test::evmoneFilename << ". Please provide the path using --evmonepath <path>." << endl;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user