Merge remote-tracking branch 'upstream/develop' into feature/vm_gas_counter_refactor

Conflicts:
	libethereum/ExtVM.cpp
	libevm/SmartVM.h
	libevm/VM.cpp
	libevm/VM.h
	libevm/VMFace.h
This commit is contained in:
Paweł Bylica 2015-05-20 17:34:09 +02:00
commit b85345cc91
6 changed files with 265 additions and 90 deletions

View File

@ -549,58 +549,53 @@ void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _e
} }
} }
void userDefinedTest(string testTypeFlag, std::function<void(json_spirit::mValue&, bool)> doTests) void userDefinedTest(std::function<void(json_spirit::mValue&, bool)> doTests)
{ {
Options::get(); // parse command line options, e.g. to enable JIT if (!Options::get().singleTest)
return;
for (int i = 1; i < boost::unit_test::framework::master_test_suite().argc; ++i) if (Options::get().singleTestFile.empty() || Options::get().singleTestName.empty())
{ {
string arg = boost::unit_test::framework::master_test_suite().argv[i]; cnote << "Missing user test specification\nUsage: testeth --singletest <filename> <testname>\n";
if (arg == testTypeFlag) return;
{
if (boost::unit_test::framework::master_test_suite().argc <= i + 2)
{
cnote << "Missing filename\nUsage: testeth " << testTypeFlag << " <filename> <testname>\n";
return;
}
string filename = boost::unit_test::framework::master_test_suite().argv[i + 1];
string testname = boost::unit_test::framework::master_test_suite().argv[i + 2];
int currentVerbosity = g_logVerbosity;
g_logVerbosity = 12;
try
{
cnote << "Testing user defined test: " << filename;
json_spirit::mValue v;
string s = asString(contents(filename));
BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + filename + " is empty. ");
json_spirit::read_string(s, v);
json_spirit::mObject oSingleTest;
json_spirit::mObject::const_iterator pos = v.get_obj().find(testname);
if (pos == v.get_obj().end())
{
cnote << "Could not find test: " << testname << " in " << filename << "\n";
return;
}
else
oSingleTest[pos->first] = pos->second;
json_spirit::mValue v_singleTest(oSingleTest);
doTests(v_singleTest, false);
}
catch (Exception const& _e)
{
BOOST_ERROR("Failed Test with Exception: " << diagnostic_information(_e));
g_logVerbosity = currentVerbosity;
}
catch (std::exception const& _e)
{
BOOST_ERROR("Failed Test with Exception: " << _e.what());
g_logVerbosity = currentVerbosity;
}
g_logVerbosity = currentVerbosity;
}
} }
auto& filename = Options::get().singleTestFile;
auto& testname = Options::get().singleTestName;
int currentVerbosity = g_logVerbosity;
g_logVerbosity = 12;
try
{
cnote << "Testing user defined test: " << filename;
json_spirit::mValue v;
string s = asString(contents(filename));
BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of " + filename + " is empty. ");
json_spirit::read_string(s, v);
json_spirit::mObject oSingleTest;
json_spirit::mObject::const_iterator pos = v.get_obj().find(testname);
if (pos == v.get_obj().end())
{
cnote << "Could not find test: " << testname << " in " << filename << "\n";
return;
}
else
oSingleTest[pos->first] = pos->second;
json_spirit::mValue v_singleTest(oSingleTest);
doTests(v_singleTest, false);
}
catch (Exception const& _e)
{
BOOST_ERROR("Failed Test with Exception: " << diagnostic_information(_e));
g_logVerbosity = currentVerbosity;
}
catch (std::exception const& _e)
{
BOOST_ERROR("Failed Test with Exception: " << _e.what());
g_logVerbosity = currentVerbosity;
}
g_logVerbosity = currentVerbosity;
} }
void executeTests(const string& _name, const string& _testPathAppendix, const boost::filesystem::path _pathToFiller, std::function<void(json_spirit::mValue&, bool)> doTests) void executeTests(const string& _name, const string& _testPathAppendix, const boost::filesystem::path _pathToFiller, std::function<void(json_spirit::mValue&, bool)> doTests)
@ -731,6 +726,8 @@ Options::Options()
bigData = true; bigData = true;
else if (arg == "--checkstate") else if (arg == "--checkstate")
checkState = true; checkState = true;
else if (arg == "--wallet")
wallet = true;
else if (arg == "--all") else if (arg == "--all")
{ {
performance = true; performance = true;
@ -738,11 +735,25 @@ Options::Options()
memory = true; memory = true;
inputLimits = true; inputLimits = true;
bigData = true; bigData = true;
wallet= true;
} }
else if (arg == "--singletest" && i + 1 < argc) else if (arg == "--singletest" && i + 1 < argc)
{ {
singleTest = true; singleTest = true;
singleTestName = argv[i + 1]; auto name1 = std::string{argv[i + 1]};
if (i + 1 < argc) // two params
{
auto name2 = std::string{argv[i + 2]};
if (name2[0] == '-') // not param, another option
singleTestName = std::move(name1);
else
{
singleTestFile = std::move(name1);
singleTestName = std::move(name2);
}
}
else
singleTestName = std::move(name1);
} }
} }
} }

