diff options
Diffstat (limited to 'test')
28 files changed, 2660 insertions, 329 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 01a9a188..8e7b8916 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,8 +4,10 @@ aux_source_directory(. SRC_LIST) aux_source_directory(libdevcore SRC_LIST) aux_source_directory(libevmasm SRC_LIST) aux_source_directory(libsolidity SRC_LIST) +aux_source_directory(libjulia SRC_LIST) aux_source_directory(contracts SRC_LIST) aux_source_directory(liblll SRC_LIST) +aux_source_directory(libjulia SRC_LIST) list(REMOVE_ITEM SRC_LIST "./fuzzer.cpp") @@ -18,7 +20,7 @@ eth_simple_add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) eth_use(${EXECUTABLE} REQUIRED Solidity::solidity Solidity::lll) include_directories(BEFORE ..) -target_link_libraries(${EXECUTABLE} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) +target_link_libraries(${EXECUTABLE} soljson ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) add_executable(solfuzzer fuzzer.cpp) target_link_libraries(solfuzzer soljson ${Boost_PROGRAM_OPTIONS_LIBRARIES}) diff --git a/test/Metadata.cpp b/test/Metadata.cpp new file mode 100644 index 00000000..03f905b1 --- /dev/null +++ b/test/Metadata.cpp @@ -0,0 +1,80 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2017 + * Metadata processing helpers. + */ + +#include <string> +#include <iostream> +#include <regex> +#include <libdevcore/JSON.h> + +using namespace std; + +namespace dev +{ +namespace test +{ + +string bytecodeSansMetadata(string const& _bytecode) +{ + /// The metadata hash takes up 43 bytes (or 86 characters in hex) + /// /a165627a7a72305820([0-9a-f]{64})0029$/ + + if (_bytecode.size() < 88) + return _bytecode; + + if (_bytecode.substr(_bytecode.size() - 4, 4) != "0029") + return _bytecode; + + if (_bytecode.substr(_bytecode.size() - 86, 18) != "a165627a7a72305820") + return _bytecode; + + return _bytecode.substr(0, _bytecode.size() - 86); +} + +bool isValidMetadata(string const& _metadata) +{ + Json::Value metadata; + if (!Json::Reader().parse(_metadata, metadata, false)) + return false; + + if ( + !metadata.isObject() || + !metadata.isMember("version") || + !metadata.isMember("language") || + !metadata.isMember("compiler") || + !metadata.isMember("settings") || + !metadata.isMember("sources") || + !metadata.isMember("output") + ) + return false; + + if (!metadata["version"].isNumeric() || metadata["version"] != 1) + return false; + + if (!metadata["language"].isString() || metadata["language"].asString() != "Solidity") + return false; + + /// @TODO add more strict checks + + return true; +} + +} +} // end namespaces diff --git a/test/Metadata.h b/test/Metadata.h new file mode 100644 index 00000000..cd92ecd8 --- /dev/null +++ b/test/Metadata.h @@ -0,0 +1,37 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2017 + * Metadata processing helpers. + */ + +#include <string> + +namespace dev +{ +namespace test +{ + +/// Returns the bytecode with the metadata hash stripped out. +std::string bytecodeSansMetadata(std::string const& _bytecode); + +/// Expects a serialised metadata JSON and returns true if the +/// content is valid metadata. +bool isValidMetadata(std::string const& _metadata); + +} +} // end namespaces diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp index 3ea3b1fe..c4fbfefb 100644 --- a/test/RPCSession.cpp +++ b/test/RPCSession.cpp @@ -316,9 +316,9 @@ Json::Value RPCSession::rpcCall(string const& _methodName, vector<string> const& request += "],\"id\":" + to_string(m_rpcSequence) + "}"; ++m_rpcSequence; - // cout << "Request: " << request << endl; + BOOST_TEST_MESSAGE("Request: " + request); string reply = m_ipcSocket.sendRequest(request); - // cout << "Reply: " << reply << endl; + BOOST_TEST_MESSAGE("Reply: " + reply); Json::Value result; BOOST_REQUIRE(Json::Reader().parse(reply, result, false)); diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh index 6dc3f77f..4074ce55 100755 --- a/test/cmdlineTests.sh +++ b/test/cmdlineTests.sh @@ -79,7 +79,14 @@ TMPDIR=$(mktemp -d) "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/test/ for f in *.sol do + set +e "$REPO_ROOT"/build/test/solfuzzer --quiet < "$f" + if [ $? -ne 0 ]; then + echo "Fuzzer failed on:" + cat "$f" + exit 1 + fi + set -e done ) rm -rf "$TMPDIR" diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp index 39c32eb7..d2904b5f 100644 --- a/test/contracts/FixedFeeRegistrar.cpp +++ b/test/contracts/FixedFeeRegistrar.cpp @@ -82,7 +82,7 @@ contract FixedFeeRegistrar is Registrar { } } function disown(string _name, address _refund) onlyrecordowner(_name) { - delete m_recordData[uint(sha3(_name)) / 8]; + delete m_recordData[uint(keccak256(_name)) / 8]; if (!_refund.send(c_fee)) throw; Changed(_name); @@ -118,7 +118,7 @@ contract FixedFeeRegistrar is Registrar { Record[2**253] m_recordData; function m_record(string _name) constant internal returns (Record storage o_record) { - return m_recordData[uint(sha3(_name)) / 8]; + return m_recordData[uint(keccak256(_name)) / 8]; } uint constant c_fee = 69 ether; } diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp index 80f06613..ef345d86 100644 --- a/test/contracts/Wallet.cpp +++ b/test/contracts/Wallet.cpp @@ -128,7 +128,7 @@ contract multiowned { } // Replaces an owner `_from` with another `_to`. - function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external { + function changeOwner(address _from, address _to) onlymanyowners(keccak256(msg.data)) external { if (isOwner(_to)) return; uint ownerIndex = m_ownerIndex[uint(_from)]; if (ownerIndex == 0) return; @@ -140,7 +140,7 @@ contract multiowned { OwnerChanged(_from, _to); } - function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external { + function addOwner(address _owner) onlymanyowners(keccak256(msg.data)) external { if (isOwner(_owner)) return; clearPending(); @@ -154,7 +154,7 @@ contract multiowned { OwnerAdded(_owner); } - function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external { + function removeOwner(address _owner) onlymanyowners(keccak256(msg.data)) external { uint ownerIndex = m_ownerIndex[uint(_owner)]; if (ownerIndex == 0) return; if (m_required > m_numOwners - 1) return; @@ -166,7 +166,7 @@ contract multiowned { OwnerRemoved(_owner); } - function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external { + function changeRequirement(uint _newRequired) onlymanyowners(keccak256(msg.data)) external { if (_newRequired > m_numOwners) return; m_required = _newRequired; clearPending(); @@ -293,11 +293,11 @@ contract daylimit is multiowned { m_lastDay = today(); } // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today. - function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external { + function setDailyLimit(uint _newLimit) onlymanyowners(keccak256(msg.data)) external { m_dailyLimit = _newLimit; } // (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today. - function resetSpentToday() onlymanyowners(sha3(msg.data)) external { + function resetSpentToday() onlymanyowners(keccak256(msg.data)) external { m_spentToday = 0; } @@ -374,7 +374,7 @@ contract Wallet is multisig, multiowned, daylimit { } // destroys the contract sending everything to `_to`. - function kill(address _to) onlymanyowners(sha3(msg.data)) external { + function kill(address _to) onlymanyowners(keccak256(msg.data)) external { selfdestruct(_to); } @@ -398,7 +398,7 @@ contract Wallet is multisig, multiowned, daylimit { return 0; } // determine our operation hash. - _r = sha3(msg.data, block.number); + _r = keccak256(msg.data, block.number); if (!confirm(_r) && m_txs[_r].to == 0) { m_txs[_r].to = _to; m_txs[_r].value = _value; diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp index 053d880f..cf99755f 100644 --- a/test/fuzzer.cpp +++ b/test/fuzzer.cpp @@ -152,7 +152,6 @@ void testCompiler() "Exception during compilation", "Unknown exception during compilation", "Unknown exception while generating contract data output", - "Unknown exception while generating formal method output", "Unknown exception while generating source name output", "Unknown error while generating JSON" }); diff --git a/test/libdevcore/MiniMoustache.cpp b/test/libdevcore/MiniMoustache.cpp new file mode 100644 index 00000000..84149173 --- /dev/null +++ b/test/libdevcore/MiniMoustache.cpp @@ -0,0 +1,127 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Unit tests for the mini moustache class. + */ + +#include <libdevcore/Whiskers.h> + +#include "../TestHelper.h" + +using namespace std; + +namespace dev +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(WhiskersTest) + +BOOST_AUTO_TEST_CASE(no_templates) +{ + string templ = "this text does not contain templates"; + BOOST_CHECK_EQUAL(Whiskers(templ).render(), templ); +} + +BOOST_AUTO_TEST_CASE(basic_replacement) +{ + string templ = "a <b> x <c> -> <d>."; + string result = Whiskers(templ) + ("b", "BE") + ("c", "CE") + ("d", "DE") + .render(); + BOOST_CHECK_EQUAL(result, "a BE x CE -> DE."); +} + +BOOST_AUTO_TEST_CASE(tag_unavailable) +{ + string templ = "<b>"; + Whiskers m(templ); + BOOST_CHECK_THROW(m.render(), WhiskersError); +} + +BOOST_AUTO_TEST_CASE(complicated_replacement) +{ + string templ = "a <b> x <complicated> \n <nes<ted>>."; + string result = Whiskers(templ) + ("b", "BE") + ("complicated", "CO<M>PL") + ("nes<ted", "NEST") + .render(); + BOOST_CHECK_EQUAL(result, "a BE x CO<M>PL \n NEST>."); +} + +BOOST_AUTO_TEST_CASE(non_existing_list) +{ + string templ = "a <#b></b>"; + Whiskers m(templ); + BOOST_CHECK_THROW(m.render(), WhiskersError); +} + +BOOST_AUTO_TEST_CASE(empty_list) +{ + string templ = "a <#b></b>x"; + string result = Whiskers(templ)("b", vector<Whiskers::StringMap>{}).render(); + BOOST_CHECK_EQUAL(result, "a x"); +} + +BOOST_AUTO_TEST_CASE(list) +{ + string templ = "a<#b>( <g> - <h> )</b>x"; + vector<map<string, string>> list(2); + list[0]["g"] = "GE"; + list[0]["h"] = "H"; + list[1]["g"] = "2GE"; + list[1]["h"] = "2H"; + string result = Whiskers(templ)("b", list).render(); + BOOST_CHECK_EQUAL(result, "a( GE - H )( 2GE - 2H )x"); +} + +BOOST_AUTO_TEST_CASE(recursive_list) +{ + // Check that templates resulting from lists are not expanded again + string templ = "a<#b> 1<g>3 </b><x>"; + vector<map<string, string>> list(1); + list[0]["g"] = "<x>"; + string result = Whiskers(templ)("x", "X")("b", list).render(); + BOOST_CHECK_EQUAL(result, "a 1<x>3 X"); +} + +BOOST_AUTO_TEST_CASE(list_can_access_upper) +{ + string templ = "<#b>(<a>)</b>"; + vector<map<string, string>> list(2); + Whiskers m(templ); + string result = m("a", "A")("b", list).render(); + BOOST_CHECK_EQUAL(result, "(A)(A)"); +} + +BOOST_AUTO_TEST_CASE(parameter_collision) +{ + string templ = "a <#b></b>"; + vector<map<string, string>> list(1); + list[0]["a"] = "x"; + Whiskers m(templ); + m("a", "X")("b", list); + BOOST_CHECK_THROW(m.render(), WhiskersError); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} diff --git a/test/libdevcore/UTF8.cpp b/test/libdevcore/UTF8.cpp new file mode 100644 index 00000000..719ada72 --- /dev/null +++ b/test/libdevcore/UTF8.cpp @@ -0,0 +1,216 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * Unit tests for UTF-8 validation. + */ + +#include <libdevcore/CommonData.h> +#include <libdevcore/UTF8.h> + +#include "../TestHelper.h" + +using namespace std; + +namespace dev +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(UTF8) + +namespace { + +bool isValidUTF8(string const& _value) +{ + size_t pos; + return validateUTF8(asString(fromHex(_value)), pos); +} + +bool isInvalidUTF8(string const& _value, size_t _expectedPos) +{ + size_t pos; + if (validateUTF8(asString(fromHex(_value)), pos)) + return false; + if (pos != _expectedPos) + return false; + return true; +} + +} + +BOOST_AUTO_TEST_CASE(valid) +{ + BOOST_CHECK(isValidUTF8("00")); + BOOST_CHECK(isValidUTF8("20")); + BOOST_CHECK(isValidUTF8("7f")); + BOOST_CHECK(isValidUTF8("c281")); + BOOST_CHECK(isValidUTF8("df81")); + BOOST_CHECK(isValidUTF8("e0a081")); + BOOST_CHECK(isValidUTF8("e18081")); + BOOST_CHECK(isValidUTF8("ec8081")); + BOOST_CHECK(isValidUTF8("ed8081")); + BOOST_CHECK(isValidUTF8("ee8081")); + BOOST_CHECK(isValidUTF8("ef8081")); + BOOST_CHECK(isValidUTF8("f0908081")); + BOOST_CHECK(isValidUTF8("f3808081")); + BOOST_CHECK(isValidUTF8("f2808081")); + BOOST_CHECK(isValidUTF8("f3808081")); + BOOST_CHECK(isValidUTF8("f48e8081")); +} + +BOOST_AUTO_TEST_CASE(invalid) +{ + // anything between 0x80 and 0xc0 is disallowed + BOOST_CHECK(isInvalidUTF8("80", 0)); // invalid per table 3.6 + BOOST_CHECK(isInvalidUTF8("a0", 0)); // invalid per table 3.6 + BOOST_CHECK(isInvalidUTF8("c0", 0)); // invalid per table 3.7 + BOOST_CHECK(isInvalidUTF8("c1", 0)); // invalid per table 3.7 + BOOST_CHECK(isInvalidUTF8("c2", 0)); // too short (position is reported as the first byte) + BOOST_CHECK(isInvalidUTF8("e08081", 2)); // e0 must be followed by >= a0 + BOOST_CHECK(isInvalidUTF8("e180", 0)); // too short + BOOST_CHECK(isInvalidUTF8("ec80", 0)); // too short + BOOST_CHECK(isInvalidUTF8("f08f8001", 2)); // f0 must be followed by >= 90 + BOOST_CHECK(isInvalidUTF8("f18080", 0)); // too short + BOOST_CHECK(isInvalidUTF8("f4908081", 2)); // f4 must be followed by < 90 + // anything above 0xf7 is disallowed + BOOST_CHECK(isInvalidUTF8("f8", 0)); // invalid per table 3.7 + BOOST_CHECK(isInvalidUTF8("f9", 0)); // invalid per table 3.7 +} + +BOOST_AUTO_TEST_CASE(corpus) +{ + string source = R"( +κόσμε + +hélló + +Ā ā Ă ă Ą ą + +ƀ Ɓ Ƃ ƃ Ƅ ƅ + +ɐ ɑ ɒ ɓ ɔ ɕ + +ʰ ʱ ʲ ʳ ʴ ʵ + +̀ ́ ̂ ̃ ̄ ̅ + +ϩ Ϫ ϫ Ϭ ϭ Ϯ + +Ё Ђ Ѓ Є Ѕ І + +Ա Բ Գ Դ Ե Զ + + ק ר ש ת װ ױ + +ځ ڂ ڃ ڄ څ چ + +ऑ ऒ ओ औ क ख + +ও ঔ ক খ গ ঘ + +ਘ ਙ ਚ ਛ ਜ ਝ + +ઓ ઔ ક ખ ગ ઘ + +ଗ ଘ ଙ ଚ ଛ ଜ + +ஔ க ங ச ஜ ஞ + +ఎ ఏ ఐ ఒ ఓ ఔ + +ಓ ಔ ಕ ಖ ಗ ಘ + +ഐ ഒ ഓ ഔ ക + +ฒ ณ ด ต ถ ท + +ມ ຢ ຣ ລ ວ ສ + +༄ ༅ ༆ ༇ ༈ ༉ + +Ⴑ Ⴒ Ⴓ Ⴔ Ⴕ Ⴖ + +ᄌ ᄍ ᄎ ᄏ ᄐ + +Ḕ ḕ Ḗ ḗ Ḙ ḙ Ḛ + +ἐ ἑ ἒ ἓ ἔ ἕ + +₠ ₡ ₢ ₣ ₤ ₥ + +⃐ ⃑ ⃒ ⃓ ⃔ ⃕ ⃖ ⃗ ⃘ ⃙ ⃚ + +ℋ ℌ ℍ ℎ ℏ ℐ ℑ + +⅓ ⅔ ⅕ ⅖ ⅗ + +∬ ∭ ∮ ∯ ∰ + +⌖ ⌗ ⌘ ⌙ ⌚ ⌛ + +␀ ␁ ␂ ␃ ␄ ␅ + +⑀ ⑁ ⑂ ⑃ ⑄ + +① ② ③ ④ ⑤ + +╘ ╙ ╚ ╛ ╜ ╝ + +▁ ▂ ▃ ▄ ▅ ▆ + +▤ ▥ ▦ ▧ ▨ + +♔ ♕ ♖ ♗ ♘ ♙ + +✈ ✉ ✌ ✍ ✎ + +ぁ あ ぃ い ぅ + +ァ ア ィ イ ゥ + +ㄅ ㄆ ㄇ ㄈ ㄉ + +ㄱ ㄲ ㄳ ㄴ ㄵ + +㆚ ㆛ ㆜ ㆝ ㆞ + +㈀ ㈁ ㈂ ㈃ ㈄ + +㌀ ㌁ ㌂ ㌃ ㌄ + +乺 乻 乼 乽 乾 + +걺 걻 걼 걽 걾 + +豈 更 車 賈 滑 + +שּׁ שּׂ אַ אָ אּ + +ﮄ ﮅ ﮆ ﮇ ﮈ ﮉ + + ﺵ ﺶ ﺷ ﺸ + +「 」 、 ・ ヲ ァ ィ ゥ + )"; + size_t pos; + BOOST_CHECK(validateUTF8(source, pos)); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp new file mode 100644 index 00000000..fa7c45ed --- /dev/null +++ b/test/libjulia/Parser.cpp @@ -0,0 +1,231 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2017 + * Unit tests for parsing Julia. + */ + +#include "../TestHelper.h" + +#include <test/libsolidity/ErrorCheck.h> + +#include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libsolidity/inlineasm/AsmAnalysisInfo.h> +#include <libsolidity/parsing/Scanner.h> +#include <libsolidity/interface/ErrorReporter.h> + +#include <boost/optional.hpp> +#include <boost/algorithm/string/replace.hpp> + +#include <string> +#include <memory> + +using namespace std; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +namespace +{ + +bool parse(string const& _source, ErrorReporter& errorReporter) +{ + try + { + auto scanner = make_shared<Scanner>(CharStream(_source)); + auto parserResult = assembly::Parser(errorReporter, true).parse(scanner); + if (parserResult) + { + assembly::AsmAnalysisInfo analysisInfo; + return (assembly::AsmAnalyzer(analysisInfo, errorReporter, true)).analyze(*parserResult); + } + } + catch (FatalError const&) + { + BOOST_FAIL("Fatal error leaked."); + } + return false; +} + +boost::optional<Error> parseAndReturnFirstError(string const& _source, bool _allowWarnings = true) +{ + ErrorList errors; + ErrorReporter errorReporter(errors); + if (!parse(_source, errorReporter)) + { + BOOST_REQUIRE_EQUAL(errors.size(), 1); + return *errors.front(); + } + else + { + // If success is true, there might still be an error in the assembly stage. + if (_allowWarnings && Error::containsOnlyWarnings(errors)) + return {}; + else if (!errors.empty()) + { + if (!_allowWarnings) + BOOST_CHECK_EQUAL(errors.size(), 1); + return *errors.front(); + } + } + return {}; +} + +bool successParse(std::string const& _source, bool _allowWarnings = true) +{ + return !parseAndReturnFirstError(_source, _allowWarnings); +} + +Error expectError(std::string const& _source, bool _allowWarnings = false) +{ + + auto error = parseAndReturnFirstError(_source, _allowWarnings); + BOOST_REQUIRE(error); + return *error; +} + +} + +#define CHECK_ERROR(text, typ, substring) \ +do \ +{ \ + Error err = expectError((text), false); \ + BOOST_CHECK(err.type() == (Error::Type::typ)); \ + BOOST_CHECK(searchErrorMessage(err, (substring))); \ +} while(0) + +BOOST_AUTO_TEST_SUITE(JuliaParser) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + BOOST_CHECK(successParse("{ }")); +} + +BOOST_AUTO_TEST_CASE(vardecl) +{ + BOOST_CHECK(successParse("{ let x:u256 := 7:u256 }")); +} + +BOOST_AUTO_TEST_CASE(vardecl_bool) +{ + BOOST_CHECK(successParse("{ let x:bool := true:bool }")); + BOOST_CHECK(successParse("{ let x:bool := false:bool }")); +} + +BOOST_AUTO_TEST_CASE(assignment) +{ + BOOST_CHECK(successParse("{ let x:u256 := 2:u256 let y:u256 := x }")); +} + +BOOST_AUTO_TEST_CASE(vardecl_complex) +{ + BOOST_CHECK(successParse("{ function add(a:u256, b:u256) -> c:u256 {} let y:u256 := 2:u256 let x:u256 := add(7:u256, add(6:u256, y)) }")); +} + +BOOST_AUTO_TEST_CASE(blocks) +{ + BOOST_CHECK(successParse("{ let x:u256 := 7:u256 { let y:u256 := 3:u256 } { let z:u256 := 2:u256 } }")); +} + +BOOST_AUTO_TEST_CASE(function_definitions) +{ + BOOST_CHECK(successParse("{ function f() { } function g(a:u256) -> x:u256 { } }")); +} + +BOOST_AUTO_TEST_CASE(function_definitions_multiple_args) +{ + BOOST_CHECK(successParse("{ function f(a:u256, d:u256) { } function g(a:u256, d:u256) -> x:u256, y:u256 { } }")); +} + +BOOST_AUTO_TEST_CASE(function_calls) +{ + BOOST_CHECK(successParse("{ function f(a:u256) -> b:u256 {} function g(a:u256, b:u256, c:u256) {} function x() { g(1:u256, 2:u256, f(3:u256)) x() } }")); +} + +BOOST_AUTO_TEST_CASE(tuple_assignment) +{ + BOOST_CHECK(successParse("{ function f() -> a:u256, b:u256, c:u256 {} let x:u256, y:u256, z:u256 := f() }")); +} + +BOOST_AUTO_TEST_CASE(label) +{ + CHECK_ERROR("{ label: }", ParserError, "Labels are not supported."); +} + +BOOST_AUTO_TEST_CASE(instructions) +{ + CHECK_ERROR("{ pop }", ParserError, "Call or assignment expected."); +} + +BOOST_AUTO_TEST_CASE(push) +{ + CHECK_ERROR("{ 0x42:u256 }", ParserError, "Call or assignment expected."); +} + +BOOST_AUTO_TEST_CASE(assign_from_stack) +{ + CHECK_ERROR("{ =: x:u256 }", ParserError, "Literal or identifier expected."); +} + +BOOST_AUTO_TEST_CASE(empty_call) +{ + CHECK_ERROR("{ () }", ParserError, "Literal or identifier expected."); +} + +BOOST_AUTO_TEST_CASE(lacking_types) +{ + CHECK_ERROR("{ let x := 1:u256 }", ParserError, "Expected token Identifier got 'Assign'"); + CHECK_ERROR("{ let x:u256 := 1 }", ParserError, "Expected token Colon got 'RBrace'"); + CHECK_ERROR("{ function f(a) {} }", ParserError, "Expected token Colon got 'RParen'"); + CHECK_ERROR("{ function f(a:u256) -> b {} }", ParserError, "Expected token Colon got 'LBrace'"); +} + +BOOST_AUTO_TEST_CASE(invalid_types) +{ + /// testing invalid literal + /// NOTE: these will need to change when types are compared + CHECK_ERROR("{ let x:bool := 1:invalid }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported)."); + /// testing invalid variable declaration + CHECK_ERROR("{ let x:invalid := 1:bool }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported)."); + CHECK_ERROR("{ function f(a:invalid) {} }", TypeError, "\"invalid\" is not a valid type (user defined types are not yet supported)."); +} + +BOOST_AUTO_TEST_CASE(builtin_types) +{ + BOOST_CHECK(successParse("{ let x:bool := true:bool }")); + BOOST_CHECK(successParse("{ let x:u8 := 1:u8 }")); + BOOST_CHECK(successParse("{ let x:s8 := 1:u8 }")); + BOOST_CHECK(successParse("{ let x:u32 := 1:u32 }")); + BOOST_CHECK(successParse("{ let x:s32 := 1:s32 }")); + BOOST_CHECK(successParse("{ let x:u64 := 1:u64 }")); + BOOST_CHECK(successParse("{ let x:s64 := 1:s64 }")); + BOOST_CHECK(successParse("{ let x:u128 := 1:u128 }")); + BOOST_CHECK(successParse("{ let x:s128 := 1:s128 }")); + BOOST_CHECK(successParse("{ let x:u256 := 1:u256 }")); + BOOST_CHECK(successParse("{ let x:s256 := 1:s256 }")); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/liblll/EndToEndTest.cpp b/test/liblll/EndToEndTest.cpp index c7c1fd3b..f3bfb438 100644 --- a/test/liblll/EndToEndTest.cpp +++ b/test/liblll/EndToEndTest.cpp @@ -57,6 +57,114 @@ BOOST_AUTO_TEST_CASE(panic) BOOST_REQUIRE(m_output.empty()); } +BOOST_AUTO_TEST_CASE(macro_zeroarg) +{ + char const* sourceCode = R"( + (returnlll + (seq + (def 'zeroarg () (seq (mstore 0 0x1234) (return 0 32))) + (zeroarg))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(0x1234))); +} + +BOOST_AUTO_TEST_CASE(macros) +{ + char const* sourceCode = R"( + (returnlll + (seq + (def 'x 1) + (def 'y () { (def 'x (+ x 2)) }) + (y) + (return x))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(3))); +} + +BOOST_AUTO_TEST_CASE(variables) +{ + char const* sourceCode = R"( + (returnlll + (seq + (set 'x 1) + (set 'y 2) + ;; this should equal to 3 + (set 'z (add (get 'x) (get 'y))) + ;; overwriting it here + (set 'y 4) + ;; each variable has a 32 byte slot, starting from memory location 0x80 + ;; variable addresses can also be retrieved by x or (ref 'x) + (set 'k (add (add (ref 'x) (ref 'y)) z)) + (return (add (add (get 'x) (add (get 'y) (get 'z))) (get 'k))))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(488))); +} + +BOOST_AUTO_TEST_CASE(when) +{ + char const* sourceCode = R"( + (returnlll + (seq + (when (= (calldatasize) 0) (return 1)) + (when (!= (calldatasize) 0) (return 2)))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(2))); + BOOST_CHECK(callFallback() == toBigEndian(u256(1))); +} + +BOOST_AUTO_TEST_CASE(unless) +{ + char const* sourceCode = R"( + (returnlll + (seq + (unless (!= (calldatasize) 0) (return 1)) + (unless (= (calldatasize) 0) (return 2)))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(2))); + BOOST_CHECK(callFallback() == toBigEndian(u256(1))); +} + +BOOST_AUTO_TEST_CASE(conditional_literal) +{ + char const* sourceCode = R"( + (returnlll + (seq + (return (if (= (calldatasize) 0) 1 2)))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(2))); + BOOST_CHECK(callFallback() == toBigEndian(u256(1))); +} + +BOOST_AUTO_TEST_CASE(conditional) +{ + char const* sourceCode = R"( + (returnlll + (seq + (if (= (calldatasize) 0) (return 1) (return 2)))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(2))); + BOOST_CHECK(callFallback() == toBigEndian(u256(1))); +} + +BOOST_AUTO_TEST_CASE(conditional_seq) +{ + char const* sourceCode = R"( + (returnlll + (seq + (return (if (= (calldatasize) 0) { 0 2 1 } { 0 1 2 })))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(2))); + BOOST_CHECK(callFallback() == toBigEndian(u256(1))); +} + BOOST_AUTO_TEST_CASE(exp_operator_const) { char const* sourceCode = R"( @@ -272,13 +380,329 @@ BOOST_AUTO_TEST_CASE(assembly_codecopy) (seq (lit 0x00 "abcdef") (asm - 0x06 0x16 0x20 codecopy + 0x06 6 codesize sub 0x20 codecopy 0x20 0x20 return))) )"; compileAndRun(sourceCode); BOOST_CHECK(callFallback() == encodeArgs(string("abcdef"))); } + +BOOST_AUTO_TEST_CASE(keccak256_32bytes) +{ + char const* sourceCode = R"( + (returnlll + (seq + (mstore 0x00 0x01) + (return (keccak256 0x00 0x20)))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))); +} + +// The following tests are for the built-in macros. +// Note that panic, returnlll and return_one_arg are well covered above. + +BOOST_AUTO_TEST_CASE(allgas) +{ + char const* sourceCode = R"( + (returnlll + (return (- (gas) allgas))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(16))); // == 21 - SUB - GAS +} + +BOOST_AUTO_TEST_CASE(send_two_args) +{ + char const* sourceCode = R"( + (returnlll + (send 0xdead 42)) + )"; + compileAndRun(sourceCode); + callFallbackWithValue(42); + BOOST_CHECK(balanceAt(Address(0xdead)) == 42); +} + +BOOST_AUTO_TEST_CASE(send_three_args) +{ + char const* sourceCode = R"( + (returnlll + (send allgas 0xdead 42)) + )"; + compileAndRun(sourceCode); + callFallbackWithValue(42); + BOOST_CHECK(balanceAt(Address(0xdead)) == 42); +} + +BOOST_AUTO_TEST_CASE(msg_six_args) +{ + char const* sourceCode = R"( + (returnlll + (seq + (when (= 0 (calldatasize)) + (seq + (mstore 0x40 1) + (def 'outsize 0x20) + (return (msg 1000 (address) 42 0x40 0x20 outsize) outsize))) + (when (= 1 (calldataload 0x00)) + (return (callvalue))))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(msg_five_args) +{ + char const* sourceCode = R"( + (returnlll + (seq + (when (= 0 (calldatasize)) + (seq + (mstore 0x20 1) + (mstore 0x40 2) + (return (msg 1000 (address) 42 0x20 0x40)))) + (when (= 3 (+ (calldataload 0x00) (calldataload 0x20))) + (return (callvalue))))) + + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(msg_four_args) +{ + char const* sourceCode = R"( + (returnlll + (seq + (when (= 0 (calldatasize)) + (return (msg 1000 (address) 42 0xff))) + (return (callvalue)))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(msg_three_args) +{ + char const* sourceCode = R"( + (returnlll + (seq + (when (= 0 (calldatasize)) + (return (msg (address) 42 0xff))) + (return (callvalue)))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(msg_two_args) +{ + char const* sourceCode = R"( + (returnlll + (seq + (when (= 0 (calldatasize)) + (return (msg (address) 0xff))) + (return 42))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(create_one_arg) +{ + char const* sourceCode = R"( + (returnlll + (seq + (call allgas + (create (returnlll (return 42))) + 0 0 0 0x00 0x20) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(create_two_args) +{ + char const* sourceCode = R"( + (returnlll + (seq + (call allgas + (create 42 (returnlll (return (balance (address))))) + 0 0 0 0x00 0x20) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallbackWithValue(42) == encodeArgs(u256(42))); +} + +BOOST_AUTO_TEST_CASE(sha3_two_args) +{ + char const* sourceCode = R"( + (returnlll + (seq + (mstore 0x00 0x01) + (return (sha3 0x00 0x20)))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))); +} + +BOOST_AUTO_TEST_CASE(sha3_one_arg) +{ + char const* sourceCode = R"( + (returnlll + (return (sha3 0x01))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6"))); +} + +BOOST_AUTO_TEST_CASE(sha3pair) +{ + char const* sourceCode = R"( + (returnlll + (return (sha3pair 0x01 0x02))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("0xe90b7bceb6e7df5418fb78d8ee546e97c83a08bbccc01a0644d599ccd2a7c2e0"))); +} + +BOOST_AUTO_TEST_CASE(sha3trip) +{ + char const* sourceCode = R"( + (returnlll + (return (sha3trip 0x01 0x02 0x03))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("0x6e0c627900b24bd432fe7b1f713f1b0744091a646a9fe4a65a18dfed21f2949c"))); +} + +BOOST_AUTO_TEST_CASE(makeperm) // Covers makeperm (implicit), permcount and perm +{ + char const* sourceCode = R"( + (returnlll + (seq + (perm 'x) (x (+ 1 x)) + (perm 'y) (y (+ 10 y)) + (when (= 2 permcount) + (return (+ x y))))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(11))); +} + +BOOST_AUTO_TEST_CASE(ecrecover) +{ + char const* sourceCode = R"( + (returnlll + (return + (ecrecover + ; Hash of 'hello world' + 0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad + ; v = 1 + 27 + 0x1c + ; r + 0xdebaaa0cddb321b2dcaaf846d39605de7b97e77ba6106587855b9106cb104215 + ; s + 0x61a22d94fa8b8a687ff9c911c844d1c016d1a685a9166858f9c7c1bc85128aca))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(fromHex("0x8743523d96a1b2cbe0c6909653a56da18ed484af"))); +} + +BOOST_AUTO_TEST_CASE(sha256_two_args) +{ + char const* sourceCode = R"( + (returnlll + (seq + (lit 0x20 "abcdefghijklmnopqrstuvwxyzABCDEF") + (lit 0x40 "GHIJKLMNOPQRSTUVWXYZ0123456789?!") + (sha256 0x20 0x40) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("0xcf25a9fe3d86ae228c226c81d2d8c64c687cd6dc4586d10d8e7e4e5b6706d429"))); +} + +BOOST_AUTO_TEST_CASE(ripemd160_two_args) +{ + char const* sourceCode = R"( + (returnlll + (seq + (lit 0x20 "abcdefghijklmnopqrstuvwxyzABCDEF") + (lit 0x40 "GHIJKLMNOPQRSTUVWXYZ0123456789?!") + (ripemd160 0x20 0x40) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("0x36c6b90a49e17d4c1e1b0e634ec74124d9b207da"))); +} + +BOOST_AUTO_TEST_CASE(sha256_one_arg) +{ + char const* sourceCode = R"( + (returnlll + (seq + (sha256 0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("0xcfd2f1fad75a1978da0a444883db7251414b139f31f5a04704c291fdb0e175e6"))); +} + +BOOST_AUTO_TEST_CASE(ripemd160_one_arg) +{ + char const* sourceCode = R"( + (returnlll + (seq + (ripemd160 0x6162636465666768696a6b6c6d6e6f707172737475767778797a414243444546) + (return 0x00 0x20))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs( + fromHex("0xac5ab22e07b0fb80c69b6207902f725e2507e546"))); +} + +BOOST_AUTO_TEST_CASE(wei_szabo_finney_ether) +{ + char const* sourceCode = R"( + (returnlll + (return (+ wei (+ szabo (+ finney ether))))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(1001001000000000001))); +} + +BOOST_AUTO_TEST_CASE(shift_left) +{ + char const* sourceCode = R"( + (returnlll + (return (shl 1 8))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(256))); +} + +BOOST_AUTO_TEST_CASE(shift_right) +{ + char const* sourceCode = R"( + (returnlll + (return (shr 65536 8))) + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callFallback() == encodeArgs(u256(256))); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/liblll/Parser.cpp b/test/liblll/Parser.cpp index 0d5d9ea5..fc977b81 100644 --- a/test/liblll/Parser.cpp +++ b/test/liblll/Parser.cpp @@ -171,7 +171,13 @@ BOOST_AUTO_TEST_CASE(list) BOOST_CHECK_EQUAL(parse(text), R"(( 1234 ))"); BOOST_CHECK(successParse("( 1234 5467 )")); - BOOST_CHECK(!successParse("()")); + BOOST_CHECK(successParse("()")); +} + +BOOST_AUTO_TEST_CASE(macro_with_zero_args) +{ + char const* text = "(def 'zeroargs () (asm INVALID))"; + BOOST_CHECK(successParse(text)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/ASTJSON.cpp b/test/libsolidity/ASTJSON.cpp index d23815b4..4fb4f20c 100644 --- a/test/libsolidity/ASTJSON.cpp +++ b/test/libsolidity/ASTJSON.cpp @@ -44,7 +44,7 @@ BOOST_AUTO_TEST_CASE(smoke_test) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); BOOST_CHECK_EQUAL(astJson["name"], "SourceUnit"); } @@ -55,7 +55,7 @@ BOOST_AUTO_TEST_CASE(source_location) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); BOOST_CHECK_EQUAL(astJson["name"], "SourceUnit"); BOOST_CHECK_EQUAL(astJson["children"][0]["name"], "ContractDefinition"); BOOST_CHECK_EQUAL(astJson["children"][0]["children"][0]["name"], "FunctionDefinition"); @@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(inheritance_specifier) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); BOOST_CHECK_EQUAL(astJson["children"][1]["attributes"]["name"], "C2"); BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["name"], "InheritanceSpecifier"); BOOST_CHECK_EQUAL(astJson["children"][1]["children"][0]["src"], "30:2:1"); @@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(using_for_directive) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value usingFor = astJson["children"][1]["children"][0]; BOOST_CHECK_EQUAL(usingFor["name"], "UsingForDirective"); BOOST_CHECK_EQUAL(usingFor["src"], "26:17:1"); @@ -101,7 +101,7 @@ BOOST_AUTO_TEST_CASE(enum_value) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value enumDefinition = astJson["children"][0]["children"][0]; BOOST_CHECK_EQUAL(enumDefinition["children"][0]["name"], "EnumValue"); BOOST_CHECK_EQUAL(enumDefinition["children"][0]["attributes"]["name"], "A"); @@ -118,7 +118,7 @@ BOOST_AUTO_TEST_CASE(modifier_definition) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value modifier = astJson["children"][0]["children"][0]; BOOST_CHECK_EQUAL(modifier["name"], "ModifierDefinition"); BOOST_CHECK_EQUAL(modifier["attributes"]["name"], "M"); @@ -132,7 +132,7 @@ BOOST_AUTO_TEST_CASE(modifier_invocation) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value modifier = astJson["children"][0]["children"][1]["children"][2]; BOOST_CHECK_EQUAL(modifier["name"], "ModifierInvocation"); BOOST_CHECK_EQUAL(modifier["src"], "52:4:1"); @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(event_definition) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value event = astJson["children"][0]["children"][0]; BOOST_CHECK_EQUAL(event["name"], "EventDefinition"); BOOST_CHECK_EQUAL(event["attributes"]["name"], "E"); @@ -162,7 +162,7 @@ BOOST_AUTO_TEST_CASE(array_type_name) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value array = astJson["children"][0]["children"][0]["children"][0]; BOOST_CHECK_EQUAL(array["name"], "ArrayTypeName"); BOOST_CHECK_EQUAL(array["src"], "13:6:1"); @@ -175,7 +175,7 @@ BOOST_AUTO_TEST_CASE(placeholder_statement) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value placeholder = astJson["children"][0]["children"][0]["children"][1]["children"][0]; BOOST_CHECK_EQUAL(placeholder["name"], "PlaceholderStatement"); BOOST_CHECK_EQUAL(placeholder["src"], "26:1:1"); @@ -188,11 +188,11 @@ BOOST_AUTO_TEST_CASE(non_utf8) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value literal = astJson["children"][0]["children"][0]["children"][2]["children"][0]["children"][1]; BOOST_CHECK_EQUAL(literal["name"], "Literal"); BOOST_CHECK_EQUAL(literal["attributes"]["hexvalue"], "ff"); - BOOST_CHECK_EQUAL(literal["attributes"]["token"], Json::nullValue); + BOOST_CHECK_EQUAL(literal["attributes"]["token"], "string"); BOOST_CHECK_EQUAL(literal["attributes"]["value"], Json::nullValue); BOOST_CHECK(literal["attributes"]["type"].asString().find("invalid") != string::npos); } @@ -207,7 +207,7 @@ BOOST_AUTO_TEST_CASE(function_type) c.parseAndAnalyze(); map<string, unsigned> sourceIndices; sourceIndices["a"] = 1; - Json::Value astJson = ASTJsonConverter(c.ast("a"), sourceIndices).json(); + Json::Value astJson = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); Json::Value fun = astJson["children"][0]["children"][0]; BOOST_CHECK_EQUAL(fun["name"], "FunctionDefinition"); Json::Value argument = fun["children"][0]["children"][0]; @@ -228,6 +228,36 @@ BOOST_AUTO_TEST_CASE(function_type) BOOST_CHECK_EQUAL(funType["attributes"]["visibility"], "external"); } +BOOST_AUTO_TEST_CASE(documentation) +{ + CompilerStack c; + c.addSource("a", "/**This contract is empty*/ contract C {}"); + c.addSource("b", + "/**This contract is empty" + " and has a line-breaking comment.*/" + "contract C {}" + ); + c.parseAndAnalyze(); + map<string, unsigned> sourceIndices; + sourceIndices["a"] = 0; + sourceIndices["b"] = 1; + Json::Value astJsonA = ASTJsonConverter(true, sourceIndices).toJson(c.ast("a")); + Json::Value documentationA = astJsonA["children"][0]["attributes"]["documentation"]; + BOOST_CHECK_EQUAL(documentationA, "This contract is empty"); + Json::Value astJsonB = ASTJsonConverter(true, sourceIndices).toJson(c.ast("b")); + Json::Value documentationB = astJsonB["children"][0]["attributes"]["documentation"]; + BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment."); + //same tests for non-legacy mode + astJsonA = ASTJsonConverter(false, sourceIndices).toJson(c.ast("a")); + documentationA = astJsonA["nodes"][0]["documentation"]; + BOOST_CHECK_EQUAL(documentationA, "This contract is empty"); + astJsonB = ASTJsonConverter(false, sourceIndices).toJson(c.ast("b")); + documentationB = astJsonB["nodes"][0]["documentation"]; + BOOST_CHECK_EQUAL(documentationB, "This contract is empty and has a line-breaking comment."); + +} + + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index c4ec0d20..99a2996e 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -31,6 +31,7 @@ #include <libsolidity/codegen/Compiler.h> #include <libsolidity/ast/AST.h> #include <libsolidity/analysis/TypeChecker.h> +#include <libsolidity/interface/ErrorReporter.h> using namespace std; using namespace dev::eth; @@ -48,28 +49,29 @@ namespace eth::AssemblyItems compileContract(const string& _sourceCode) { ErrorList errors; - Parser parser(errors); + ErrorReporter errorReporter(errors); + Parser parser(errorReporter); ASTPointer<SourceUnit> sourceUnit; BOOST_REQUIRE_NO_THROW(sourceUnit = parser.parse(make_shared<Scanner>(CharStream(_sourceCode)))); BOOST_CHECK(!!sourceUnit); map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes; - NameAndTypeResolver resolver({}, scopes, errors); - solAssert(Error::containsOnlyWarnings(errors), ""); + NameAndTypeResolver resolver({}, scopes, errorReporter); + solAssert(Error::containsOnlyWarnings(errorReporter.errors()), ""); resolver.registerDeclarations(*sourceUnit); for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { BOOST_REQUIRE_NO_THROW(resolver.resolveNamesAndTypes(*contract)); - if (!Error::containsOnlyWarnings(errors)) + if (!Error::containsOnlyWarnings(errorReporter.errors())) return AssemblyItems(); } for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { - TypeChecker checker(errors); + TypeChecker checker(errorReporter); BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*contract)); - if (!Error::containsOnlyWarnings(errors)) + if (!Error::containsOnlyWarnings(errorReporter.errors())) return AssemblyItems(); } for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) @@ -117,8 +119,8 @@ BOOST_AUTO_TEST_CASE(location_test) shared_ptr<string const> n = make_shared<string>(""); AssemblyItems items = compileContract(sourceCode); vector<SourceLocation> locations = - vector<SourceLocation>(17, SourceLocation(2, 75, n)) + - vector<SourceLocation>(30, SourceLocation(20, 72, n)) + + vector<SourceLocation>(19, SourceLocation(2, 75, n)) + + vector<SourceLocation>(32, SourceLocation(20, 72, n)) + vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + vector<SourceLocation>(2, SourceLocation(58, 67, n)) + vector<SourceLocation>(3, SourceLocation(20, 72, n)); diff --git a/test/libsolidity/ErrorCheck.cpp b/test/libsolidity/ErrorCheck.cpp index 75555c9b..9b0f9fb7 100644 --- a/test/libsolidity/ErrorCheck.cpp +++ b/test/libsolidity/ErrorCheck.cpp @@ -29,6 +29,15 @@ using namespace std; bool dev::solidity::searchErrorMessage(Error const& _err, std::string const& _substr) { if (string const* errorMessage = boost::get_error_info<dev::errinfo_comment>(_err)) - return errorMessage->find(_substr) != std::string::npos; + { + if (errorMessage->find(_substr) == std::string::npos) + { + cout << "Expected message \"" << _substr << "\" but found" << *errorMessage << endl; + return false; + } + return true; + } + else + cout << "Expected error message but found none." << endl; return _substr.empty(); } diff --git a/test/libsolidity/GasMeter.cpp b/test/libsolidity/GasMeter.cpp index f90cb105..ef560b12 100644 --- a/test/libsolidity/GasMeter.cpp +++ b/test/libsolidity/GasMeter.cpp @@ -151,20 +151,20 @@ BOOST_AUTO_TEST_CASE(simple_contract) contract test { bytes32 public shaValue; function f(uint a) { - shaValue = sha3(a); + shaValue = keccak256(a); } } )"; testCreationTimeGas(sourceCode); } -BOOST_AUTO_TEST_CASE(store_sha3) +BOOST_AUTO_TEST_CASE(store_keccak256) { char const* sourceCode = R"( contract test { bytes32 public shaValue; function test(uint a) { - shaValue = sha3(a); + shaValue = keccak256(a); } } )"; diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index 8bf4df8e..5197f649 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -22,7 +22,7 @@ #include "../TestHelper.h" -#include <libsolidity/inlineasm/AsmStack.h> +#include <libsolidity/interface/AssemblyStack.h> #include <libsolidity/parsing/Scanner.h> #include <libsolidity/interface/Exceptions.h> #include <libsolidity/ast/AST.h> @@ -47,49 +47,56 @@ namespace test namespace { -boost::optional<Error> parseAndReturnFirstError(string const& _source, bool _assemble = false, bool _allowWarnings = true) +boost::optional<Error> parseAndReturnFirstError( + string const& _source, + bool _assemble = false, + bool _allowWarnings = true, + AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM +) { - assembly::InlineAssemblyStack stack; + AssemblyStack stack; bool success = false; try { - success = stack.parse(std::make_shared<Scanner>(CharStream(_source))); + success = stack.parseAndAnalyze("", _source); if (success && _assemble) - stack.assemble(); + stack.assemble(_machine); } catch (FatalError const&) { BOOST_FAIL("Fatal error leaked."); success = false; } - if (!success) - { - BOOST_REQUIRE_EQUAL(stack.errors().size(), 1); - return *stack.errors().front(); - } - else + shared_ptr<Error const> error; + for (auto const& e: stack.errors()) { - // If success is true, there might still be an error in the assembly stage. - if (_allowWarnings && Error::containsOnlyWarnings(stack.errors())) - return {}; - else if (!stack.errors().empty()) - { - if (!_allowWarnings) - BOOST_CHECK_EQUAL(stack.errors().size(), 1); - return *stack.errors().front(); - } + if (_allowWarnings && e->type() == Error::Type::Warning) + continue; + if (error) + BOOST_FAIL("Found more than one error."); + error = e; } + if (!success) + BOOST_REQUIRE(error); + if (error) + return *error; return {}; } -bool successParse(std::string const& _source, bool _assemble = false, bool _allowWarnings = true) +bool successParse( + string const& _source, + bool _assemble = false, + bool _allowWarnings = true, + AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM +) { - return !parseAndReturnFirstError(_source, _assemble, _allowWarnings); + return !parseAndReturnFirstError(_source, _assemble, _allowWarnings, _machine); } bool successAssemble(string const& _source, bool _allowWarnings = true) { - return successParse(_source, true, _allowWarnings); + return successParse(_source, true, _allowWarnings, AssemblyStack::Machine::EVM) && + successParse(_source, true, _allowWarnings, AssemblyStack::Machine::EVM15); } Error expectError(std::string const& _source, bool _assemble, bool _allowWarnings = false) @@ -100,29 +107,35 @@ Error expectError(std::string const& _source, bool _assemble, bool _allowWarning return *error; } -void parsePrintCompare(string const& _source) +void parsePrintCompare(string const& _source, bool _canWarn = false) { - assembly::InlineAssemblyStack stack; - BOOST_REQUIRE(stack.parse(std::make_shared<Scanner>(CharStream(_source)))); - BOOST_REQUIRE(stack.errors().empty()); - BOOST_CHECK_EQUAL(stack.toString(), _source); + AssemblyStack stack; + BOOST_REQUIRE(stack.parseAndAnalyze("", _source)); + if (_canWarn) + BOOST_REQUIRE(Error::containsOnlyWarnings(stack.errors())); + else + BOOST_REQUIRE(stack.errors().empty()); + BOOST_CHECK_EQUAL(stack.print(), _source); } } -#define CHECK_ERROR(text, assemble, typ, substring) \ +#define CHECK_ERROR(text, assemble, typ, substring, warnings) \ do \ { \ - Error err = expectError((text), (assemble), false); \ + Error err = expectError((text), (assemble), warnings); \ BOOST_CHECK(err.type() == (Error::Type::typ)); \ BOOST_CHECK(searchErrorMessage(err, (substring))); \ } while(0) #define CHECK_PARSE_ERROR(text, type, substring) \ -CHECK_ERROR(text, false, type, substring) +CHECK_ERROR(text, false, type, substring, false) + +#define CHECK_PARSE_WARNING(text, type, substring) \ +CHECK_ERROR(text, false, type, substring, false) #define CHECK_ASSEMBLE_ERROR(text, type, substring) \ -CHECK_ERROR(text, true, type, substring) +CHECK_ERROR(text, true, type, substring, false) @@ -161,6 +174,27 @@ BOOST_AUTO_TEST_CASE(vardecl) BOOST_CHECK(successParse("{ let x := 7 }")); } +BOOST_AUTO_TEST_CASE(vardecl_name_clashes) +{ + CHECK_PARSE_ERROR("{ let x := 1 let x := 2 }", DeclarationError, "Variable name x already taken in this scope."); +} + +BOOST_AUTO_TEST_CASE(vardecl_multi) +{ + BOOST_CHECK(successParse("{ function f() -> x, y {} let x, y := f() }")); +} + +BOOST_AUTO_TEST_CASE(vardecl_multi_conflict) +{ + CHECK_PARSE_ERROR("{ function f() -> x, y {} let x, x := f() }", DeclarationError, "Variable name x already taken in this scope."); +} + +BOOST_AUTO_TEST_CASE(vardecl_bool) +{ + CHECK_PARSE_ERROR("{ let x := true }", ParserError, "True and false are not valid literals."); + CHECK_PARSE_ERROR("{ let x := false }", ParserError, "True and false are not valid literals."); +} + BOOST_AUTO_TEST_CASE(assignment) { BOOST_CHECK(successParse("{ let x := 2 7 8 add =: x }")); @@ -181,6 +215,16 @@ BOOST_AUTO_TEST_CASE(functional) BOOST_CHECK(successParse("{ let x := 2 add(7, mul(6, x)) mul(7, 8) add =: x }")); } +BOOST_AUTO_TEST_CASE(functional_partial) +{ + CHECK_PARSE_ERROR("{ let x := byte }", ParserError, "Expected token \"(\""); +} + +BOOST_AUTO_TEST_CASE(functional_partial_success) +{ + BOOST_CHECK(successParse("{ let x := byte(1, 2) }")); +} + BOOST_AUTO_TEST_CASE(functional_assignment) { BOOST_CHECK(successParse("{ let x := 2 x := 7 }")); @@ -202,6 +246,89 @@ BOOST_AUTO_TEST_CASE(variable_use_before_decl) CHECK_PARSE_ERROR("{ let x := mul(2, x) }", DeclarationError, "Variable x used before it was declared."); } +BOOST_AUTO_TEST_CASE(switch_statement) +{ + BOOST_CHECK(successParse("{ switch 42 default {} }")); + BOOST_CHECK(successParse("{ switch 42 case 1 {} }")); + BOOST_CHECK(successParse("{ switch 42 case 1 {} case 2 {} }")); + BOOST_CHECK(successParse("{ switch 42 case 1 {} default {} }")); + BOOST_CHECK(successParse("{ switch 42 case 1 {} case 2 {} default {} }")); + BOOST_CHECK(successParse("{ switch mul(1, 2) case 1 {} case 2 {} default {} }")); + BOOST_CHECK(successParse("{ function f() -> x {} switch f() case 1 {} case 2 {} default {} }")); +} + +BOOST_AUTO_TEST_CASE(switch_no_cases) +{ + CHECK_PARSE_ERROR("{ switch 42 }", ParserError, "Switch statement without any cases."); +} + +BOOST_AUTO_TEST_CASE(switch_duplicate_case) +{ + CHECK_PARSE_ERROR("{ switch 42 case 1 {} case 1 {} default {} }", DeclarationError, "Duplicate case defined"); +} + +BOOST_AUTO_TEST_CASE(switch_invalid_expression) +{ + CHECK_PARSE_ERROR("{ switch {} default {} }", ParserError, "Literal, identifier or instruction expected."); + CHECK_PARSE_ERROR("{ switch calldatasize default {} }", ParserError, "Instructions are not supported as expressions for switch."); + CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", ParserError, "Instruction \"mstore\" not allowed in this context"); +} + +BOOST_AUTO_TEST_CASE(switch_default_before_case) +{ + CHECK_PARSE_ERROR("{ switch 42 default {} case 1 {} }", ParserError, "Case not allowed after default case."); +} + +BOOST_AUTO_TEST_CASE(switch_duplicate_default_case) +{ + CHECK_PARSE_ERROR("{ switch 42 default {} default {} }", ParserError, "Only one default case allowed."); +} + +BOOST_AUTO_TEST_CASE(switch_invalid_case) +{ + CHECK_PARSE_ERROR("{ switch 42 case mul(1, 2) {} case 2 {} default {} }", ParserError, "Literal expected."); +} + +BOOST_AUTO_TEST_CASE(switch_invalid_body) +{ + CHECK_PARSE_ERROR("{ switch 42 case 1 mul case 2 {} default {} }", ParserError, "Expected token LBrace got 'Identifier'"); +} + +BOOST_AUTO_TEST_CASE(for_statement) +{ + BOOST_CHECK(successParse("{ for {} 1 {} {} }")); + BOOST_CHECK(successParse("{ for { let i := 1 } lt(i, 5) { i := add(i, 1) } {} }")); +} + +BOOST_AUTO_TEST_CASE(for_invalid_expression) +{ + CHECK_PARSE_ERROR("{ for {} {} {} {} }", ParserError, "Literal, identifier or instruction expected."); + CHECK_PARSE_ERROR("{ for 1 1 {} {} }", ParserError, "Expected token LBrace got 'Number'"); + CHECK_PARSE_ERROR("{ for {} 1 1 {} }", ParserError, "Expected token LBrace got 'Number'"); + CHECK_PARSE_ERROR("{ for {} 1 {} 1 }", ParserError, "Expected token LBrace got 'Number'"); + CHECK_PARSE_ERROR("{ for {} calldatasize {} {} }", ParserError, "Instructions are not supported as conditions for the for statement."); + CHECK_PARSE_ERROR("{ for {} mstore(1, 1) {} {} }", ParserError, "Instruction \"mstore\" not allowed in this context"); +} + +BOOST_AUTO_TEST_CASE(for_visibility) +{ + BOOST_CHECK(successParse("{ for { let i := 1 } i { pop(i) } { pop(i) } }")); + CHECK_PARSE_ERROR("{ for {} i { let i := 1 } {} }", DeclarationError, "Identifier not found"); + CHECK_PARSE_ERROR("{ for {} 1 { let i := 1 } { pop(i) } }", DeclarationError, "Identifier not found"); + CHECK_PARSE_ERROR("{ for {} 1 { pop(i) } { let i := 1 } }", DeclarationError, "Identifier not found"); + CHECK_PARSE_ERROR("{ for { pop(i) } 1 { let i := 1 } {} }", DeclarationError, "Identifier not found"); + CHECK_PARSE_ERROR("{ for { pop(i) } 1 { } { let i := 1 } }", DeclarationError, "Identifier not found"); + CHECK_PARSE_ERROR("{ for {} i {} { let i := 1 } }", DeclarationError, "Identifier not found"); + CHECK_PARSE_ERROR("{ for {} 1 { pop(i) } { let i := 1 } }", DeclarationError, "Identifier not found"); + CHECK_PARSE_ERROR("{ for { let x := 1 } 1 { let x := 1 } {} }", DeclarationError, "Variable name x already taken in this scope"); + CHECK_PARSE_ERROR("{ for { let x := 1 } 1 {} { let x := 1 } }", DeclarationError, "Variable name x already taken in this scope"); + CHECK_PARSE_ERROR("{ let x := 1 for { let x := 1 } 1 {} {} }", DeclarationError, "Variable name x already taken in this scope"); + CHECK_PARSE_ERROR("{ let x := 1 for {} 1 { let x := 1 } {} }", DeclarationError, "Variable name x already taken in this scope"); + CHECK_PARSE_ERROR("{ let x := 1 for {} 1 {} { let x := 1 } }", DeclarationError, "Variable name x already taken in this scope"); + // Check that body and post are not sub-scopes of each other. + BOOST_CHECK(successParse("{ for {} 1 { let x := 1 } { let x := 1 } }")); +} + BOOST_AUTO_TEST_CASE(blocks) { BOOST_CHECK(successParse("{ let x := 7 { let y := 3 } { let z := 2 } }")); @@ -243,6 +370,23 @@ BOOST_AUTO_TEST_CASE(variable_access_cross_functions) CHECK_PARSE_ERROR("{ let x := 2 function g() { x pop } }", DeclarationError, "Identifier not found."); } +BOOST_AUTO_TEST_CASE(invalid_tuple_assignment) +{ + /// The push(42) is added here to silence the unbalanced stack error, so that there's only one error reported. + CHECK_PARSE_ERROR("{ 42 let x, y := 1 }", DeclarationError, "Variable count mismatch."); +} + +BOOST_AUTO_TEST_CASE(instruction_too_few_arguments) +{ + CHECK_PARSE_ERROR("{ mul() }", ParserError, "Expected expression (\"mul\" expects 2 arguments)"); + CHECK_PARSE_ERROR("{ mul(1) }", ParserError, "Expected comma (\"mul\" expects 2 arguments)"); +} + +BOOST_AUTO_TEST_CASE(instruction_too_many_arguments) +{ + CHECK_PARSE_ERROR("{ mul(1, 2, 3) }", ParserError, "Expected ')' (\"mul\" expects 2 arguments)"); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(Printing) @@ -269,7 +413,7 @@ BOOST_AUTO_TEST_CASE(print_functional) BOOST_AUTO_TEST_CASE(print_label) { - parsePrintCompare("{\n loop:\n jump(loop)\n}"); + parsePrintCompare("{\n loop:\n jump(loop)\n}", true); } BOOST_AUTO_TEST_CASE(print_assignments) @@ -277,6 +421,11 @@ BOOST_AUTO_TEST_CASE(print_assignments) parsePrintCompare("{\n let x := mul(2, 3)\n 7\n =: x\n x := add(1, 2)\n}"); } +BOOST_AUTO_TEST_CASE(print_multi_assignments) +{ + parsePrintCompare("{\n function f() -> x, y\n {\n }\n let x, y := f()\n}"); +} + BOOST_AUTO_TEST_CASE(print_string_literals) { parsePrintCompare("{\n \"\\n'\\xab\\x95\\\"\"\n pop\n}"); @@ -286,13 +435,23 @@ BOOST_AUTO_TEST_CASE(print_string_literal_unicode) { string source = "{ let x := \"\\u1bac\" }"; string parsed = "{\n let x := \"\\xe1\\xae\\xac\"\n}"; - assembly::InlineAssemblyStack stack; - BOOST_REQUIRE(stack.parse(std::make_shared<Scanner>(CharStream(source)))); + AssemblyStack stack; + BOOST_REQUIRE(stack.parseAndAnalyze("", source)); BOOST_REQUIRE(stack.errors().empty()); - BOOST_CHECK_EQUAL(stack.toString(), parsed); + BOOST_CHECK_EQUAL(stack.print(), parsed); parsePrintCompare(parsed); } +BOOST_AUTO_TEST_CASE(print_switch) +{ + parsePrintCompare("{\n switch 42\n case 1 {\n }\n case 2 {\n }\n default {\n }\n}"); +} + +BOOST_AUTO_TEST_CASE(print_for) +{ + parsePrintCompare("{\n let ret := 5\n for {\n let i := 1\n }\n lt(i, 15)\n {\n i := add(i, 1)\n }\n {\n ret := mul(ret, i)\n }\n}"); +} + BOOST_AUTO_TEST_CASE(function_definitions_multiple_args) { parsePrintCompare("{\n function f(a, d)\n {\n mstore(a, d)\n }\n function g(a, d) -> x, y\n {\n }\n}"); @@ -358,7 +517,7 @@ BOOST_AUTO_TEST_CASE(imbalanced_stack) BOOST_AUTO_TEST_CASE(error_tag) { - BOOST_CHECK(successAssemble("{ jump(invalidJumpLabel) }")); + CHECK_ERROR("{ jump(invalidJumpLabel) }", true, DeclarationError, "Identifier not found", true); } BOOST_AUTO_TEST_CASE(designated_invalid_instruction) @@ -386,6 +545,101 @@ BOOST_AUTO_TEST_CASE(revert) BOOST_CHECK(successAssemble("{ revert(0, 0) }")); } +BOOST_AUTO_TEST_CASE(function_calls) +{ + BOOST_CHECK(successAssemble("{ function f() {} }")); + BOOST_CHECK(successAssemble("{ function f() { let y := 2 } }")); + BOOST_CHECK(successAssemble("{ function f() -> z { let y := 2 } }")); + BOOST_CHECK(successAssemble("{ function f(a) { let y := 2 } }")); + BOOST_CHECK(successAssemble("{ function f(a) { let y := a } }")); + BOOST_CHECK(successAssemble("{ function f() -> x, y, z {} }")); + BOOST_CHECK(successAssemble("{ function f(x, y, z) {} }")); + BOOST_CHECK(successAssemble("{ function f(a, b) -> x, y, z { y := a } }")); + BOOST_CHECK(successAssemble("{ function f() {} f() }")); + BOOST_CHECK(successAssemble("{ function f() -> x, y { x := 1 y := 2} let a, b := f() }")); + BOOST_CHECK(successAssemble("{ function f(a, b) -> x, y { x := b y := a } let a, b := f(2, 3) }")); + BOOST_CHECK(successAssemble("{ function rec(a) { rec(sub(a, 1)) } rec(2) }")); + BOOST_CHECK(successAssemble("{ let r := 2 function f() -> x, y { x := 1 y := 2} let a, b := f() b := r }")); + BOOST_CHECK(successAssemble("{ function f() { g() } function g() { f() } }")); +} + +BOOST_AUTO_TEST_CASE(embedded_functions) +{ + BOOST_CHECK(successAssemble("{ function f(r, s) -> x { function g(a) -> b { } x := g(2) } let x := f(2, 3) }")); +} + +BOOST_AUTO_TEST_CASE(switch_statement) +{ + BOOST_CHECK(successAssemble("{ switch 1 default {} }")); + BOOST_CHECK(successAssemble("{ switch 1 case 1 {} default {} }")); + BOOST_CHECK(successAssemble("{ switch 1 case 1 {} }")); + BOOST_CHECK(successAssemble("{ let a := 3 switch a case 1 { a := 1 } case 2 { a := 5 } a := 9}")); + BOOST_CHECK(successAssemble("{ let a := 2 switch calldataload(0) case 1 { a := 1 } case 2 { a := 5 } }")); +} + +BOOST_AUTO_TEST_CASE(for_statement) +{ + BOOST_CHECK(successAssemble("{ for {} 1 {} {} }")); + BOOST_CHECK(successAssemble("{ let x := calldatasize() for { let i := 0} lt(i, x) { i := add(i, 1) } { mstore(i, 2) } }")); +} + + +BOOST_AUTO_TEST_CASE(large_constant) +{ + auto source = R"({ + switch mul(1, 2) + case 0x0000000000000000000000000000000000000000000000000000000026121ff0 { + } + })"; + BOOST_CHECK(successAssemble(source)); +} + +BOOST_AUTO_TEST_CASE(keccak256) +{ + BOOST_CHECK(successAssemble("{ 0 0 keccak256 pop }")); + BOOST_CHECK(successAssemble("{ pop(keccak256(0, 0)) }")); + BOOST_CHECK(successAssemble("{ 0 0 sha3 pop }")); + BOOST_CHECK(successAssemble("{ pop(sha3(0, 0)) }")); +} + +BOOST_AUTO_TEST_CASE(returndatasize) +{ + BOOST_CHECK(successAssemble("{ let r := returndatasize }")); +} + +BOOST_AUTO_TEST_CASE(returndatasize_functional) +{ + BOOST_CHECK(successAssemble("{ let r := returndatasize() }")); +} + +BOOST_AUTO_TEST_CASE(returndatacopy) +{ + BOOST_CHECK(successAssemble("{ 64 32 0 returndatacopy }")); +} + +BOOST_AUTO_TEST_CASE(returndatacopy_functional) +{ + BOOST_CHECK(successAssemble("{ returndatacopy(0, 32, 64) }")); +} + +BOOST_AUTO_TEST_CASE(staticcall) +{ + BOOST_CHECK(successAssemble("{ pop(staticcall(10000, 0x123, 64, 0x10, 128, 0x10)) }")); +} + +BOOST_AUTO_TEST_CASE(create2) +{ + BOOST_CHECK(successAssemble("{ pop(create2(10, 0x123, 32, 64)) }")); +} + +BOOST_AUTO_TEST_CASE(jump_warning) +{ + CHECK_PARSE_WARNING("{ 1 jump }", Warning, "Jump instructions"); + CHECK_PARSE_WARNING("{ 1 2 jumpi }", Warning, "Jump instructions"); + CHECK_PARSE_WARNING("{ a: jump(a) }", Warning, "Jump instructions"); + CHECK_PARSE_WARNING("{ a: jumpi(a, 2) }", Warning, "Jump instructions"); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp new file mode 100644 index 00000000..aa690f0b --- /dev/null +++ b/test/libsolidity/JSONCompiler.cpp @@ -0,0 +1,111 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @date 2017 + * Unit tests for solc/jsonCompiler.cpp. + */ + +#include <string> +#include <iostream> +#include <regex> +#include <boost/test/unit_test.hpp> +#include <libdevcore/JSON.h> + +#include "../Metadata.h" +#include "../TestHelper.h" + +using namespace std; + +extern "C" +{ +extern char const* compileJSONMulti(char const* _input, bool _optimize); +} + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +namespace +{ + +Json::Value compile(string const& _input) +{ + string output(compileJSONMulti(_input.c_str(), dev::test::Options::get().optimize)); + Json::Value ret; + BOOST_REQUIRE(Json::Reader().parse(output, ret, false)); + return ret; +} + +} // end anonymous namespace + +BOOST_AUTO_TEST_SUITE(JSONCompiler) + +BOOST_AUTO_TEST_CASE(basic_compilation) +{ + char const* input = R"( + { + "sources": { + "fileA": "contract A { }" + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(result.isObject()); + BOOST_CHECK(result["contracts"].isObject()); + BOOST_CHECK(result["contracts"]["fileA:A"].isObject()); + Json::Value contract = result["contracts"]["fileA:A"]; + BOOST_CHECK(contract.isObject()); + BOOST_CHECK(contract["interface"].isString()); + BOOST_CHECK_EQUAL(contract["interface"].asString(), "[]"); + BOOST_CHECK(contract["bytecode"].isString()); + BOOST_CHECK_EQUAL( + dev::test::bytecodeSansMetadata(contract["bytecode"].asString()), + "60606040523415600e57600080fd5b5b603680601c6000396000f30060606040525b600080fd00" + ); + BOOST_CHECK(contract["runtimeBytecode"].isString()); + BOOST_CHECK_EQUAL( + dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()), + "60606040525b600080fd00" + ); + BOOST_CHECK(contract["functionHashes"].isObject()); + BOOST_CHECK(contract["gasEstimates"].isObject()); + BOOST_CHECK_EQUAL( + dev::jsonCompactPrint(contract["gasEstimates"]), + "{\"creation\":[62,10800],\"external\":{},\"internal\":{}}" + ); + BOOST_CHECK(contract["metadata"].isString()); + BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString())); + BOOST_CHECK(result["sources"].isObject()); + BOOST_CHECK(result["sources"]["fileA"].isObject()); + BOOST_CHECK(result["sources"]["fileA"]["AST"].isObject()); + BOOST_CHECK_EQUAL( + dev::jsonCompactPrint(result["sources"]["fileA"]["AST"]), + "{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]}}," + "\"children\":[{\"attributes\":{\"baseContracts\":[null],\"contractDependencies\":[null]," + "\"contractKind\":\"contract\",\"documentation\":null,\"fullyImplemented\":true,\"linearizedBaseContracts\":[1]," + "\"name\":\"A\",\"nodes\":[null],\"scope\":2},\"id\":1,\"name\":\"ContractDefinition\"," + "\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}" + ); +} +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/libsolidity/Metadata.cpp b/test/libsolidity/Metadata.cpp new file mode 100644 index 00000000..60bb2e4e --- /dev/null +++ b/test/libsolidity/Metadata.cpp @@ -0,0 +1,63 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see <http://www.gnu.org/licenses/>. + */ +/** + * @date 2017 + * Unit tests for the metadata output. + */ + +#include "../Metadata.h" +#include "../TestHelper.h" +#include <libsolidity/interface/CompilerStack.h> +#include <libdevcore/SwarmHash.h> + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +BOOST_AUTO_TEST_SUITE(Metadata) + +BOOST_AUTO_TEST_CASE(metadata_stamp) +{ + // Check that the metadata stamp is at the end of the runtime bytecode. + char const* sourceCode = R"( + pragma solidity >=0.0; + contract test { + function g(function(uint) external returns (uint) x) {} + } + )"; + CompilerStack compilerStack; + BOOST_REQUIRE(compilerStack.compile(std::string(sourceCode))); + bytes const& bytecode = compilerStack.runtimeObject("test").bytecode; + std::string const& metadata = compilerStack.onChainMetadata("test"); + BOOST_CHECK(dev::test::isValidMetadata(metadata)); + bytes hash = dev::swarmHash(metadata).asBytes(); + BOOST_REQUIRE(hash.size() == 32); + BOOST_REQUIRE(bytecode.size() >= 2); + size_t metadataCBORSize = (size_t(bytecode.end()[-2]) << 8) + size_t(bytecode.end()[-1]); + BOOST_REQUIRE(metadataCBORSize < bytecode.size() - 2); + bytes expectation = bytes{0xa1, 0x65, 'b', 'z', 'z', 'r', '0', 0x58, 0x20} + hash; + BOOST_CHECK(std::equal(expectation.begin(), expectation.end(), bytecode.end() - metadataCBORSize - 2)); +} + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index bdcc5b10..f87390e1 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -44,7 +44,7 @@ public: { ETH_TEST_REQUIRE_NO_THROW(m_compilerStack.parseAndAnalyze("pragma solidity >=0.0;\n" + _code), "Parsing contract failed"); - Json::Value generatedInterface = m_compilerStack.metadata("", DocumentationType::ABIInterface); + Json::Value generatedInterface = m_compilerStack.contractABI(""); Json::Value expectedInterface; m_reader.parse(_expectedInterfaceString, expectedInterface); BOOST_CHECK_MESSAGE( @@ -752,26 +752,6 @@ BOOST_AUTO_TEST_CASE(function_type) checkInterface(sourceCode, interface); } -BOOST_AUTO_TEST_CASE(metadata_stamp) -{ - // Check that the metadata stamp is at the end of the runtime bytecode. - char const* sourceCode = R"( - pragma solidity >=0.0; - contract test { - function g(function(uint) external returns (uint) x) {} - } - )"; - BOOST_REQUIRE(m_compilerStack.compile(std::string(sourceCode))); - bytes const& bytecode = m_compilerStack.runtimeObject("test").bytecode; - bytes hash = dev::swarmHash(m_compilerStack.onChainMetadata("test")).asBytes(); - BOOST_REQUIRE(hash.size() == 32); - BOOST_REQUIRE(bytecode.size() >= 2); - size_t metadataCBORSize = (size_t(bytecode.end()[-2]) << 8) + size_t(bytecode.end()[-1]); - BOOST_REQUIRE(metadataCBORSize < bytecode.size() - 2); - bytes expectation = bytes{0xa1, 0x65, 'b', 'z', 'z', 'r', '0', 0x58, 0x20} + hash; - BOOST_CHECK(std::equal(expectation.begin(), expectation.end(), bytecode.end() - metadataCBORSize - 2)); -} - BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index f2f4b8b0..a6c01283 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1355,7 +1355,7 @@ BOOST_AUTO_TEST_CASE(multiple_elementary_accessors) function test() { data = 8; name = "Celina"; - a_hash = sha3(123); + a_hash = keccak256(123); an_address = address(0x1337); super_secret_data = 42; } @@ -1864,12 +1864,12 @@ BOOST_AUTO_TEST_CASE(selfdestruct) BOOST_CHECK_EQUAL(balanceAt(address), amount); } -BOOST_AUTO_TEST_CASE(sha3) +BOOST_AUTO_TEST_CASE(keccak256) { char const* sourceCode = R"( contract test { - function a(bytes32 input) returns (bytes32 sha3hash) { - return sha3(input); + function a(bytes32 input) returns (bytes32 hash) { + return keccak256(input); } } )"; @@ -1883,6 +1883,23 @@ BOOST_AUTO_TEST_CASE(sha3) testContractAgainstCpp("a(bytes32)", f, u256(-1)); } +BOOST_AUTO_TEST_CASE(sha3) +{ + char const* sourceCode = R"( + contract test { + // to confuse the optimiser + function b(bytes32 input) returns (bytes32) { + return sha3(input); + } + function a(bytes32 input) returns (bool) { + return keccak256(input) == b(input); + } + } + )"; + compileAndRun(sourceCode); + BOOST_REQUIRE(callContractFunction("a(bytes32)", u256(42)) == encodeArgs(true)); +} + BOOST_AUTO_TEST_CASE(sha256) { char const* sourceCode = R"( @@ -3110,13 +3127,13 @@ BOOST_AUTO_TEST_CASE(empty_name_return_parameter) BOOST_CHECK(callContractFunction("f(uint256)", 9) == encodeArgs(9)); } -BOOST_AUTO_TEST_CASE(sha3_multiple_arguments) +BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments) { char const* sourceCode = R"( contract c { function foo(uint a, uint b, uint c) returns (bytes32 d) { - d = sha3(a, b, c); + d = keccak256(a, b, c); } } )"; @@ -3129,13 +3146,13 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments) toBigEndian(u256(13))))); } -BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_numeric_literals) +BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments_with_numeric_literals) { char const* sourceCode = R"( contract c { function foo(uint a, uint16 b) returns (bytes32 d) { - d = sha3(a, b, 145); + d = keccak256(a, b, 145); } } )"; @@ -3148,17 +3165,17 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_numeric_literals) bytes(1, 0x91)))); } -BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_string_literals) +BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments_with_string_literals) { char const* sourceCode = R"( contract c { function foo() returns (bytes32 d) { - d = sha3("foo"); + d = keccak256("foo"); } function bar(uint a, uint16 b) returns (bytes32 d) { - d = sha3(a, b, 145, "foo"); + d = keccak256(a, b, 145, "foo"); } } )"; @@ -3174,7 +3191,7 @@ BOOST_AUTO_TEST_CASE(sha3_multiple_arguments_with_string_literals) bytes{0x66, 0x6f, 0x6f}))); } -BOOST_AUTO_TEST_CASE(sha3_with_bytes) +BOOST_AUTO_TEST_CASE(keccak256_with_bytes) { char const* sourceCode = R"( contract c { @@ -3185,7 +3202,7 @@ BOOST_AUTO_TEST_CASE(sha3_with_bytes) data[0] = "f"; data[1] = "o"; data[2] = "o"; - return sha3(data) == sha3("foo"); + return keccak256(data) == keccak256("foo"); } } )"; @@ -3193,7 +3210,7 @@ BOOST_AUTO_TEST_CASE(sha3_with_bytes) BOOST_CHECK(callContractFunction("foo()") == encodeArgs(true)); } -BOOST_AUTO_TEST_CASE(iterated_sha3_with_bytes) +BOOST_AUTO_TEST_CASE(iterated_keccak256_with_bytes) { char const* sourceCode = R"( contract c { @@ -3204,7 +3221,7 @@ BOOST_AUTO_TEST_CASE(iterated_sha3_with_bytes) data[0] = "x"; data[1] = "y"; data[2] = "z"; - return sha3("b", sha3(data), "a"); + return keccak256("b", keccak256(data), "a"); } } )"; @@ -3214,13 +3231,13 @@ BOOST_AUTO_TEST_CASE(iterated_sha3_with_bytes) )); } -BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments) +BOOST_AUTO_TEST_CASE(sha3_multiple_arguments) { char const* sourceCode = R"( contract c { function foo(uint a, uint b, uint c) returns (bytes32 d) { - d = keccak256(a, b, c); + d = sha3(a, b, c); } })"; compileAndRun(sourceCode); @@ -3245,7 +3262,7 @@ BOOST_AUTO_TEST_CASE(generic_call) function sender() payable {} function doSend(address rec) returns (uint d) { - bytes4 signature = bytes4(bytes32(sha3("receive(uint256)"))); + bytes4 signature = bytes4(bytes32(keccak256("receive(uint256)"))); rec.call.value(2)(signature, 23); return receiver(rec).received(); } @@ -3270,7 +3287,7 @@ BOOST_AUTO_TEST_CASE(generic_callcode) function Sender() payable { } function doSend(address rec) returns (uint d) { - bytes4 signature = bytes4(bytes32(sha3("receive(uint256)"))); + bytes4 signature = bytes4(bytes32(keccak256("receive(uint256)"))); rec.callcode.value(2)(signature, 23); return Receiver(rec).received(); } @@ -3307,7 +3324,7 @@ BOOST_AUTO_TEST_CASE(generic_delegatecall) function Sender() payable {} function doSend(address rec) payable { - bytes4 signature = bytes4(bytes32(sha3("receive(uint256)"))); + bytes4 signature = bytes4(bytes32(keccak256("receive(uint256)"))); if (rec.delegatecall(signature, 23)) {} } } @@ -3372,7 +3389,7 @@ BOOST_AUTO_TEST_CASE(bytes_from_calldata_to_memory) char const* sourceCode = R"( contract C { function f() returns (bytes32) { - return sha3("abc", msg.data); + return keccak256("abc", msg.data); } } )"; @@ -3685,6 +3702,50 @@ BOOST_AUTO_TEST_CASE(enum_explicit_overflow) BOOST_CHECK(callContractFunction("getChoiceExp(uint256)", 0) == encodeArgs(0)); } +BOOST_AUTO_TEST_CASE(storing_invalid_boolean) +{ + char const* sourceCode = R"( + contract C { + event Ev(bool); + bool public perm; + function set() returns(uint) { + bool tmp; + assembly { + tmp := 5 + } + perm = tmp; + return 1; + } + function ret() returns(bool) { + bool tmp; + assembly { + tmp := 5 + } + return tmp; + } + function ev() returns(uint) { + bool tmp; + assembly { + tmp := 5 + } + Ev(tmp); + return 1; + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("set()") == encodeArgs(1)); + BOOST_CHECK(callContractFunction("perm()") == encodeArgs(1)); + BOOST_CHECK(callContractFunction("ret()") == encodeArgs(1)); + BOOST_CHECK(callContractFunction("ev()") == encodeArgs(1)); + BOOST_REQUIRE_EQUAL(m_logs.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); + BOOST_CHECK(m_logs[0].data == encodeArgs(1)); + BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); + BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::keccak256(string("Ev(bool)"))); +} + + BOOST_AUTO_TEST_CASE(using_contract_enums_with_explicit_contract_name) { char const* sourceCode = R"( @@ -4466,6 +4527,38 @@ BOOST_AUTO_TEST_CASE(array_copy_including_mapping) BOOST_CHECK(storageEmpty(m_contractAddress)); } +BOOST_AUTO_TEST_CASE(swap_in_storage_overwrite) +{ + // This tests a swap in storage which does not work as one + // might expect because we do not have temporary storage. + // (x, y) = (y, x) is the same as + // y = x; + // x = y; + char const* sourceCode = R"( + contract c { + struct S { uint a; uint b; } + S public x; + S public y; + function set() { + x.a = 1; x.b = 2; + y.a = 3; y.b = 4; + } + function swap() { + (x, y) = (y, x); + } + } + )"; + compileAndRun(sourceCode); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(0), u256(0))); + BOOST_CHECK(callContractFunction("y()") == encodeArgs(u256(0), u256(0))); + BOOST_CHECK(callContractFunction("set()") == encodeArgs()); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(1), u256(2))); + BOOST_CHECK(callContractFunction("y()") == encodeArgs(u256(3), u256(4))); + BOOST_CHECK(callContractFunction("swap()") == encodeArgs()); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(1), u256(2))); + BOOST_CHECK(callContractFunction("y()") == encodeArgs(u256(1), u256(2))); +} + BOOST_AUTO_TEST_CASE(pass_dynamic_arguments_to_the_base) { char const* sourceCode = R"( @@ -5294,7 +5387,7 @@ BOOST_AUTO_TEST_CASE(reusing_memory) mapping(uint => uint) map; function f(uint x) returns (uint) { map[x] = x; - return (new Helper(uint(sha3(this.g(map[x]))))).flag(); + return (new Helper(uint(keccak256(this.g(map[x]))))).flag(); } function g(uint a) returns (uint) { @@ -7445,6 +7538,33 @@ BOOST_AUTO_TEST_CASE(inline_assembly_storage_access) BOOST_CHECK(callContractFunction("z()") == encodeArgs(u256(7))); } +BOOST_AUTO_TEST_CASE(inline_assembly_storage_access_inside_function) +{ + char const* sourceCode = R"( + contract C { + uint16 x; + uint16 public y; + uint public z; + function f() returns (bool) { + uint off1; + uint off2; + assembly { + function f() -> o1 { + sstore(z_slot, 7) + o1 := y_offset + } + off2 := f() + } + assert(off2 == 2); + return true; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(true)); + BOOST_CHECK(callContractFunction("z()") == encodeArgs(u256(7))); +} + BOOST_AUTO_TEST_CASE(inline_assembly_storage_access_via_pointer) { char const* sourceCode = R"( @@ -7518,6 +7638,178 @@ BOOST_AUTO_TEST_CASE(inline_assembly_function_access) BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(10))); } +BOOST_AUTO_TEST_CASE(inline_assembly_function_call) +{ + char const* sourceCode = R"( + contract C { + function f() { + assembly { + function asmfun(a, b, c) -> x, y, z { + x := a + y := b + z := 7 + } + let a1, b1, c1 := asmfun(1, 2, 3) + mstore(0x00, a1) + mstore(0x20, b1) + mstore(0x40, c1) + return(0, 0x60) + } + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1), u256(2), u256(7))); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_function_call2) +{ + char const* sourceCode = R"( + contract C { + function f() { + assembly { + let d := 0x10 + function asmfun(a, b, c) -> x, y, z { + x := a + y := b + z := 7 + } + let a1, b1, c1 := asmfun(1, 2, 3) + mstore(0x00, a1) + mstore(0x20, b1) + mstore(0x40, c1) + mstore(0x60, d) + return(0, 0x80) + } + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1), u256(2), u256(7), u256(0x10))); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_embedded_function_call) +{ + char const* sourceCode = R"( + contract C { + function f() { + assembly { + let d := 0x10 + function asmfun(a, b, c) -> x, y, z { + x := g(a) + function g(r) -> s { s := mul(r, r) } + y := g(b) + z := 7 + } + let a1, b1, c1 := asmfun(1, 2, 3) + mstore(0x00, a1) + mstore(0x20, b1) + mstore(0x40, c1) + mstore(0x60, d) + return(0, 0x80) + } + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(1), u256(4), u256(7), u256(0x10))); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_switch) +{ + char const* sourceCode = R"( + contract C { + function f(uint a) returns (uint b) { + assembly { + switch a + case 1 { b := 8 } + case 2 { b := 9 } + default { b := 2 } + } + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f(uint256)", u256(0)) == encodeArgs(u256(2))); + BOOST_CHECK(callContractFunction("f(uint256)", u256(1)) == encodeArgs(u256(8))); + BOOST_CHECK(callContractFunction("f(uint256)", u256(2)) == encodeArgs(u256(9))); + BOOST_CHECK(callContractFunction("f(uint256)", u256(3)) == encodeArgs(u256(2))); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_recursion) +{ + char const* sourceCode = R"( + contract C { + function f(uint a) returns (uint b) { + assembly { + function fac(n) -> nf { + switch n + case 0 { nf := 1 } + case 1 { nf := 1 } + default { nf := mul(n, fac(sub(n, 1))) } + } + b := fac(a) + } + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f(uint256)", u256(0)) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("f(uint256)", u256(1)) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("f(uint256)", u256(2)) == encodeArgs(u256(2))); + BOOST_CHECK(callContractFunction("f(uint256)", u256(3)) == encodeArgs(u256(6))); + BOOST_CHECK(callContractFunction("f(uint256)", u256(4)) == encodeArgs(u256(24))); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_for) +{ + char const* sourceCode = R"( + contract C { + function f(uint a) returns (uint b) { + assembly { + function fac(n) -> nf { + nf := 1 + for { let i := n } gt(i, 0) { i := sub(i, 1) } { + nf := mul(nf, i) + } + } + b := fac(a) + } + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f(uint256)", u256(0)) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("f(uint256)", u256(1)) == encodeArgs(u256(1))); + BOOST_CHECK(callContractFunction("f(uint256)", u256(2)) == encodeArgs(u256(2))); + BOOST_CHECK(callContractFunction("f(uint256)", u256(3)) == encodeArgs(u256(6))); + BOOST_CHECK(callContractFunction("f(uint256)", u256(4)) == encodeArgs(u256(24))); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_for2) +{ + char const* sourceCode = R"( + contract C { + uint st; + function f(uint a) returns (uint b, uint c, uint d) { + st = 0; + assembly { + function sideeffect(r) -> x { sstore(0, add(sload(0), r)) x := 1} + for { let i := a } eq(i, sideeffect(2)) { d := add(d, 3) } { + b := i + i := 0 + } + } + c = st; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f(uint256)", u256(0)) == encodeArgs(u256(0), u256(2), u256(0))); + BOOST_CHECK(callContractFunction("f(uint256)", u256(1)) == encodeArgs(u256(1), u256(4), u256(3))); + BOOST_CHECK(callContractFunction("f(uint256)", u256(2)) == encodeArgs(u256(0), u256(2), u256(0))); +} + BOOST_AUTO_TEST_CASE(index_access_with_type_conversion) { // Test for a bug where higher order bits cleanup was not done for array index access. @@ -9126,21 +9418,6 @@ BOOST_AUTO_TEST_CASE(packed_storage_overflow) BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0x1234), u256(0), u256(0), u256(0xfffe))); } -BOOST_AUTO_TEST_CASE(inline_assembly_invalidjumplabel) -{ - char const* sourceCode = R"( - contract C { - function f() { - assembly { - jump(invalidJumpLabel) - } - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - BOOST_CHECK(callContractFunction("f()") == encodeArgs()); -} - BOOST_AUTO_TEST_CASE(contracts_separated_with_comment) { char const* sourceCode = R"( @@ -9268,6 +9545,50 @@ BOOST_AUTO_TEST_CASE(revert) BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(42))); } +BOOST_AUTO_TEST_CASE(negative_stack_height) +{ + // This code was causing negative stack height during code generation + // because the stack height was not adjusted at the beginning of functions. + char const* sourceCode = R"( + contract C { + mapping(uint => Invoice) public invoices; + struct Invoice { + uint AID; + bool Aboola; + bool Aboolc; + bool exists; + } + function nredit(uint startindex) public constant returns(uint[500] CIDs, uint[500] dates, uint[500] RIDs, bool[500] Cboolas, uint[500] amounts){} + function return500InvoicesByDates(uint begindate, uint enddate, uint startindex) public constant returns(uint[500] AIDs, bool[500] Aboolas, uint[500] dates, bytes32[3][500] Abytesas, bytes32[3][500] bytesbs, bytes32[2][500] bytescs, uint[500] amounts, bool[500] Aboolbs, bool[500] Aboolcs){} + function return500PaymentsByDates(uint begindate, uint enddate, uint startindex) public constant returns(uint[500] BIDs, uint[500] dates, uint[500] RIDs, bool[500] Bboolas, bytes32[3][500] bytesbs,bytes32[2][500] bytescs, uint[500] amounts, bool[500] Bboolbs){} + } + )"; + compileAndRun(sourceCode, 0, "C"); +} + +BOOST_AUTO_TEST_CASE(literal_empty_string) +{ + char const* sourceCode = R"( + contract C { + bytes32 public x; + uint public a; + function f(bytes32 _x, uint _a) { + x = _x; + a = _a; + } + function g() { + this.f("", 2); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("g()") == encodeArgs()); + BOOST_CHECK(callContractFunction("x()") == encodeArgs(u256(0))); + BOOST_CHECK(callContractFunction("a()") == encodeArgs(u256(2))); +} + BOOST_AUTO_TEST_CASE(scientific_notation) { char const* sourceCode = R"( @@ -9336,6 +9657,45 @@ BOOST_AUTO_TEST_CASE(interface) BOOST_CHECK(callContractFunction("f(address)", recipient) == encodeArgs(true)); } +BOOST_AUTO_TEST_CASE(keccak256_assembly) +{ + char const* sourceCode = R"( + contract C { + function f() returns (bytes32 ret) { + assembly { + ret := keccak256(0, 0) + } + } + function g() returns (bytes32 ret) { + assembly { + 0 + 0 + keccak256 + =: ret + } + } + function h() returns (bytes32 ret) { + assembly { + ret := sha3(0, 0) + } + } + function i() returns (bytes32 ret) { + assembly { + 0 + 0 + sha3 + =: ret + } + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("f()") == fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); + BOOST_CHECK(callContractFunction("g()") == fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); + BOOST_CHECK(callContractFunction("h()") == fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); + BOOST_CHECK(callContractFunction("i()") == fromHex("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index 3116aea8..58efa0a2 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -29,6 +29,7 @@ #include <libsolidity/codegen/ExpressionCompiler.h> #include <libsolidity/ast/AST.h> #include <libsolidity/analysis/TypeChecker.h> +#include <libsolidity/interface/ErrorReporter.h> #include "../TestHelper.h" using namespace std; @@ -98,7 +99,8 @@ bytes compileFirstExpression( try { ErrorList errors; - sourceUnit = Parser(errors).parse(make_shared<Scanner>(CharStream(_sourceCode))); + ErrorReporter errorReporter(errors); + sourceUnit = Parser(errorReporter).parse(make_shared<Scanner>(CharStream(_sourceCode))); if (!sourceUnit) return bytes(); } @@ -114,8 +116,9 @@ bytes compileFirstExpression( declarations.push_back(variable.get()); ErrorList errors; + ErrorReporter errorReporter(errors); map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes; - NameAndTypeResolver resolver(declarations, scopes, errors); + NameAndTypeResolver resolver(declarations, scopes, errorReporter); resolver.registerDeclarations(*sourceUnit); vector<ContractDefinition const*> inheritanceHierarchy; @@ -128,7 +131,8 @@ bytes compileFirstExpression( for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get())) { - TypeChecker typeChecker(errors); + ErrorReporter errorReporter(errors); + TypeChecker typeChecker(errorReporter); BOOST_REQUIRE(typeChecker.checkTypeRequirements(*contract)); } for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 3a9f7295..d0aee3d0 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -30,7 +30,7 @@ #include <libsolidity/analysis/StaticAnalyzer.h> #include <libsolidity/analysis/PostTypeChecker.h> #include <libsolidity/analysis/SyntaxChecker.h> -#include <libsolidity/interface/Exceptions.h> +#include <libsolidity/interface/ErrorReporter.h> #include <libsolidity/analysis/GlobalContext.h> #include <libsolidity/analysis/TypeChecker.h> @@ -56,7 +56,8 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, // Silence compiler version warning string source = _insertVersionPragma ? "pragma solidity >=0.0;\n" + _source : _source; ErrorList errors; - Parser parser(errors); + ErrorReporter errorReporter(errors); + Parser parser(errorReporter); ASTPointer<SourceUnit> sourceUnit; // catch exceptions for a transition period try @@ -65,14 +66,14 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, if(!sourceUnit) BOOST_FAIL("Parsing failed in type checker test."); - SyntaxChecker syntaxChecker(errors); + SyntaxChecker syntaxChecker(errorReporter); if (!syntaxChecker.checkSyntax(*sourceUnit)) - return make_pair(sourceUnit, errors.at(0)); + return make_pair(sourceUnit, errorReporter.errors().at(0)); std::shared_ptr<GlobalContext> globalContext = make_shared<GlobalContext>(); map<ASTNode const*, shared_ptr<DeclarationContainer>> scopes; - NameAndTypeResolver resolver(globalContext->declarations(), scopes, errors); - solAssert(Error::containsOnlyWarnings(errors), ""); + NameAndTypeResolver resolver(globalContext->declarations(), scopes, errorReporter); + solAssert(Error::containsOnlyWarnings(errorReporter.errors()), ""); resolver.registerDeclarations(*sourceUnit); bool success = true; @@ -92,26 +93,32 @@ parseAnalyseAndReturnError(string const& _source, bool _reportWarnings = false, globalContext->setCurrentContract(*contract); resolver.updateDeclaration(*globalContext->currentThis()); - TypeChecker typeChecker(errors); + TypeChecker typeChecker(errorReporter); bool success = typeChecker.checkTypeRequirements(*contract); - BOOST_CHECK(success || !errors.empty()); + BOOST_CHECK(success || !errorReporter.errors().empty()); } if (success) - if (!PostTypeChecker(errors).check(*sourceUnit)) + if (!PostTypeChecker(errorReporter).check(*sourceUnit)) success = false; if (success) - if (!StaticAnalyzer(errors).analyze(*sourceUnit)) + if (!StaticAnalyzer(errorReporter).analyze(*sourceUnit)) success = false; - if (errors.size() > 1 && !_allowMultipleErrors) - BOOST_FAIL("Multiple errors found"); - for (auto const& currentError: errors) + std::shared_ptr<Error const> error; + for (auto const& currentError: errorReporter.errors()) { if ( (_reportWarnings && currentError->type() == Error::Type::Warning) || (!_reportWarnings && currentError->type() != Error::Type::Warning) ) - return make_pair(sourceUnit, currentError); + { + if (error && !_allowMultipleErrors) + BOOST_FAIL("Multiple errors found"); + if (!error) + error = currentError; + } } + if (error) + return make_pair(sourceUnit, error); } catch (InternalCompilerError const& _e) { @@ -192,11 +199,16 @@ CHECK_ERROR_OR_WARNING(text, type, substring, false, false) #define CHECK_ERROR_ALLOW_MULTI(text, type, substring) \ CHECK_ERROR_OR_WARNING(text, type, substring, false, true) -// [checkWarning(text, type, substring)] asserts that the compilation down to typechecking -// emits a warning of type [type] and with a message containing [substring]. +// [checkWarning(text, substring)] asserts that the compilation down to typechecking +// emits a warning and with a message containing [substring]. #define CHECK_WARNING(text, substring) \ CHECK_ERROR_OR_WARNING(text, Warning, substring, true, false) +// [checkWarningAllowMulti(text, substring)] aserts that the compilation down to typechecking +// emits a warning and with a message containing [substring]. +#define CHECK_WARNING_ALLOW_MULTI(text, substring) \ +CHECK_ERROR_OR_WARNING(text, Warning, substring, true, true) + // [checkSuccess(text)] asserts that the compilation down to typechecking succeeds. #define CHECK_SUCCESS(text) do { BOOST_CHECK(success((text))); } while(0) @@ -548,6 +560,51 @@ BOOST_AUTO_TEST_CASE(comparison_bitop_precedence) CHECK_SUCCESS(text); } +BOOST_AUTO_TEST_CASE(comparison_of_function_types) +{ + char const* text = R"( + contract C { + function f() returns (bool ret) { + return this.f < this.f; + } + } + )"; + CHECK_ERROR(text, TypeError, "Operator < not compatible"); + text = R"( + contract C { + function f() returns (bool ret) { + return f < f; + } + } + )"; + CHECK_ERROR(text, TypeError, "Operator < not compatible"); + text = R"( + contract C { + function f() returns (bool ret) { + return f == f; + } + function g() returns (bool ret) { + return f != f; + } + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(comparison_of_mapping_types) +{ + char const* text = R"( + contract C { + mapping(uint => uint) x; + function f() returns (bool ret) { + var y = x; + return x == y; + } + } + )"; + CHECK_ERROR(text, TypeError, "Operator == not compatible"); +} + BOOST_AUTO_TEST_CASE(function_no_implementation) { ASTPointer<SourceUnit> sourceUnit; @@ -1043,6 +1100,28 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables) CHECK_SUCCESS(text); } +BOOST_AUTO_TEST_CASE(function_modifier_double_invocation) +{ + char const* text = R"( + contract B { + function f(uint x) mod(x) mod(2) { } + modifier mod(uint a) { if (a > 0) _; } + } + )"; + CHECK_ERROR(text, DeclarationError, "Modifier already used for this function"); +} + +BOOST_AUTO_TEST_CASE(base_constructor_double_invocation) +{ + char const* text = R"( + contract C { function C(uint a) {} } + contract B is C { + function B() C(2) C(2) {} + } + )"; + CHECK_ERROR(text, DeclarationError, "Base constructor already provided"); +} + BOOST_AUTO_TEST_CASE(legal_modifier_override) { char const* text = R"( @@ -1069,7 +1148,7 @@ BOOST_AUTO_TEST_CASE(modifier_overrides_function) )"; // Error: Identifier already declared. // Error: Override changes modifier to function. - CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, ""); + CHECK_ERROR_ALLOW_MULTI(text, DeclarationError, "Identifier already declared"); } BOOST_AUTO_TEST_CASE(function_overrides_modifier) @@ -1591,6 +1670,16 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter) CHECK_SUCCESS(text); } +BOOST_AUTO_TEST_CASE(constant_input_parameter) +{ + char const* text = R"( + contract test { + function f(uint[] constant a) { } + } + )"; + CHECK_ERROR_ALLOW_MULTI(text, TypeError, "Illegal use of \"constant\" specifier."); +} + BOOST_AUTO_TEST_CASE(empty_name_return_parameter) { char const* text = R"( @@ -1698,6 +1787,46 @@ BOOST_AUTO_TEST_CASE(exp_warn_literal_base) CHECK_SUCCESS(sourceCode); } + +BOOST_AUTO_TEST_CASE(warn_var_from_zero) +{ + char const* sourceCode = R"( + contract test { + function f() returns (uint) { + var i = 1; + return i; + } + } + )"; + CHECK_WARNING(sourceCode, "uint8, which can hold values between 0 and 255"); + sourceCode = R"( + contract test { + function f() { + var i = 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + i; + } + } + )"; + CHECK_WARNING(sourceCode, "uint256, which can hold values between 0 and 115792089237316195423570985008687907853269984665640564039457584007913129639935"); + sourceCode = R"( + contract test { + function f() { + var i = -2; + i; + } + } + )"; + CHECK_WARNING(sourceCode, "int8, which can hold values between -128 and 127"); + sourceCode = R"( + contract test { + function f() { + for (var i = 0; i < msg.data.length; i++) { } + } + } + )"; + CHECK_WARNING(sourceCode, "uint8, which can hold"); +} + BOOST_AUTO_TEST_CASE(enum_member_access) { char const* text = R"( @@ -2167,6 +2296,36 @@ BOOST_AUTO_TEST_CASE(test_byte_is_alias_of_byte1) ETH_TEST_REQUIRE_NO_THROW(parseAndAnalyse(text), "Type resolving failed"); } +BOOST_AUTO_TEST_CASE(warns_assigning_decimal_to_bytesxx) +{ + char const* text = R"( + contract Foo { + bytes32 a = 7; + } + )"; + CHECK_WARNING(text, "Decimal literal assigned to bytesXX variable will be left-aligned."); +} + +BOOST_AUTO_TEST_CASE(does_not_warn_assigning_hex_number_to_bytesxx) +{ + char const* text = R"( + contract Foo { + bytes32 a = 0x1234; + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(explicit_conversion_from_decimal_to_bytesxx) +{ + char const* text = R"( + contract Foo { + bytes32 a = bytes32(7); + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_CASE(assigning_value_to_const_variable) { char const* text = R"( @@ -2268,6 +2427,16 @@ BOOST_AUTO_TEST_CASE(constant_struct) CHECK_ERROR(text, TypeError, "implemented"); } +BOOST_AUTO_TEST_CASE(address_is_constant) +{ + char const* text = R"( + contract C { + address constant x = 0x1212121212121212121212121212121212121212; + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_CASE(uninitialized_const_variable) { char const* text = R"( @@ -2416,6 +2585,16 @@ BOOST_AUTO_TEST_CASE(invalid_utf8_explicit) CHECK_ERROR(sourceCode, TypeError, "Explicit type conversion not allowed"); } +BOOST_AUTO_TEST_CASE(large_utf8_codepoint) +{ + char const* sourceCode = R"( + contract C { + string s = "\xf0\x9f\xa6\x84"; + } + )"; + CHECK_SUCCESS(sourceCode); +} + BOOST_AUTO_TEST_CASE(string_index) { char const* sourceCode = R"( @@ -2933,12 +3112,12 @@ BOOST_AUTO_TEST_CASE(non_initialized_references) CHECK_WARNING(text, "Uninitialized storage pointer"); } -BOOST_AUTO_TEST_CASE(sha3_with_large_integer_constant) +BOOST_AUTO_TEST_CASE(keccak256_with_large_integer_constant) { char const* text = R"( contract c { - function f() { sha3(2**500); } + function f() { keccak256(2**500); } } )"; CHECK_ERROR(text, TypeError, ""); @@ -3042,9 +3221,9 @@ BOOST_AUTO_TEST_CASE(tuples) contract C { function f() { uint a = (1); - var (b,) = (1,); - var (c,d) = (1, 2 + a); - var (e,) = (1, 2, b); + var (b,) = (uint8(1),); + var (c,d) = (uint32(1), 2 + a); + var (e,) = (uint64(1), 2, b); a;b;c;d;e; } } @@ -3682,12 +3861,12 @@ BOOST_AUTO_TEST_CASE(conditional_with_all_types) byte[2] memory a; byte[2] memory b; var k = true ? a : b; - k[0] = 0; //Avoid unused var warning + k[0] = byte(0); //Avoid unused var warning bytes memory e; bytes memory f; var l = true ? e : f; - l[0] = 0; // Avoid unused var warning + l[0] = byte(0); // Avoid unused var warning // fixed bytes bytes2 c; @@ -4500,7 +4679,7 @@ BOOST_AUTO_TEST_CASE(unused_return_value_callcode) } } )"; - CHECK_WARNING(text, "Return value of low-level calls not used"); + CHECK_WARNING_ALLOW_MULTI(text, "Return value of low-level calls not used"); } BOOST_AUTO_TEST_CASE(unused_return_value_delegatecall) @@ -4515,6 +4694,31 @@ BOOST_AUTO_TEST_CASE(unused_return_value_delegatecall) CHECK_WARNING(text, "Return value of low-level calls not used"); } +BOOST_AUTO_TEST_CASE(warn_about_callcode) +{ + char const* text = R"( + contract test { + function f() { + var x = address(0x12).callcode; + x; + } + } + )"; + CHECK_WARNING(text, "\"callcode\" has been deprecated in favour"); +} + +BOOST_AUTO_TEST_CASE(no_warn_about_callcode_as_local) +{ + char const* text = R"( + contract test { + function callcode() { + var x = this.callcode; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_CASE(modifier_without_underscore) { char const* text = R"( @@ -4996,6 +5200,26 @@ BOOST_AUTO_TEST_CASE(external_function_type_to_uint) CHECK_ERROR(text, TypeError, "Explicit type conversion not allowed"); } +BOOST_AUTO_TEST_CASE(warn_function_type_parameters_with_names) +{ + char const* text = R"( + contract C { + function(uint a) f; + } + )"; + CHECK_WARNING(text, "Naming function type parameters is deprecated."); +} + +BOOST_AUTO_TEST_CASE(warn_function_type_return_parameters_with_names) +{ + char const* text = R"( + contract C { + function(uint) returns(bool ret) f; + } + )"; + CHECK_WARNING(text, "Naming function type return parameters is deprecated."); +} + BOOST_AUTO_TEST_CASE(shift_constant_left_negative_rvalue) { char const* text = R"( @@ -5158,6 +5382,52 @@ BOOST_AUTO_TEST_CASE(inline_assembly_constant_access) CHECK_ERROR(text, TypeError, "Constant variables not supported by inline assembly"); } +BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions) +{ + char const* text = R"( + contract test { + function f() { + uint a; + assembly { + function g() -> x { x := a } + } + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Cannot access local Solidity variables from inside an inline assembly function."); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_local_variable_access_out_of_functions_storage_ptr) +{ + char const* text = R"( + contract test { + uint[] r; + function f() { + uint[] storage a = r; + assembly { + function g() -> x { x := a_offset } + } + } + } + )"; + CHECK_ERROR(text, DeclarationError, "Cannot access local Solidity variables from inside an inline assembly function."); +} + +BOOST_AUTO_TEST_CASE(inline_assembly_storage_variable_access_out_of_functions) +{ + char const* text = R"( + contract test { + uint a; + function f() { + assembly { + function g() -> x { x := a_slot } + } + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_CASE(invalid_mobile_type) { char const* text = R"( @@ -5319,7 +5589,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_checksum) char const* text = R"( contract C { function f() { - var x = 0xFA0bFc97E48458494Ccd857e1A85DC91F7F0046E; + address x = 0xFA0bFc97E48458494Ccd857e1A85DC91F7F0046E; x; } } @@ -5332,7 +5602,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_no_checksum) char const* text = R"( contract C { function f() { - var x = 0xfa0bfc97e48458494ccd857e1a85dc91f7f0046e; + address x = 0xfa0bfc97e48458494ccd857e1a85dc91f7f0046e; x; } } @@ -5345,7 +5615,7 @@ BOOST_AUTO_TEST_CASE(invalid_address_length) char const* text = R"( contract C { function f() { - var x = 0xA0bFc97E48458494Ccd857e1A85DC91F7F0046E; + address x = 0xA0bFc97E48458494Ccd857e1A85DC91F7F0046E; x; } } @@ -5353,6 +5623,25 @@ BOOST_AUTO_TEST_CASE(invalid_address_length) CHECK_WARNING(text, "checksum"); } +BOOST_AUTO_TEST_CASE(address_test_for_bug_in_implementation) +{ + // A previous implementation claimed the string would be an address + char const* text = R"( + contract AddrString { + address public test = "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c"; + } + )"; + CHECK_ERROR(text, TypeError, "is not implicitly convertible to expected type address"); + text = R"( + contract AddrString { + function f() returns (address) { + return "0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c"; + } + } + )"; + CHECK_ERROR(text, TypeError, "is not implicitly convertible to expected type"); +} + BOOST_AUTO_TEST_CASE(early_exit_on_fatal_errors) { // This tests a crash that occured because we did not stop for fatal errors. @@ -5400,7 +5689,7 @@ BOOST_AUTO_TEST_CASE(cyclic_dependency_for_constants) contract C { uint constant a = b * c; uint constant b = 7; - uint constant c = b + uint(sha3(d)); + uint constant c = b + uint(keccak256(d)); uint constant d = 2 + a; } )"; @@ -5409,7 +5698,7 @@ BOOST_AUTO_TEST_CASE(cyclic_dependency_for_constants) contract C { uint constant a = b * c; uint constant b = 7; - uint constant c = 4 + uint(sha3(d)); + uint constant c = 4 + uint(keccak256(d)); uint constant d = 2 + b; } )"; @@ -5520,6 +5809,16 @@ BOOST_AUTO_TEST_CASE(interface_variables) CHECK_ERROR(text, TypeError, "Variables cannot be declared in interfaces"); } +BOOST_AUTO_TEST_CASE(interface_function_parameters) +{ + char const* text = R"( + interface I { + function f(uint a) returns(bool); + } + )"; + success(text); +} + BOOST_AUTO_TEST_CASE(interface_enums) { char const* text = R"( @@ -5608,6 +5907,80 @@ BOOST_AUTO_TEST_CASE(pure_statement_check_for_regular_for_loop) success(text); } +BOOST_AUTO_TEST_CASE(warn_multiple_storage_storage_copies) +{ + char const* text = R"( + contract C { + struct S { uint a; uint b; } + S x; S y; + function f() { + (x, y) = (y, x); + } + } + )"; + CHECK_WARNING(text, "This assignment performs two copies to storage."); +} + +BOOST_AUTO_TEST_CASE(warn_multiple_storage_storage_copies_fill_right) +{ + char const* text = R"( + contract C { + struct S { uint a; uint b; } + S x; S y; + function f() { + (x, y, ) = (y, x, 1, 2); + } + } + )"; + CHECK_WARNING(text, "This assignment performs two copies to storage."); +} + +BOOST_AUTO_TEST_CASE(warn_multiple_storage_storage_copies_fill_left) +{ + char const* text = R"( + contract C { + struct S { uint a; uint b; } + S x; S y; + function f() { + (,x, y) = (1, 2, y, x); + } + } + )"; + CHECK_WARNING(text, "This assignment performs two copies to storage."); +} + +BOOST_AUTO_TEST_CASE(nowarn_swap_memory) +{ + char const* text = R"( + contract C { + struct S { uint a; uint b; } + function f() { + S memory x; + S memory y; + (x, y) = (y, x); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(nowarn_swap_storage_pointers) +{ + char const* text = R"( + contract C { + struct S { uint a; uint b; } + S x; S y; + function f() { + S storage x_local = x; + S storage y_local = y; + S storage z_local = x; + (x, y_local, x_local, z_local) = (y, x_local, y_local, y); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_CASE(warn_unused_local) { char const* text = R"( @@ -5625,7 +5998,7 @@ BOOST_AUTO_TEST_CASE(warn_unused_local_assigned) char const* text = R"( contract C { function f() { - var a = 1; + uint a = 1; } } )"; @@ -5718,7 +6091,58 @@ BOOST_AUTO_TEST_CASE(no_unused_dec_after_use) CHECK_SUCCESS_NO_WARNINGS(text); } +BOOST_AUTO_TEST_CASE(no_unused_inline_asm) +{ + char const* text = R"( + contract C { + function f() { + uint a; + assembly { + a := 1 + } + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(callable_crash) +{ + char const* text = R"( + contract C { + struct S { uint a; bool x; } + S public s; + function C() { + 3({a: 1, x: true}); + } + } + )"; + CHECK_ERROR(text, TypeError, "Type is not callable"); +} + +BOOST_AUTO_TEST_CASE(returndatacopy_as_variable) +{ + char const* text = R"( + contract c { function f() { uint returndatasize; assembly { returndatasize }}} + )"; + CHECK_WARNING_ALLOW_MULTI(text, "Variable is shadowed in inline assembly by an instruction of the same name"); +} + +BOOST_AUTO_TEST_CASE(create2_as_variable) +{ + char const* text = R"( + contract c { function f() { uint create2; assembly { create2(0, 0, 0, 0) }}} + )"; + CHECK_WARNING_ALLOW_MULTI(text, "Variable is shadowed in inline assembly by an instruction of the same name"); +} +BOOST_AUTO_TEST_CASE(shadowing_warning_can_be_removed) +{ + char const* text = R"( + contract C {function f() {assembly {}}} + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/SolidityNatspecJSON.cpp b/test/libsolidity/SolidityNatspecJSON.cpp index 78c1a0ee..2a7376b9 100644 --- a/test/libsolidity/SolidityNatspecJSON.cpp +++ b/test/libsolidity/SolidityNatspecJSON.cpp @@ -49,9 +49,9 @@ public: Json::Value generatedDocumentation; if (_userDocumentation) - generatedDocumentation = m_compilerStack.metadata("", DocumentationType::NatspecUser); + generatedDocumentation = m_compilerStack.natspec("", DocumentationType::NatspecUser); else - generatedDocumentation = m_compilerStack.metadata("", DocumentationType::NatspecDev); + generatedDocumentation = m_compilerStack.natspec("", DocumentationType::NatspecDev); Json::Value expectedDocumentation; m_reader.parse(_expectedDocumentationString, expectedDocumentation); BOOST_CHECK_MESSAGE( diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index d705e3c8..a4d80c99 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -322,18 +322,18 @@ BOOST_AUTO_TEST_CASE(storage_write_in_loops) // Information in joining branches is not retained anymore. BOOST_AUTO_TEST_CASE(retain_information_in_branches) { - // This tests that the optimizer knows that we already have "z == sha3(y)" inside both branches. + // This tests that the optimizer knows that we already have "z == keccak256(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); + bytes32 z = keccak256(y); if (x > 8) { - z = sha3(y); + z = keccak256(y); a = x; } else { - z = sha3(y); + z = keccak256(y); a = x; } r_a = a; @@ -349,7 +349,7 @@ BOOST_AUTO_TEST_CASE(retain_information_in_branches) bytes optimizedBytecode = compileAndRunWithOptimizer(sourceCode, 0, "c", true); size_t numSHA3s = 0; eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) { - if (_instr == Instruction::SHA3) + if (_instr == Instruction::KECCAK256) numSHA3s++; }); // TEST DISABLED - OPTIMIZER IS NOT EFFECTIVE ON THIS ONE ANYMORE @@ -358,7 +358,7 @@ BOOST_AUTO_TEST_CASE(retain_information_in_branches) BOOST_AUTO_TEST_CASE(store_tags_as_unions) { - // This calls the same function from two sources and both calls have a certain sha3 on + // This calls the same function from two sources and both calls have a certain Keccak-256 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 @@ -370,19 +370,19 @@ BOOST_AUTO_TEST_CASE(store_tags_as_unions) contract test { bytes32 data; function f(uint x, bytes32 y) external returns (uint r_a, bytes32 r_d) { - r_d = sha3(y); + r_d = keccak256(y); shared(y); - r_d = sha3(y); + r_d = keccak256(y); r_a = 5; } function g(uint x, bytes32 y) external returns (uint r_a, bytes32 r_d) { - r_d = sha3(y); + r_d = keccak256(y); shared(y); - r_d = bytes32(uint(sha3(y)) + 2); + r_d = bytes32(uint(keccak256(y)) + 2); r_a = 7; } function shared(bytes32 y) internal { - data = sha3(y); + data = keccak256(y); } } )"; @@ -392,7 +392,7 @@ BOOST_AUTO_TEST_CASE(store_tags_as_unions) bytes optimizedBytecode = compileAndRunWithOptimizer(sourceCode, 0, "test", true); size_t numSHA3s = 0; eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) { - if (_instr == Instruction::SHA3) + if (_instr == Instruction::KECCAK256) numSHA3s++; }); // TEST DISABLED UNTIL 93693404 IS IMPLEMENTED @@ -401,8 +401,8 @@ BOOST_AUTO_TEST_CASE(store_tags_as_unions) BOOST_AUTO_TEST_CASE(incorrect_storage_access_bug) { - // This bug appeared because a sha3 operation with too low sequence number was used, - // resulting in memory not being rewritten before the sha3. The fix was to + // This bug appeared because a Keccak-256 operation with too low sequence number was used, + // resulting in memory not being rewritten before the Keccak-256. The fix was to // take the max of the min sequence numbers when merging the states. char const* sourceCode = R"( contract C @@ -697,59 +697,6 @@ BOOST_AUTO_TEST_CASE(cse_interleaved_storage_at_known_location_offset) }); } -BOOST_AUTO_TEST_CASE(cse_interleaved_memory_at_known_location_offset) -{ - // stores and reads to/from two locations which are known to be different, - // should not optimize away the first store, because the location overlaps with the load, - // but it should optimize away the second, because we know that the location is different by 32 - AssemblyItems input{ - u256(0x50), - Instruction::DUP2, - u256(2), - Instruction::ADD, - Instruction::MSTORE, // ["DUP1"+2] = 0x50 - u256(0x60), - Instruction::DUP2, - u256(32), - Instruction::ADD, - Instruction::MSTORE, // ["DUP1"+32] = 0x60 - Instruction::DUP1, - Instruction::MLOAD, // read from "DUP1" - u256(0x70), - Instruction::DUP3, - u256(32), - Instruction::ADD, - Instruction::MSTORE, // ["DUP1"+32] = 0x70 - u256(0x80), - Instruction::DUP3, - u256(2), - Instruction::ADD, - Instruction::MSTORE, // ["DUP1"+2] = 0x80 - }; - // If the actual code changes too much, we could also simply check that the output contains - // exactly 3 MSTORE and exactly 1 MLOAD instruction. - checkCSE(input, { - u256(0x50), - u256(2), - Instruction::DUP3, - Instruction::ADD, - Instruction::SWAP1, - Instruction::DUP2, - Instruction::MSTORE, // ["DUP1"+2] = 0x50 - Instruction::DUP2, - Instruction::MLOAD, // read from "DUP1" - u256(0x70), - u256(32), - Instruction::DUP5, - Instruction::ADD, - Instruction::MSTORE, // ["DUP1"+32] = 0x70 - u256(0x80), - Instruction::SWAP1, - Instruction::SWAP2, - Instruction::MSTORE // ["DUP1"+2] = 0x80 - }); -} - BOOST_AUTO_TEST_CASE(cse_deep_stack) { AssemblyItems input{ @@ -821,19 +768,19 @@ BOOST_AUTO_TEST_CASE(cse_jumpi_jump) }); } -BOOST_AUTO_TEST_CASE(cse_empty_sha3) +BOOST_AUTO_TEST_CASE(cse_empty_keccak256) { AssemblyItems input{ u256(0), Instruction::DUP2, - Instruction::SHA3 + Instruction::KECCAK256 }; checkCSE(input, { u256(dev::keccak256(bytesConstRef())) }); } -BOOST_AUTO_TEST_CASE(cse_partial_sha3) +BOOST_AUTO_TEST_CASE(cse_partial_keccak256) { AssemblyItems input{ u256(0xabcd) << (256 - 16), @@ -841,7 +788,7 @@ BOOST_AUTO_TEST_CASE(cse_partial_sha3) Instruction::MSTORE, u256(2), u256(0), - Instruction::SHA3 + Instruction::KECCAK256 }; checkCSE(input, { u256(0xabcd) << (256 - 16), @@ -851,19 +798,19 @@ BOOST_AUTO_TEST_CASE(cse_partial_sha3) }); } -BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_location) +BOOST_AUTO_TEST_CASE(cse_keccak256_twice_same_location) { - // sha3 twice from same dynamic location + // Keccak-256 twice from same dynamic location AssemblyItems input{ Instruction::DUP2, Instruction::DUP1, Instruction::MSTORE, u256(64), Instruction::DUP2, - Instruction::SHA3, + Instruction::KECCAK256, u256(64), Instruction::DUP3, - Instruction::SHA3 + Instruction::KECCAK256 }; checkCSE(input, { Instruction::DUP2, @@ -871,27 +818,27 @@ BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_location) Instruction::MSTORE, u256(64), Instruction::DUP2, - Instruction::SHA3, + Instruction::KECCAK256, Instruction::DUP1 }); } -BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content) +BOOST_AUTO_TEST_CASE(cse_keccak256_twice_same_content) { - // sha3 twice from different dynamic location but with same content + // Keccak-256 twice from different dynamic location but with same content AssemblyItems input{ Instruction::DUP1, u256(0x80), Instruction::MSTORE, // m[128] = DUP1 u256(0x20), u256(0x80), - Instruction::SHA3, // sha3(m[128..(128+32)]) + Instruction::KECCAK256, // keccak256(m[128..(128+32)]) Instruction::DUP2, u256(12), Instruction::MSTORE, // m[12] = DUP1 u256(0x20), u256(12), - Instruction::SHA3 // sha3(m[12..(12+32)]) + Instruction::KECCAK256 // keccak256(m[12..(12+32)]) }; checkCSE(input, { u256(0x80), @@ -900,7 +847,7 @@ BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content) Instruction::MSTORE, u256(0x20), Instruction::SWAP1, - Instruction::SHA3, + Instruction::KECCAK256, u256(12), Instruction::DUP3, Instruction::SWAP1, @@ -909,10 +856,10 @@ BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content) }); } -BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_dynamic_store_in_between) +BOOST_AUTO_TEST_CASE(cse_keccak256_twice_same_content_dynamic_store_in_between) { - // sha3 twice from different dynamic location but with same content, - // dynamic mstore in between, which forces us to re-calculate the sha3 + // Keccak-256 twice from different dynamic location but with same content, + // dynamic mstore in between, which forces us to re-calculate the hash AssemblyItems input{ u256(0x80), Instruction::DUP2, @@ -921,7 +868,7 @@ BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_dynamic_store_in_between) u256(0x20), Instruction::DUP1, Instruction::DUP3, - Instruction::SHA3, // sha3(m[128..(128+32)]) + Instruction::KECCAK256, // keccak256(m[128..(128+32)]) u256(12), Instruction::DUP5, Instruction::DUP2, @@ -932,15 +879,15 @@ BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_dynamic_store_in_between) Instruction::SWAP2, Instruction::SWAP1, Instruction::SWAP2, - Instruction::SHA3 // sha3(m[12..(12+32)]) + Instruction::KECCAK256 // keccak256(m[12..(12+32)]) }; checkCSE(input, input); } -BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_noninterfering_store_in_between) +BOOST_AUTO_TEST_CASE(cse_keccak256_twice_same_content_noninterfering_store_in_between) { - // sha3 twice from different dynamic location but with same content, - // dynamic mstore in between, but does not force us to re-calculate the sha3 + // Keccak-256 twice from different dynamic location but with same content, + // dynamic mstore in between, but does not force us to re-calculate the hash AssemblyItems input{ u256(0x80), Instruction::DUP2, @@ -949,7 +896,7 @@ BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_noninterfering_store_in_between u256(0x20), Instruction::DUP1, Instruction::DUP3, - Instruction::SHA3, // sha3(m[128..(128+32)]) + Instruction::KECCAK256, // keccak256(m[128..(128+32)]) u256(12), Instruction::DUP5, Instruction::DUP2, @@ -962,12 +909,12 @@ BOOST_AUTO_TEST_CASE(cse_sha3_twice_same_content_noninterfering_store_in_between Instruction::MSTORE, // does not destoy memory knowledge u256(0x20), u256(12), - Instruction::SHA3 // sha3(m[12..(12+32)]) + Instruction::KECCAK256 // keccak256(m[12..(12+32)]) }; // if this changes too often, only count the number of SHA3 and MSTORE instructions AssemblyItems output = CSE(input); BOOST_CHECK_EQUAL(4, count(output.begin(), output.end(), AssemblyItem(Instruction::MSTORE))); - BOOST_CHECK_EQUAL(1, count(output.begin(), output.end(), AssemblyItem(Instruction::SHA3))); + BOOST_CHECK_EQUAL(1, count(output.begin(), output.end(), AssemblyItem(Instruction::KECCAK256))); } BOOST_AUTO_TEST_CASE(cse_with_initially_known_stack) @@ -1189,6 +1136,32 @@ BOOST_AUTO_TEST_CASE(clear_unreachable_code) ); } +BOOST_AUTO_TEST_CASE(peephole_double_push) +{ + AssemblyItems items{ + u256(0), + u256(0), + u256(5), + u256(5), + u256(4), + u256(5) + }; + AssemblyItems expectation{ + u256(0), + Instruction::DUP1, + u256(5), + Instruction::DUP1, + u256(4), + u256(5) + }; + PeepholeOptimiser peepOpt(items); + BOOST_REQUIRE(peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( + items.begin(), items.end(), + expectation.begin(), expectation.end() + ); +} + BOOST_AUTO_TEST_CASE(computing_constants) { char const* sourceCode = R"( @@ -1296,7 +1269,7 @@ BOOST_AUTO_TEST_CASE(constant_optimization_early_exit) // Store and hash assembly { mstore(32, x) - ret := sha3(0, 40) + ret := keccak256(0, 40) } } } diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 6e33aba5..78edd4d1 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -24,7 +24,7 @@ #include <memory> #include <libsolidity/parsing/Scanner.h> #include <libsolidity/parsing/Parser.h> -#include <libsolidity/interface/Exceptions.h> +#include <libsolidity/interface/ErrorReporter.h> #include "../TestHelper.h" #include "ErrorCheck.h" @@ -41,7 +41,8 @@ namespace { ASTPointer<ContractDefinition> parseText(std::string const& _source, ErrorList& _errors) { - ASTPointer<SourceUnit> sourceUnit = Parser(_errors).parse(std::make_shared<Scanner>(CharStream(_source))); + ErrorReporter errorReporter(_errors); + ASTPointer<SourceUnit> sourceUnit = Parser(errorReporter).parse(std::make_shared<Scanner>(CharStream(_source))); if (!sourceUnit) return ASTPointer<ContractDefinition>(); for (ASTPointer<ASTNode> const& node: sourceUnit->nodes()) @@ -198,6 +199,17 @@ BOOST_AUTO_TEST_CASE(missing_argument_in_named_args) CHECK_PARSE_ERROR(text, "Expected primary expression"); } +BOOST_AUTO_TEST_CASE(trailing_comma_in_named_args) +{ + char const* text = R"( + contract test { + function a(uint a, uint b, uint c) returns (uint r) { r = a * 100 + b * 10 + c * 1; } + function b() returns (uint r) { r = a({a: 1, b: 2, c: 3, }); } + } + )"; + CHECK_PARSE_ERROR(text, "Unexpected trailing comma"); +} + BOOST_AUTO_TEST_CASE(two_exact_functions) { char const* text = R"( @@ -889,6 +901,24 @@ BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers) CHECK_PARSE_ERROR(text, "Visibility already specified"); } +BOOST_AUTO_TEST_CASE(multiple_payable_specifiers) +{ + char const* text = R"( + contract c { + function f() payable payable {} + })"; + CHECK_PARSE_ERROR(text, "Multiple \"payable\" specifiers."); +} + +BOOST_AUTO_TEST_CASE(multiple_constant_specifiers) +{ + char const* text = R"( + contract c { + function f() constant constant {} + })"; + CHECK_PARSE_ERROR(text, "Multiple \"constant\" specifiers."); +} + BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations) { char const* text = R"( diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index ffb0e2c6..be13d46b 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -26,6 +26,7 @@ #include <libsolidity/interface/StandardCompiler.h> #include <libdevcore/JSON.h> +#include "../Metadata.h" using namespace std; using namespace dev::eth; @@ -68,60 +69,12 @@ bool containsAtMostWarnings(Json::Value const& _compilerResult) BOOST_REQUIRE(error.isObject()); BOOST_REQUIRE(error["severity"].isString()); if (error["severity"].asString() != "warning") - { - cout << error << std::endl; return false; - } } return true; } -string bytecodeSansMetadata(string const& _bytecode) -{ - /// The metadata hash takes up 43 bytes (or 86 characters in hex) - /// /a165627a7a72305820([0-9a-f]{64})0029$/ - - if (_bytecode.size() < 88) - return _bytecode; - - if (_bytecode.substr(_bytecode.size() - 4, 4) != "0029") - return _bytecode; - - if (_bytecode.substr(_bytecode.size() - 86, 18) != "a165627a7a72305820") - return _bytecode; - - return _bytecode.substr(0, _bytecode.size() - 86); -} - -bool isValidMetadata(string const& _metadata) -{ - Json::Value metadata; - if (!Json::Reader().parse(_metadata, metadata, false)) - return false; - - if ( - !metadata.isObject() || - !metadata.isMember("version") || - !metadata.isMember("language") || - !metadata.isMember("compiler") || - !metadata.isMember("settings") || - !metadata.isMember("sources") || - !metadata.isMember("output") - ) - return false; - - if (!metadata["version"].isNumeric() || metadata["version"] != 1) - return false; - - if (!metadata["language"].isString() || metadata["language"].asString() != "Solidity") - return false; - - /// @TODO add more strict checks - - return true; -} - Json::Value getContractResult(Json::Value const& _compilerResult, string const& _file, string const& _name) { if ( @@ -236,34 +189,43 @@ BOOST_AUTO_TEST_CASE(basic_compilation) Json::Value contract = getContractResult(result, "fileA", "A"); BOOST_CHECK(contract.isObject()); BOOST_CHECK(contract["abi"].isArray()); - BOOST_CHECK(dev::jsonCompactPrint(contract["abi"]) == "[]"); + BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["abi"]), "[]"); BOOST_CHECK(contract["devdoc"].isObject()); - BOOST_CHECK(dev::jsonCompactPrint(contract["devdoc"]) == "{\"methods\":{}}"); + BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["devdoc"]), "{\"methods\":{}}"); BOOST_CHECK(contract["userdoc"].isObject()); - BOOST_CHECK(dev::jsonCompactPrint(contract["userdoc"]) == "{\"methods\":{}}"); + BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["userdoc"]), "{\"methods\":{}}"); BOOST_CHECK(contract["evm"].isObject()); /// @TODO check evm.methodIdentifiers, legacyAssembly, bytecode, deployedBytecode BOOST_CHECK(contract["evm"]["bytecode"].isObject()); BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString()); - BOOST_CHECK(bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()) == - "60606040523415600b57fe5b5b60338060196000396000f30060606040525bfe00"); + BOOST_CHECK_EQUAL( + dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()), + "60606040523415600e57600080fd5b5b603680601c6000396000f30060606040525b600080fd00" + ); BOOST_CHECK(contract["evm"]["assembly"].isString()); - BOOST_CHECK(contract["evm"]["assembly"].asString() == + BOOST_CHECK(contract["evm"]["assembly"].asString().find( " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x60)\n jumpi(tag_1, iszero(callvalue))\n" - " invalid\ntag_1:\ntag_2:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n" + " 0x0\n dup1\n revert\ntag_1:\ntag_2:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n" " return\nstop\n\nsub_0: assembly {\n /* \"fileA\":0:14 contract A { } */\n" - " mstore(0x40, 0x60)\n tag_1:\n invalid\n}\n"); + " mstore(0x40, 0x60)\n tag_1:\n 0x0\n dup1\n revert\n\n" + " auxdata: 0xa165627a7a7230582") == 0); BOOST_CHECK(contract["evm"]["gasEstimates"].isObject()); - BOOST_CHECK(dev::jsonCompactPrint(contract["evm"]["gasEstimates"]) == - "{\"creation\":{\"codeDepositCost\":\"10200\",\"executionCost\":\"62\",\"totalCost\":\"10262\"}}"); + BOOST_CHECK_EQUAL( + dev::jsonCompactPrint(contract["evm"]["gasEstimates"]), + "{\"creation\":{\"codeDepositCost\":\"10800\",\"executionCost\":\"62\",\"totalCost\":\"10862\"}}" + ); BOOST_CHECK(contract["metadata"].isString()); - BOOST_CHECK(isValidMetadata(contract["metadata"].asString())); + BOOST_CHECK(dev::test::isValidMetadata(contract["metadata"].asString())); BOOST_CHECK(result["sources"].isObject()); BOOST_CHECK(result["sources"]["fileA"].isObject()); BOOST_CHECK(result["sources"]["fileA"]["legacyAST"].isObject()); - BOOST_CHECK(dev::jsonCompactPrint(result["sources"]["fileA"]["legacyAST"]) == - "{\"children\":[{\"attributes\":{\"fullyImplemented\":true,\"isLibrary\":false,\"linearizedBaseContracts\":[1]," - "\"name\":\"A\"},\"children\":[],\"id\":1,\"name\":\"ContractDefinition\",\"src\":\"0:14:0\"}],\"name\":\"SourceUnit\"}"); + BOOST_CHECK_EQUAL( + dev::jsonCompactPrint(result["sources"]["fileA"]["legacyAST"]), + "{\"attributes\":{\"absolutePath\":\"fileA\",\"exportedSymbols\":{\"A\":[1]}},\"children\":" + "[{\"attributes\":{\"baseContracts\":[null],\"contractDependencies\":[null],\"contractKind\":\"contract\"," + "\"documentation\":null,\"fullyImplemented\":true,\"linearizedBaseContracts\":[1],\"name\":\"A\",\"nodes\":[null],\"scope\":2}," + "\"id\":1,\"name\":\"ContractDefinition\",\"src\":\"0:14:0\"}],\"id\":2,\"name\":\"SourceUnit\",\"src\":\"0:14:0\"}" + ); } BOOST_AUTO_TEST_SUITE_END() |