diff --git a/TestHelper.cpp b/TestHelper.cpp index dede47038..aada83041 100644 --- a/TestHelper.cpp +++ b/TestHelper.cpp @@ -549,58 +549,53 @@ void checkCallCreates(eth::Transactions _resultCallCreates, eth::Transactions _e } } -void userDefinedTest(string testTypeFlag, std::function doTests) +void userDefinedTest(std::function 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]; - if (arg == testTypeFlag) - { - if (boost::unit_test::framework::master_test_suite().argc <= i + 2) - { - cnote << "Missing filename\nUsage: testeth " << testTypeFlag << " \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; - } + cnote << "Missing user test specification\nUsage: testeth --singletest \n"; + return; } + + 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 doTests) @@ -731,6 +726,8 @@ Options::Options() bigData = true; else if (arg == "--checkstate") checkState = true; + else if (arg == "--wallet") + wallet = true; else if (arg == "--all") { performance = true; @@ -738,11 +735,25 @@ Options::Options() memory = true; inputLimits = true; bigData = true; + wallet= true; } else if (arg == "--singletest" && i + 1 < argc) { 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); } } } diff --git a/TestHelper.h b/TestHelper.h index 00b520d06..fc6c77fad 100644 --- a/TestHelper.h +++ b/TestHelper.h @@ -157,7 +157,7 @@ void checkLog(eth::LogEntries _resultLogs, eth::LogEntries _expectedLogs); 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 doTests); -void userDefinedTest(std::string testTypeFlag, std::function doTests); +void userDefinedTest(std::function doTests); RLPStream createRLPStreamFromTransactionFields(json_spirit::mObject& _tObj); eth::LastHashes lastHashes(u256 _currentBlockNumber); json_spirit::mObject fillJsonWithState(eth::State _state); @@ -188,12 +188,14 @@ public: /// Test selection /// @{ bool singleTest = false; + std::string singleTestFile; std::string singleTestName; bool performance = false; bool quadratic = false; bool memory = false; bool inputLimits = false; bool bigData = false; + bool wallet = false; /// @} /// Get reference to options diff --git a/libsolidity/SolidityCompiler.cpp b/libsolidity/SolidityCompiler.cpp index aa83c4650..dda7847ed 100644 --- a/libsolidity/SolidityCompiler.cpp +++ b/libsolidity/SolidityCompiler.cpp @@ -116,36 +116,35 @@ BOOST_AUTO_TEST_CASE(ifStatement) bytes code = compileContract(sourceCode); unsigned shift = 60; unsigned boilerplateSize = 73; - bytes expectation({byte(Instruction::JUMPDEST), - byte(Instruction::PUSH1), 0x0, - byte(Instruction::DUP1), - byte(Instruction::PUSH1), byte(0x1b + shift), // "true" target - byte(Instruction::JUMPI), - // new check "else if" condition - byte(Instruction::DUP1), - byte(Instruction::ISZERO), - byte(Instruction::PUSH1), byte(0x13 + shift), - byte(Instruction::JUMPI), - // "else" body - byte(Instruction::PUSH1), 0x4f, - byte(Instruction::POP), - byte(Instruction::PUSH1), byte(0x17 + shift), // exit path of second part - byte(Instruction::JUMP), - // "else if" body - byte(Instruction::JUMPDEST), - byte(Instruction::PUSH1), 0x4e, - byte(Instruction::POP), - byte(Instruction::JUMPDEST), - byte(Instruction::PUSH1), byte(0x1f + shift), - byte(Instruction::JUMP), - // "if" body - byte(Instruction::JUMPDEST), - byte(Instruction::PUSH1), 0x4d, - byte(Instruction::POP), - byte(Instruction::JUMPDEST), - byte(Instruction::JUMPDEST), - byte(Instruction::POP), - byte(Instruction::JUMP)}); + bytes expectation({ + byte(Instruction::JUMPDEST), + byte(Instruction::PUSH1), 0x0, + byte(Instruction::DUP1), + byte(Instruction::ISZERO), + byte(Instruction::PUSH1), byte(0x0f + shift), // "false" target + byte(Instruction::JUMPI), + // "if" body + byte(Instruction::PUSH1), 0x4d, + byte(Instruction::POP), + byte(Instruction::PUSH1), byte(0x21 + shift), + byte(Instruction::JUMP), + // new check "else if" condition + byte(Instruction::JUMPDEST), + byte(Instruction::DUP1), + byte(Instruction::ISZERO), + byte(Instruction::ISZERO), + byte(Instruction::PUSH1), byte(0x1c + shift), + byte(Instruction::JUMPI), + // "else if" body + byte(Instruction::PUSH1), 0x4e, + byte(Instruction::POP), + byte(Instruction::PUSH1), byte(0x20 + shift), + byte(Instruction::JUMP), + // "else" body + byte(Instruction::JUMPDEST), + byte(Instruction::PUSH1), 0x4f, + byte(Instruction::POP), + }); checkCodePresentAt(code, expectation, boilerplateSize); } diff --git a/libsolidity/SolidityEndToEndTest.cpp b/libsolidity/SolidityEndToEndTest.cpp index ed5f1acdf..90ce20d26 100644 --- a/libsolidity/SolidityEndToEndTest.cpp +++ b/libsolidity/SolidityEndToEndTest.cpp @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include using namespace std; @@ -1501,9 +1501,7 @@ BOOST_AUTO_TEST_CASE(sha256) compileAndRun(sourceCode); auto f = [&](u256 const& _input) -> u256 { - h256 ret; - dev::sha256(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32)); - return ret; + return dev::sha256(dev::ref(toBigEndian(_input))); }; testSolidityAgainstCpp("a(bytes32)", f, u256(4)); testSolidityAgainstCpp("a(bytes32)", f, u256(5)); @@ -1520,9 +1518,7 @@ BOOST_AUTO_TEST_CASE(ripemd) compileAndRun(sourceCode); auto f = [&](u256 const& _input) -> u256 { - h256 ret; - dev::ripemd160(dev::ref(toBigEndian(_input)), bytesRef(&ret[0], 32)); - return u256(ret); + return h256(dev::ripemd160(h256(_input).ref()), h256::AlignLeft); // This should be aligned right. i guess it's fixed elsewhere? }; testSolidityAgainstCpp("a(bytes32)", f, u256(4)); 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_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) { // this test just checks that the copy loop does not mess up the stack diff --git a/libsolidity/SolidityNameAndTypeResolution.cpp b/libsolidity/SolidityNameAndTypeResolution.cpp index 4ec7b8bda..c52bbf9de 100644 --- a/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/libsolidity/SolidityNameAndTypeResolution.cpp @@ -23,7 +23,7 @@ #include #include -#include +#include #include #include #include diff --git a/libsolidity/SolidityOptimizer.cpp b/libsolidity/SolidityOptimizer.cpp index 3cb6a536a..744fc48ae 100644 --- a/libsolidity/SolidityOptimizer.cpp +++ b/libsolidity/SolidityOptimizer.cpp @@ -29,6 +29,7 @@ #include #include #include +#include using namespace std; using namespace dev::eth; @@ -96,7 +97,7 @@ public: { eth::KnownState state; for (auto const& item: addDummyLocations(_input)) - state.feedItem(item); + state.feedItem(item, true); return state; } @@ -125,7 +126,7 @@ public: 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; // Running it four times should be enough for these tests. @@ -138,6 +139,12 @@ public: back_inserter(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()); } @@ -251,6 +258,106 @@ BOOST_AUTO_TEST_CASE(function_calls) 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) { eth::KnownState state; @@ -868,6 +975,35 @@ BOOST_AUTO_TEST_CASE(control_flow_graph_do_not_remove_returned_to) 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 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() }