View File

@ -157,7 +157,7 @@ void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs);
void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _expectedCallCreates); void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _expectedCallCreates);
void executeTests(const std::string& _name, const std::string& _testPathAppendix, const boost::filesystem::path _pathToFiller, std::function<void(json_spirit::mValue&, bool)> doTests); void executeTests(const std::string& _name, const std::string& _testPathAppendix, const boost::filesystem::path _pathToFiller, std::function<void(json_spirit::mValue&, bool)> doTests);
void userDefinedTest(std::string testTypeFlag, std::function<void(json_spirit::mValue&, bool)> doTests); void userDefinedTest(std::function<void(json_spirit::mValue&, bool)> doTests);
RLPStream createRLPStreamFromTransactionFields(json_spirit::mObject& _tObj); RLPStream createRLPStreamFromTransactionFields(json_spirit::mObject& _tObj);
eth::LastHashes lastHashes(u256 _currentBlockNumber); eth::LastHashes lastHashes(u256 _currentBlockNumber);
json_spirit::mObject fillJsonWithState(eth::State _state); json_spirit::mObject fillJsonWithState(eth::State _state);
@ -188,12 +188,14 @@ public:
/// Test selection /// Test selection
/// @{ /// @{
bool singleTest = false; bool singleTest = false;
std::string singleTestFile;
std::string singleTestName; std::string singleTestName;
bool performance = false; bool performance = false;
bool quadratic = false; bool quadratic = false;
bool memory = false; bool memory = false;
bool inputLimits = false; bool inputLimits = false;
bool bigData = false; bool bigData = false;
bool wallet = false;
/// @} /// @}
/// Get reference to options /// Get reference to options

View File

@ -116,36 +116,35 @@ BOOST_AUTO_TEST_CASE(ifStatement)
bytes code = compileContract(sourceCode); bytes code = compileContract(sourceCode);
unsigned shift = 60; unsigned shift = 60;
unsigned boilerplateSize = 73; unsigned boilerplateSize = 73;
bytes expectation({byte(Instruction::JUMPDEST), bytes expectation({
byte(Instruction::PUSH1), 0x0, byte(Instruction::JUMPDEST),
byte(Instruction::DUP1), byte(Instruction::PUSH1), 0x0,
byte(Instruction::PUSH1), byte(0x1b + shift), // "true" target byte(Instruction::DUP1),
byte(Instruction::JUMPI), byte(Instruction::ISZERO),
// new check "else if" condition byte(Instruction::PUSH1), byte(0x0f + shift), // "false" target
byte(Instruction::DUP1), byte(Instruction::JUMPI),
byte(Instruction::ISZERO), // "if" body
byte(Instruction::PUSH1), byte(0x13 + shift), byte(Instruction::PUSH1), 0x4d,
byte(Instruction::JUMPI), byte(Instruction::POP),
// "else" body byte(Instruction::PUSH1), byte(0x21 + shift),
byte(Instruction::PUSH1), 0x4f, byte(Instruction::JUMP),
byte(Instruction::POP), // new check "else if" condition
byte(Instruction::PUSH1), byte(0x17 + shift), // exit path of second part byte(Instruction::JUMPDEST),
byte(Instruction::JUMP), byte(Instruction::DUP1),
// "else if" body byte(Instruction::ISZERO),
byte(Instruction::JUMPDEST), byte(Instruction::ISZERO),
byte(Instruction::PUSH1), 0x4e, byte(Instruction::PUSH1), byte(0x1c + shift),
byte(Instruction::POP), byte(Instruction::JUMPI),
byte(Instruction::JUMPDEST), // "else if" body
byte(Instruction::PUSH1), byte(0x1f + shift), byte(Instruction::PUSH1), 0x4e,
byte(Instruction::JUMP), byte(Instruction::POP),
// "if" body byte(Instruction::PUSH1), byte(0x20 + shift),
byte(Instruction::JUMPDEST), byte(Instruction::JUMP),
byte(Instruction::PUSH1), 0x4d, // "else" body
byte(Instruction::POP), byte(Instruction::JUMPDEST),
byte(Instruction::JUMPDEST), byte(Instruction::PUSH1), 0x4f,
byte(Instruction::JUMPDEST), byte(Instruction::POP),
byte(Instruction::POP), });
byte(Instruction::JUMP)});
checkCodePresentAt(code, expectation, boilerplateSize); checkCodePresentAt(code, expectation, boilerplateSize);
} }

View File

@ -24,7 +24,7 @@
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
#include <libdevcrypto/SHA3.h> #include <libdevcore/Hash.h>
#include <test/libsolidity/solidityExecutionFramework.h> #include <test/libsolidity/solidityExecutionFramework.h>
using namespace std; using namespace std;
@ -1501,9 +1501,7 @@ BOOST_AUTO_TEST_CASE(sha256)
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto f = [&](u256 const& _input) -> u256 auto f = [&](u256 const& _input) -> u256
{ {
h256 ret; return dev::sha256(dev::ref(toBigEndian(_input)));
dev::sha256(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32));
return ret;
}; };
testSolidityAgainstCpp("a(bytes32)", f, u256(4)); testSolidityAgainstCpp("a(bytes32)", f, u256(4));
testSolidityAgainstCpp("a(bytes32)", f, u256(5)); testSolidityAgainstCpp("a(bytes32)", f, u256(5));
@ -1520,9 +1518,7 @@ BOOST_AUTO_TEST_CASE(ripemd)
compileAndRun(sourceCode); compileAndRun(sourceCode);
auto f = [&](u256 const& _input) -> u256 auto f = [&](u256 const& _input) -> u256
{ {
h256 ret; return h256(dev::ripemd160(h256(_input).ref()), h256::AlignLeft); // This should be aligned right. i guess it's fixed elsewhere?
dev::ripemd160(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32));
return u256(ret);
}; };
testSolidityAgainstCpp("a(bytes32)", f, u256(4)); testSolidityAgainstCpp("a(bytes32)", f, u256(4));
testSolidityAgainstCpp("a(bytes32)", f, u256(5)); testSolidityAgainstCpp("a(bytes32)", f, u256(5));
@ -2558,6 +2554,37 @@ BOOST_AUTO_TEST_CASE(generic_call)
BOOST_CHECK_EQUAL(m_state.balance(m_contractAddress), 50 - 2); BOOST_CHECK_EQUAL(m_state.balance(m_contractAddress), 50 - 2);
} }
BOOST_AUTO_TEST_CASE(generic_callcode)
{
char const* sourceCode = R"**(
contract receiver {
uint public received;
function receive(uint256 x) { received = x; }
}
contract sender {
uint public received;
function doSend(address rec) returns (uint d)
{
bytes4 signature = bytes4(bytes32(sha3("receive(uint256)")));
rec.callcode.value(2)(signature, 23);
return receiver(rec).received();
}
}
)**";
compileAndRun(sourceCode, 0, "receiver");
u160 const c_receiverAddress = m_contractAddress;
compileAndRun(sourceCode, 50, "sender");
u160 const c_senderAddress = m_contractAddress;
BOOST_CHECK(callContractFunction("doSend(address)", c_receiverAddress) == encodeArgs(0));
BOOST_CHECK(callContractFunction("received()") == encodeArgs(23));
m_contractAddress = c_receiverAddress;
BOOST_CHECK(callContractFunction("received()") == encodeArgs(0));
BOOST_CHECK(m_state.storage(c_receiverAddress).empty());
BOOST_CHECK(!m_state.storage(c_senderAddress).empty());
BOOST_CHECK_EQUAL(m_state.balance(c_receiverAddress), 0);
BOOST_CHECK_EQUAL(m_state.balance(c_senderAddress), 50);
}
BOOST_AUTO_TEST_CASE(store_bytes) BOOST_AUTO_TEST_CASE(store_bytes)
{ {
// this test just checks that the copy loop does not mess up the stack // this test just checks that the copy loop does not mess up the stack

View File

@ -23,7 +23,7 @@
#include <string> #include <string>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcrypto/SHA3.h> #include <libdevcore/SHA3.h>
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h> #include <libsolidity/Parser.h>
#include <libsolidity/NameAndTypeResolver.h> #include <libsolidity/NameAndTypeResolver.h>

View File

@ -29,6 +29,7 @@
#include <libevmasm/CommonSubexpressionEliminator.h> #include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/ControlFlowGraph.h> #include <libevmasm/ControlFlowGraph.h>
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
#include <libevmasm/BlockDeduplicator.h>
using namespace std; using namespace std;
using namespace dev::eth; using namespace dev::eth;
@ -96,7 +97,7 @@ public:
{ {
eth::KnownState state; eth::KnownState state;
for (auto const& item: addDummyLocations(_input)) for (auto const& item: addDummyLocations(_input))
state.feedItem(item); state.feedItem(item, true);
return state; return state;
} }
@ -125,7 +126,7 @@ public:
BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end()); BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
} }
void checkCFG(AssemblyItems const& _input, AssemblyItems const& _expectation) AssemblyItems getCFG(AssemblyItems const& _input)
{ {
AssemblyItems output = _input; AssemblyItems output = _input;
// Running it four times should be enough for these tests. // Running it four times should be enough for these tests.
@ -138,6 +139,12 @@ public:
back_inserter(optItems)); back_inserter(optItems));
output = move(optItems); output = move(optItems);
} }
return output;
}
void checkCFG(AssemblyItems const& _input, AssemblyItems const& _expectation)
{
AssemblyItems output = getCFG(_input);
BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end()); BOOST_CHECK_EQUAL_COLLECTIONS(_expectation.begin(), _expectation.end(), output.begin(), output.end());
} }
@ -251,6 +258,106 @@ BOOST_AUTO_TEST_CASE(function_calls)
compareVersions("f(uint256)", 36); compareVersions("f(uint256)", 36);
} }
BOOST_AUTO_TEST_CASE(storage_write_in_loops)
{
char const* sourceCode = R"(
contract test {
uint d;
function f(uint a) returns (uint r) {
var x = d;
for (uint i = 1; i < a * a; i++) {
r = d;
d = i;
}
}
}
)";
compileBothVersions(sourceCode);
compareVersions("f(uint256)", 0);
compareVersions("f(uint256)", 10);
compareVersions("f(uint256)", 36);
}
BOOST_AUTO_TEST_CASE(retain_information_in_branches)
{
// This tests that the optimizer knows that we already have "z == sha3(y)" inside both branches.
char const* sourceCode = R"(
contract c {
bytes32 d;
uint a;
function f(uint x, bytes32 y) returns (uint r_a, bytes32 r_d) {
bytes32 z = sha3(y);
if (x > 8) {
z = sha3(y);
a = x;
} else {
z = sha3(y);
a = x;
}
r_a = a;
r_d = d;
}
}
)";
compileBothVersions(sourceCode);
compareVersions("f(uint256,bytes32)", 0, "abc");
compareVersions("f(uint256,bytes32)", 8, "def");
compareVersions("f(uint256,bytes32)", 10, "ghi");
m_optimize = true;
bytes optimizedBytecode = compileAndRun(sourceCode, 0, "c");
size_t numSHA3s = 0;
eth::eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) {
if (_instr == eth::Instruction::SHA3)
numSHA3s++;
});
BOOST_CHECK_EQUAL(1, numSHA3s);
}
BOOST_AUTO_TEST_CASE(store_tags_as_unions)
{
// This calls the same function from two sources and both calls have a certain sha3 on
// the stack at the same position.
// Without storing tags as unions, the return from the shared function would not know where to
// jump and thus all jumpdests are forced to clear their state and we do not know about the
// sha3 anymore.
// Note that, for now, this only works if the functions have the same number of return
// parameters since otherwise, the return jump addresses are at different stack positions
// which triggers the "unknown jump target" situation.
char const* sourceCode = R"(
contract test {
bytes32 data;
function f(uint x, bytes32 y) external returns (uint r_a, bytes32 r_d) {
r_d = sha3(y);
shared(y);
r_d = sha3(y);
r_a = 5;
}
function g(uint x, bytes32 y) external returns (uint r_a, bytes32 r_d) {
r_d = sha3(y);
shared(y);
r_d = bytes32(uint(sha3(y)) + 2);
r_a = 7;
}
function shared(bytes32 y) internal {
data = sha3(y);
}
}
)";
compileBothVersions(sourceCode);
compareVersions("f()", 7, "abc");
m_optimize = true;
bytes optimizedBytecode = compileAndRun(sourceCode, 0, "test");
size_t numSHA3s = 0;
eth::eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) {
if (_instr == eth::Instruction::SHA3)
numSHA3s++;
});
BOOST_CHECK_EQUAL(2, numSHA3s);
}
BOOST_AUTO_TEST_CASE(cse_intermediate_swap) BOOST_AUTO_TEST_CASE(cse_intermediate_swap)
{ {
eth::KnownState state; eth::KnownState state;
@ -868,6 +975,35 @@ BOOST_AUTO_TEST_CASE(control_flow_graph_do_not_remove_returned_to)
checkCFG(input, {u256(2)}); checkCFG(input, {u256(2)});
} }
BOOST_AUTO_TEST_CASE(block_deduplicator)
{
AssemblyItems input{
AssemblyItem(PushTag, 2),
AssemblyItem(PushTag, 1),
AssemblyItem(PushTag, 3),
u256(6),
eth::Instruction::SWAP3,
eth::Instruction::JUMP,
AssemblyItem(Tag, 1),
u256(6),
eth::Instruction::SWAP3,
eth::Instruction::JUMP,
AssemblyItem(Tag, 2),
u256(6),
eth::Instruction::SWAP3,
eth::Instruction::JUMP,
AssemblyItem(Tag, 3)
};
BlockDeduplicator dedup(input);
dedup.deduplicate();
set<u256> pushTags;
for (AssemblyItem const& item: input)
if (item.type() == PushTag)
pushTags.insert(item.data());
BOOST_CHECK_EQUAL(pushTags.size(), 2);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} }