diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/CMakeLists.txt | 4 | ||||
-rw-r--r-- | test/ExecutionFramework.h | 16 | ||||
-rw-r--r-- | test/boostTest.cpp | 1 | ||||
-rwxr-xr-x | test/externalTests.sh | 47 | ||||
-rw-r--r-- | test/fuzzer.cpp | 2 | ||||
-rw-r--r-- | test/libjulia/Common.cpp | 87 | ||||
-rw-r--r-- | test/libjulia/Common.h | 55 | ||||
-rw-r--r-- | test/libjulia/Disambiguator.cpp | 105 | ||||
-rw-r--r-- | test/libjulia/FunctionGrouper.cpp | 85 | ||||
-rw-r--r-- | test/libjulia/FunctionHoister.cpp | 85 | ||||
-rw-r--r-- | test/libjulia/Parser.cpp | 12 | ||||
-rw-r--r-- | test/libsolidity/ABIDecoderTests.cpp | 794 | ||||
-rw-r--r-- | test/libsolidity/ABIEncoderTests.cpp | 50 | ||||
-rw-r--r-- | test/libsolidity/ABITestsCommon.h | 43 | ||||
-rw-r--r-- | test/libsolidity/Assembly.cpp | 54 | ||||
-rw-r--r-- | test/libsolidity/InlineAssembly.cpp | 91 | ||||
-rw-r--r-- | test/libsolidity/JSONCompiler.cpp | 2 | ||||
-rw-r--r-- | test/libsolidity/SMTChecker.cpp | 146 | ||||
-rw-r--r-- | test/libsolidity/SolidityABIJSON.cpp | 3 | ||||
-rw-r--r-- | test/libsolidity/SolidityEndToEndTest.cpp | 6 | ||||
-rw-r--r-- | test/libsolidity/SolidityNameAndTypeResolution.cpp | 46 | ||||
-rw-r--r-- | test/libsolidity/SolidityParser.cpp | 10 | ||||
-rw-r--r-- | test/libsolidity/StandardCompiler.cpp | 113 |
23 files changed, 1719 insertions, 138 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6a8a4399..f36ad4c5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -3,7 +3,7 @@ list(REMOVE_ITEM sources "${CMAKE_CURRENT_SOURCE_DIR}/fuzzer.cpp") file(GLOB_RECURSE headers "*.h") add_executable(soltest ${sources} ${headers}) -target_link_libraries(soltest PRIVATE soljson solidity lll evmasm devcore ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) +target_link_libraries(soltest PRIVATE libsolc solidity lll evmasm devcore ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) add_executable(solfuzzer fuzzer.cpp) -target_link_libraries(solfuzzer soljson evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES}) +target_link_libraries(solfuzzer PRIVATE libsolc evmasm ${Boost_PROGRAM_OPTIONS_LIBRARIES}) diff --git a/test/ExecutionFramework.h b/test/ExecutionFramework.h index 2c61c0a6..8aa99473 100644 --- a/test/ExecutionFramework.h +++ b/test/ExecutionFramework.h @@ -84,14 +84,24 @@ public: return callFallbackWithValue(0); } - template <class... Args> - bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, Args const&... _arguments) + bytes const& callContractFunctionWithValueNoEncoding(std::string _sig, u256 const& _value, bytes const& _arguments) { FixedHash<4> hash(dev::keccak256(_sig)); - sendMessage(hash.asBytes() + encodeArgs(_arguments...), false, _value); + sendMessage(hash.asBytes() + _arguments, false, _value); return m_output; } + bytes const& callContractFunctionNoEncoding(std::string _sig, bytes const& _arguments) + { + return callContractFunctionWithValueNoEncoding(_sig, 0, _arguments); + } + + template <class... Args> + bytes const& callContractFunctionWithValue(std::string _sig, u256 const& _value, Args const&... _arguments) + { + return callContractFunctionWithValueNoEncoding(_sig, _value, encodeArgs(_arguments...)); + } + template <class... Args> bytes const& callContractFunction(std::string _sig, Args const&... _arguments) { diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 7b452e06..a3cc51c5 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -57,6 +57,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) if (dev::test::Options::get().disableIPC) { for (auto suite: { + "ABIDecoderTest", "ABIEncoderTest", "SolidityAuctionRegistrar", "SolidityFixedFeeRegistrar", diff --git a/test/externalTests.sh b/test/externalTests.sh index 1cc0af19..11972eae 100755 --- a/test/externalTests.sh +++ b/test/externalTests.sh @@ -44,53 +44,6 @@ DIR=$(mktemp -d) npm install find . -name soljson.js -exec cp "$SOLJSON" {} \; - # This is a patch that lets truffle ignore the pre-release compiler warning - cat > truffle.patch <<EOF ---- node_modules/truffle/build/cli.bundled.js 2017-11-27 16:56:47.114830112 +0100 -+++ /tmp/patched 2017-11-27 16:52:31.887064115 +0100 -@@ -313846,9 +313846,12 @@ - }); - - output = JSON.parse(output); -+ var errors = output.errors.filter(function(solidity_error) { -+ return solidity_error.formattedMessage.indexOf("pre-release compiler") < 0; -+ }); - -- if (output.errors) { -- throw new CompileError(output.errors[0].formattedMessage); -+ if (errors) { -+ throw new CompileError(errors[0].formattedMessage); - } - - return { -@@ -313901,9 +313904,13 @@ - return {error: importErrorKey}; - }); - -- output = JSON.parse(output); -+ output = JSON.parse(output); -+ -+ var errors = output.errors.filter(function(solidity_error) { -+ return solidity_error.formattedMessage.indexOf("pre-release compiler") < 0; -+ }); - -- var nonImportErrors = output.errors.filter(function(solidity_error) { -+ var nonImportErrors = errors.filter(function(solidity_error) { - // If the import error key is not found, we must not have an import error. - // This means we have a *different* parsing error which we should show to the user. - // Note: solc can return multiple parsing errors at once. -@@ -313917,7 +313924,7 @@ - - // Now, all errors must be import errors. - // Filter out our forced import, then get the import paths of the rest. -- var imports = output.errors.filter(function(solidity_error) { -+ var imports = errors.filter(function(solidity_error) { - return solidity_error.message.indexOf(failingImportFileName) < 0; - }).map(function(solidity_error) { - var matches = solidity_error.formattedMessage.match(/import[^'"]+("|')([^'"]+)("|');/); -EOF - - patch node_modules/truffle/build/cli.bundled.js ./truffle.patch npm run test ) rm -rf "$DIR" diff --git a/test/fuzzer.cpp b/test/fuzzer.cpp index 53ba7201..578e63a4 100644 --- a/test/fuzzer.cpp +++ b/test/fuzzer.cpp @@ -20,7 +20,7 @@ #include <libevmasm/Assembly.h> #include <libevmasm/ConstantOptimiser.h> -#include <solc/jsonCompiler.h> +#include <libsolc/libsolc.h> #include <json/json.h> diff --git a/test/libjulia/Common.cpp b/test/libjulia/Common.cpp new file mode 100644 index 00000000..e1ab8215 --- /dev/null +++ b/test/libjulia/Common.cpp @@ -0,0 +1,87 @@ +/* + 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 + * Common functions the iulia tests. + */ + +#include <test/libjulia/Common.h> + +#include <libjulia/optimiser/Disambiguator.h> + +#include <libsolidity/parsing/Scanner.h> + +#include <libsolidity/inlineasm/AsmParser.h> +#include <libsolidity/inlineasm/AsmAnalysis.h> +#include <libsolidity/inlineasm/AsmPrinter.h> + +#include <libsolidity/interface/SourceReferenceFormatter.h> +#include <libsolidity/interface/ErrorReporter.h> + +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace dev::julia; +using namespace dev::solidity; + +void dev::julia::test::printErrors(ErrorList const& _errors, Scanner const& _scanner) +{ + for (auto const& error: _errors) + SourceReferenceFormatter::printExceptionInformation( + cout, + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error", + [&](std::string const&) -> Scanner const& { return _scanner; } + ); +} + + +pair<shared_ptr<Block>, shared_ptr<assembly::AsmAnalysisInfo>> dev::julia::test::parse(string const& _source, bool _julia) +{ + auto flavour = _julia ? assembly::AsmFlavour::IULIA : assembly::AsmFlavour::Strict; + ErrorList errors; + ErrorReporter errorReporter(errors); + auto scanner = make_shared<Scanner>(CharStream(_source), ""); + auto parserResult = assembly::Parser(errorReporter, flavour).parse(scanner); + if (parserResult) + { + BOOST_REQUIRE(errorReporter.errors().empty()); + auto analysisInfo = make_shared<assembly::AsmAnalysisInfo>(); + assembly::AsmAnalyzer analyzer(*analysisInfo, errorReporter, flavour); + if (analyzer.analyze(*parserResult)) + { + BOOST_REQUIRE(errorReporter.errors().empty()); + return make_pair(parserResult, analysisInfo); + } + } + printErrors(errors, *scanner); + BOOST_FAIL("Invalid source."); + + // Unreachable. + return {}; +} + +assembly::Block dev::julia::test::disambiguate(string const& _source, bool _julia) +{ + auto result = parse(_source, _julia); + return boost::get<Block>(Disambiguator(*result.second)(*result.first)); +} + +string dev::julia::test::format(string const& _source, bool _julia) +{ + return assembly::AsmPrinter(_julia)(*parse(_source, _julia).first); +} diff --git a/test/libjulia/Common.h b/test/libjulia/Common.h new file mode 100644 index 00000000..1371101c --- /dev/null +++ b/test/libjulia/Common.h @@ -0,0 +1,55 @@ +/* + 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 + * Common functions the iulia tests. + */ + +#pragma once + +#include <libsolidity/inlineasm/AsmData.h> + +#include <string> +#include <vector> +#include <memory> + +namespace dev +{ +namespace solidity +{ +class Scanner; +class Error; +using ErrorList = std::vector<std::shared_ptr<Error const>>; +namespace assembly +{ +struct AsmAnalysisInfo; +} +} +namespace julia +{ +namespace test +{ + +void printErrors(solidity::ErrorList const& _errors, solidity::Scanner const& _scanner); +std::pair<std::shared_ptr<solidity::assembly::Block>, std::shared_ptr<solidity::assembly::AsmAnalysisInfo>> +parse(std::string const& _source, bool _julia = true); +solidity::assembly::Block disambiguate(std::string const& _source, bool _julia = true); +std::string format(std::string const& _source, bool _julia = true); + +} +} +} diff --git a/test/libjulia/Disambiguator.cpp b/test/libjulia/Disambiguator.cpp new file mode 100644 index 00000000..a6338449 --- /dev/null +++ b/test/libjulia/Disambiguator.cpp @@ -0,0 +1,105 @@ +/* + 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 iulia name disambiguator. + */ + +#include <test/libjulia/Common.h> + +#include <libsolidity/inlineasm/AsmPrinter.h> + +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace dev::julia::test; +using namespace dev::solidity; + +#define CHECK(_original, _expectation)\ +do\ +{\ + assembly::AsmPrinter p(true);\ + string result = p(disambiguate(_original));\ + BOOST_CHECK_EQUAL(result, format(_expectation));\ + BOOST_CHECK_EQUAL(result, p(disambiguate(result)));\ +}\ +while(false) + +BOOST_AUTO_TEST_SUITE(IuliaDisambiguator) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + CHECK("{ }", "{ }"); +} + +BOOST_AUTO_TEST_CASE(variables) +{ + CHECK( + "{ { let a:u256 } { let a:u256 } }", + "{ { let a:u256 } { let a_1:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(variables_clash) +{ + CHECK( + "{ { let a:u256 let a_1:u256 } { let a:u256 } }", + "{ { let a:u256 let a_1:u256 } { let a_2:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(variables_inside_functions) +{ + CHECK( + "{ { let c:u256 let b:u256 } function f(a:u256, c:u256) -> b:u256 { let x:u256 } { let a:u256 let x:u256 } }", + "{ { let c:u256 let b:u256 } function f(a:u256, c_1:u256) -> b_1:u256 { let x:u256 } { let a_1:u256 let x_1:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(function_call) +{ + CHECK( + "{ { let a:u256, b:u256, c:u256, d:u256, f:u256 } { function f(a:u256) -> c:u256, d:u256 { let b:u256, c_1:u256 := f(a) } } }", + "{ { let a:u256, b:u256, c:u256, d:u256, f:u256 } { function f_1(a_1:u256) -> c_1:u256, d_1:u256 { let b_1:u256, c_1_1:u256 := f_1(a_1) } } }" + ); +} + +BOOST_AUTO_TEST_CASE(for_statement) +{ + CHECK( + "{ { let a:u256, b:u256 } { for { let a:u256 } a { a := a } { let b:u256 := a } } }", + "{ { let a:u256, b:u256 } { for { let a_1:u256 } a_1 { a_1 := a_1 } { let b_1:u256 := a_1 } } }" + ); +} + +BOOST_AUTO_TEST_CASE(switch_statement) +{ + CHECK( + "{ { let a:u256, b:u256, c:u256 } { let a:u256 switch a case 0:u256 { let b:u256 := a } default { let c:u256 := a } } }", + "{ { let a:u256, b:u256, c:u256 } { let a_1:u256 switch a_1 case 0:u256 { let b_1:u256 := a_1 } default { let c_1:u256 := a_1 } } }" + ); +} + +BOOST_AUTO_TEST_CASE(if_statement) +{ + CHECK( + "{ { let a:u256, b:u256, c:u256 } { let a:bool if a { let b:bool := a } } }", + "{ { let a:u256, b:u256, c:u256 } { let a_1:bool if a_1 { let b_1:bool := a_1 } } }" + ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/FunctionGrouper.cpp b/test/libjulia/FunctionGrouper.cpp new file mode 100644 index 00000000..78f382cb --- /dev/null +++ b/test/libjulia/FunctionGrouper.cpp @@ -0,0 +1,85 @@ +/* + 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 iulia function grouper. + */ + +#include <test/libjulia/Common.h> + +#include <libjulia/optimiser/FunctionGrouper.h> + +#include <libsolidity/inlineasm/AsmPrinter.h> + +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace dev::julia; +using namespace dev::julia::test; +using namespace dev::solidity; + +#define CHECK(_original, _expectation)\ +do\ +{\ + assembly::AsmPrinter p(true);\ + Block b = disambiguate(_original);\ + (FunctionGrouper{})(b);\ + string result = p(b);\ + BOOST_CHECK_EQUAL(result, format(_expectation));\ +}\ +while(false) + +BOOST_AUTO_TEST_SUITE(IuliaFunctionGrouper) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + CHECK("{ }", "{ { } }"); +} + +BOOST_AUTO_TEST_CASE(single_fun) +{ + CHECK( + "{ let a:u256 function f() {} }", + "{ { let a:u256 } function f() {} }" + ); +} + +BOOST_AUTO_TEST_CASE(multi_fun_mixed) +{ + CHECK( + "{ let a:u256 function f() { let b:u256 } let c:u256 function g() { let d:u256 } let e:u256 }", + "{ { let a:u256 let c:u256 let e:u256 } function f() { let b:u256 } function g() { let d:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(nested_fun) +{ + CHECK( + "{ let a:u256 function f() { let b:u256 function g() { let c:u256} let d:u256 } }", + "{ { let a:u256 } function f() { let b:u256 function g() { let c:u256} let d:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(empty_block) +{ + CHECK( + "{ let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }", + "{ { let a:u256 { } } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }" + ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/FunctionHoister.cpp b/test/libjulia/FunctionHoister.cpp new file mode 100644 index 00000000..3d6fff85 --- /dev/null +++ b/test/libjulia/FunctionHoister.cpp @@ -0,0 +1,85 @@ +/* + 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 iulia function hoister. + */ + +#include <test/libjulia/Common.h> + +#include <libjulia/optimiser/FunctionHoister.h> + +#include <libsolidity/inlineasm/AsmPrinter.h> + +#include <boost/test/unit_test.hpp> + +using namespace std; +using namespace dev::julia; +using namespace dev::julia::test; +using namespace dev::solidity; + +#define CHECK(_original, _expectation)\ +do\ +{\ + assembly::AsmPrinter p(true);\ + Block b = disambiguate(_original);\ + (FunctionHoister{})(b);\ + string result = p(b);\ + BOOST_CHECK_EQUAL(result, format(_expectation));\ +}\ +while(false) + +BOOST_AUTO_TEST_SUITE(IuliaFunctionHoister) + +BOOST_AUTO_TEST_CASE(smoke_test) +{ + CHECK("{ }", "{ }"); +} + +BOOST_AUTO_TEST_CASE(single_fun) +{ + CHECK( + "{ let a:u256 function f() {} }", + "{ let a:u256 function f() {} }" + ); +} + +BOOST_AUTO_TEST_CASE(multi_fun_mixed) +{ + CHECK( + "{ let a:u256 function f() { let b:u256 } let c:u256 function g() { let d:u256 } let e:u256 }", + "{ let a:u256 let c:u256 let e:u256 function f() { let b:u256 } function g() { let d:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(nested_fun) +{ + CHECK( + "{ let a:u256 function f() { let b:u256 function g() { let c:u256} let d:u256 } }", + "{ let a:u256 function g() { let c:u256 } function f() { let b:u256 let d:u256 } }" + ); +} + +BOOST_AUTO_TEST_CASE(empty_block) +{ + CHECK( + "{ let a:u256 { } function f() -> x:bool { let b:u256 := 4:u256 {} for {} f() {} {} } }", + "{ let a:u256 function f() -> x:bool { let b:u256 := 4:u256 for {} f() {} {} } }" + ); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libjulia/Parser.cpp b/test/libjulia/Parser.cpp index 9aa325a4..a8a41b3c 100644 --- a/test/libjulia/Parser.cpp +++ b/test/libjulia/Parser.cpp @@ -52,11 +52,11 @@ bool parse(string const& _source, ErrorReporter& errorReporter) try { auto scanner = make_shared<Scanner>(CharStream(_source)); - auto parserResult = assembly::Parser(errorReporter, true).parse(scanner); + auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::IULIA).parse(scanner); if (parserResult) { assembly::AsmAnalysisInfo analysisInfo; - return (assembly::AsmAnalyzer(analysisInfo, errorReporter, true)).analyze(*parserResult); + return (assembly::AsmAnalyzer(analysisInfo, errorReporter, assembly::AsmFlavour::IULIA)).analyze(*parserResult); } } catch (FatalError const&) @@ -196,6 +196,14 @@ BOOST_AUTO_TEST_CASE(empty_call) CHECK_ERROR("{ () }", ParserError, "Literal or identifier expected."); } +BOOST_AUTO_TEST_CASE(tokens_as_identifers) +{ + BOOST_CHECK(successParse("{ let return:u256 := 1:u256 }")); + BOOST_CHECK(successParse("{ let byte:u256 := 1:u256 }")); + BOOST_CHECK(successParse("{ let address:u256 := 1:u256 }")); + BOOST_CHECK(successParse("{ let bool:u256 := 1:u256 }")); +} + BOOST_AUTO_TEST_CASE(lacking_types) { CHECK_ERROR("{ let x := 1:u256 }", ParserError, "Expected token Identifier got 'Assign'"); diff --git a/test/libsolidity/ABIDecoderTests.cpp b/test/libsolidity/ABIDecoderTests.cpp new file mode 100644 index 00000000..15c04b37 --- /dev/null +++ b/test/libsolidity/ABIDecoderTests.cpp @@ -0,0 +1,794 @@ +/* + 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 Solidity's ABI decoder. + */ + +#include <functional> +#include <string> +#include <tuple> +#include <boost/test/unit_test.hpp> +#include <libsolidity/interface/Exceptions.h> +#include <test/libsolidity/SolidityExecutionFramework.h> + +#include <test/libsolidity/ABITestsCommon.h> + +using namespace std; +using namespace std::placeholders; +using namespace dev::test; + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +BOOST_FIXTURE_TEST_SUITE(ABIDecoderTest, SolidityExecutionFramework) + +BOOST_AUTO_TEST_CASE(both_encoders_macro) +{ + // This tests that the "both decoders macro" at least runs twice and + // modifies the source. + string sourceCode; + int runs = 0; + BOTH_ENCODERS(runs++;) + BOOST_CHECK(sourceCode == NewEncoderPragma); + BOOST_CHECK_EQUAL(runs, 2); +} + +BOOST_AUTO_TEST_CASE(value_types) +{ + string sourceCode = R"( + contract C { + function f(uint a, uint16 b, uint24 c, int24 d, bytes3 x, bool e, C g) public returns (uint) { + if (a != 1) return 1; + if (b != 2) return 2; + if (c != 3) return 3; + if (d != 4) return 4; + if (x != "abc") return 5; + if (e != true) return 6; + if (g != this) return 7; + return 20; + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction( + "f(uint256,uint16,uint24,int24,bytes3,bool,address)", + 1, 2, 3, 4, string("abc"), true, u160(m_contractAddress) + ), encodeArgs(u256(20))); + ) +} + +BOOST_AUTO_TEST_CASE(enums) +{ + string sourceCode = R"( + contract C { + enum E { A, B } + function f(E e) public pure returns (uint x) { + assembly { x := e } + } + } + )"; + bool newDecoder = false; + BOTH_ENCODERS( + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f(uint8)", 0), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("f(uint8)", 1), encodeArgs(u256(1))); + // The old decoder was not as strict about enums + ABI_CHECK(callContractFunction("f(uint8)", 2), (newDecoder ? encodeArgs() : encodeArgs(2))); + ABI_CHECK(callContractFunction("f(uint8)", u256(-1)), (newDecoder? encodeArgs() : encodeArgs(u256(0xff)))); + newDecoder = true; + ) +} + +BOOST_AUTO_TEST_CASE(cleanup) +{ + string sourceCode = R"( + contract C { + function f(uint16 a, int16 b, address c, bytes3 d, bool e) + public pure returns (uint v, uint w, uint x, uint y, uint z) { + assembly { v := a w := b x := c y := d z := e} + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode); + ABI_CHECK( + callContractFunction("f(uint16,int16,address,bytes3,bool)", 1, 2, 3, "a", true), + encodeArgs(u256(1), u256(2), u256(3), string("a"), true) + ); + ABI_CHECK( + callContractFunction( + "f(uint16,int16,address,bytes3,bool)", + u256(0xffffff), u256(0x1ffff), u256(-1), string("abcd"), u256(4) + ), + encodeArgs(u256(0xffff), u256(-1), (u256(1) << 160) - 1, string("abc"), true) + ); + ) +} + +BOOST_AUTO_TEST_CASE(fixed_arrays) +{ + string sourceCode = R"( + contract C { + function f(uint16[3] a, uint16[2][3] b, uint i, uint j, uint k) + public pure returns (uint, uint) { + return (a[i], b[j][k]); + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode); + bytes args = encodeArgs( + 1, 2, 3, + 11, 12, + 21, 22, + 31, 32, + 1, 2, 1 + ); + ABI_CHECK( + callContractFunction("f(uint16[3],uint16[2][3],uint256,uint256,uint256)", args), + encodeArgs(u256(2), u256(32)) + ); + ) +} + +BOOST_AUTO_TEST_CASE(dynamic_arrays) +{ + string sourceCode = R"( + contract C { + function f(uint a, uint16[] b, uint c) + public pure returns (uint, uint, uint) { + return (b.length, b[a], c); + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode); + bytes args = encodeArgs( + 6, 0x60, 9, + 7, + 11, 12, 13, 14, 15, 16, 17 + ); + ABI_CHECK( + callContractFunction("f(uint256,uint16[],uint256)", args), + encodeArgs(u256(7), u256(17), u256(9)) + ); + ) +} + +BOOST_AUTO_TEST_CASE(dynamic_nested_arrays) +{ + string sourceCode = R"( + contract C { + function f(uint a, uint16[][] b, uint[2][][3] c, uint d) + public pure returns (uint, uint, uint, uint, uint, uint, uint) { + return (a, b.length, b[1].length, b[1][1], c[1].length, c[1][1][1], d); + } + function test() view returns (uint, uint, uint, uint, uint, uint, uint) { + uint16[][] memory b = new uint16[][](3); + b[0] = new uint16[](2); + b[0][0] = 0x55; + b[0][1] = 0x56; + b[1] = new uint16[](4); + b[1][0] = 0x65; + b[1][1] = 0x66; + b[1][2] = 0x67; + b[1][3] = 0x68; + + uint[2][][3] memory c; + c[0] = new uint[2][](1); + c[0][0][1] = 0x75; + c[1] = new uint[2][](5); + c[1][1][1] = 0x85; + + return this.f(0x12, b, c, 0x13); + } + } + )"; + NEW_ENCODER( + compileAndRun(sourceCode); + bytes args = encodeArgs( + 0x12, 4 * 0x20, 17 * 0x20, 0x13, + // b + 3, 3 * 0x20, 6 * 0x20, 11 * 0x20, + 2, 85, 86, + 4, 101, 102, 103, 104, + 0, + // c + 3 * 0x20, 6 * 0x20, 17 * 0x20, + 1, 0, 117, + 5, 0, 0, 0, 133, 0, 0, 0, 0, 0, 0, + 0 + ); + + bytes expectation = encodeArgs(0x12, 3, 4, 0x66, 5, 0x85, 0x13); + ABI_CHECK(callContractFunction("test()"), expectation); + ABI_CHECK(callContractFunction("f(uint256,uint16[][],uint256[2][][3],uint256)", args), expectation); + ) +} + +BOOST_AUTO_TEST_CASE(byte_arrays) +{ + string sourceCode = R"( + contract C { + function f(uint a, bytes b, uint c) + public pure returns (uint, uint, byte, uint) { + return (a, b.length, b[3], c); + } + + function f_external(uint a, bytes b, uint c) + external pure returns (uint, uint, byte, uint) { + return (a, b.length, b[3], c); + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode); + bytes args = encodeArgs( + 6, 0x60, 9, + 7, "abcdefg" + ); + ABI_CHECK( + callContractFunction("f(uint256,bytes,uint256)", args), + encodeArgs(u256(6), u256(7), "d", 9) + ); + ABI_CHECK( + callContractFunction("f_external(uint256,bytes,uint256)", args), + encodeArgs(u256(6), u256(7), "d", 9) + ); + ) +} + +BOOST_AUTO_TEST_CASE(calldata_arrays_too_large) +{ + string sourceCode = R"( + contract C { + function f(uint a, uint[] b, uint c) external pure returns (uint) { + return 7; + } + } + )"; + bool newEncoder = false; + BOTH_ENCODERS( + compileAndRun(sourceCode); + bytes args = encodeArgs( + 6, 0x60, 9, + (u256(1) << 255) + 2, 1, 2 + ); + ABI_CHECK( + callContractFunction("f(uint256,uint256[],uint256)", args), + newEncoder ? encodeArgs() : encodeArgs(7) + ); + newEncoder = true; + ) +} + +BOOST_AUTO_TEST_CASE(decode_from_memory_simple) +{ + string sourceCode = R"( + contract C { + uint public _a; + uint[] public _b; + function C(uint a, uint[] b) { + _a = a; + _b = b; + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode, 0, "C", encodeArgs( + 7, 0x40, + // b + 3, 0x21, 0x22, 0x23 + )); + ABI_CHECK(callContractFunction("_a()"), encodeArgs(7)); + ABI_CHECK(callContractFunction("_b(uint256)", 0), encodeArgs(0x21)); + ABI_CHECK(callContractFunction("_b(uint256)", 1), encodeArgs(0x22)); + ABI_CHECK(callContractFunction("_b(uint256)", 2), encodeArgs(0x23)); + ABI_CHECK(callContractFunction("_b(uint256)", 3), encodeArgs()); + ) +} + +BOOST_AUTO_TEST_CASE(decode_function_type) +{ + string sourceCode = R"( + contract D { + function () external returns (uint) public _a; + function D(function () external returns (uint) a) { + _a = a; + } + } + contract C { + function f() returns (uint) { + return 3; + } + function g(function () external returns (uint) _f) returns (uint) { + return _f(); + } + // uses "decode from memory" + function test1() returns (uint) { + D d = new D(this.f); + return d._a()(); + } + // uses "decode from calldata" + function test2() returns (uint) { + return this.g(this.f); + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("test1()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("test2()"), encodeArgs(3)); + ) +} + +BOOST_AUTO_TEST_CASE(decode_function_type_array) +{ + string sourceCode = R"( + contract D { + function () external returns (uint)[] public _a; + function D(function () external returns (uint)[] a) { + _a = a; + } + } + contract E { + function () external returns (uint)[3] public _a; + function E(function () external returns (uint)[3] a) { + _a = a; + } + } + contract C { + function f1() public returns (uint) { + return 1; + } + function f2() public returns (uint) { + return 2; + } + function f3() public returns (uint) { + return 3; + } + function g(function () external returns (uint)[] _f, uint i) public returns (uint) { + return _f[i](); + } + function h(function () external returns (uint)[3] _f, uint i) public returns (uint) { + return _f[i](); + } + // uses "decode from memory" + function test1_dynamic() public returns (uint) { + var x = new function() external returns (uint)[](3); + x[0] = this.f1; + x[1] = this.f2; + x[2] = this.f3; + D d = new D(x); + return d._a(2)(); + } + function test1_static() public returns (uint) { + E e = new E([this.f1, this.f2, this.f3]); + return e._a(2)(); + } + // uses "decode from calldata" + function test2_dynamic() public returns (uint) { + var x = new function() external returns (uint)[](3); + x[0] = this.f1; + x[1] = this.f2; + x[2] = this.f3; + return this.g(x, 0); + } + function test2_static() public returns (uint) { + return this.h([this.f1, this.f2, this.f3], 0); + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("test1_static()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("test1_dynamic()"), encodeArgs(3)); + ABI_CHECK(callContractFunction("test2_static()"), encodeArgs(1)); + ABI_CHECK(callContractFunction("test2_dynamic()"), encodeArgs(1)); + ) +} + +BOOST_AUTO_TEST_CASE(decode_from_memory_complex) +{ + string sourceCode = R"( + contract C { + uint public _a; + uint[] public _b; + bytes[2] public _c; + function C(uint a, uint[] b, bytes[2] c) { + _a = a; + _b = b; + _c = c; + } + } + )"; + NEW_ENCODER( + compileAndRun(sourceCode, 0, "C", encodeArgs( + 7, 0x60, 7 * 0x20, + // b + 3, 0x21, 0x22, 0x23, + // c + 0x40, 0x80, + 8, string("abcdefgh"), + 52, string("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ") + )); + ABI_CHECK(callContractFunction("_a()"), encodeArgs(7)); + ABI_CHECK(callContractFunction("_b(uint256)", 0), encodeArgs(0x21)); + ABI_CHECK(callContractFunction("_b(uint256)", 1), encodeArgs(0x22)); + ABI_CHECK(callContractFunction("_b(uint256)", 2), encodeArgs(0x23)); + ABI_CHECK(callContractFunction("_b(uint256)", 3), encodeArgs()); + ABI_CHECK(callContractFunction("_c(uint256)", 0), encodeArgs(0x20, 8, string("abcdefgh"))); + ABI_CHECK(callContractFunction("_c(uint256)", 1), encodeArgs(0x20, 52, string("ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"))); + ABI_CHECK(callContractFunction("_c(uint256)", 2), encodeArgs()); + ) +} + +BOOST_AUTO_TEST_CASE(short_input_value_type) +{ + string sourceCode = R"( + contract C { + function f(uint a, uint b) public pure returns (uint) { return a; } + } + )"; + bool newDecoder = false; + BOTH_ENCODERS( + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f(uint256,uint256)", 1, 2), encodeArgs(1)); + ABI_CHECK(callContractFunctionNoEncoding("f(uint256,uint256)", bytes(64, 0)), encodeArgs(0)); + ABI_CHECK(callContractFunctionNoEncoding("f(uint256,uint256)", bytes(63, 0)), newDecoder ? encodeArgs() : encodeArgs(0)); + newDecoder = true; + ) +} + +BOOST_AUTO_TEST_CASE(short_input_array) +{ + string sourceCode = R"( + contract C { + function f(uint[] a) public pure returns (uint) { return 7; } + } + )"; + bool newDecoder = false; + BOTH_ENCODERS( + compileAndRun(sourceCode); + ABI_CHECK(callContractFunctionNoEncoding("f(uint256[])", encodeArgs(0x20, 0)), encodeArgs(7)); + ABI_CHECK(callContractFunctionNoEncoding("f(uint256[])", encodeArgs(0x20, 1)), newDecoder ? encodeArgs() : encodeArgs(7)); + ABI_CHECK(callContractFunctionNoEncoding("f(uint256[])", encodeArgs(0x20, 1) + bytes(31, 0)), newDecoder ? encodeArgs() : encodeArgs(7)); + ABI_CHECK(callContractFunctionNoEncoding("f(uint256[])", encodeArgs(0x20, 1) + bytes(32, 0)), encodeArgs(7)); + ABI_CHECK(callContractFunctionNoEncoding("f(uint256[])", encodeArgs(0x20, 2, 5, 6)), encodeArgs(7)); + newDecoder = true; + ) +} + +BOOST_AUTO_TEST_CASE(short_dynamic_input_array) +{ + string sourceCode = R"( + contract C { + function f(bytes[1] a) public pure returns (uint) { return 7; } + } + )"; + NEW_ENCODER( + compileAndRun(sourceCode); + ABI_CHECK(callContractFunctionNoEncoding("f(bytes[1])", encodeArgs(0x20)), encodeArgs()); + ) +} + +BOOST_AUTO_TEST_CASE(short_input_bytes) +{ + string sourceCode = R"( + contract C { + function e(bytes a) public pure returns (uint) { return 7; } + function f(bytes[] a) public pure returns (uint) { return 7; } + } + )"; + NEW_ENCODER( + compileAndRun(sourceCode); + ABI_CHECK(callContractFunctionNoEncoding("e(bytes)", encodeArgs(0x20, 7) + bytes(5, 0)), encodeArgs()); + ABI_CHECK(callContractFunctionNoEncoding("e(bytes)", encodeArgs(0x20, 7) + bytes(6, 0)), encodeArgs()); + ABI_CHECK(callContractFunctionNoEncoding("e(bytes)", encodeArgs(0x20, 7) + bytes(7, 0)), encodeArgs(7)); + ABI_CHECK(callContractFunctionNoEncoding("e(bytes)", encodeArgs(0x20, 7) + bytes(8, 0)), encodeArgs(7)); + ABI_CHECK(callContractFunctionNoEncoding("f(bytes[])", encodeArgs(0x20, 1, 0x20, 7) + bytes(5, 0)), encodeArgs()); + ABI_CHECK(callContractFunctionNoEncoding("f(bytes[])", encodeArgs(0x20, 1, 0x20, 7) + bytes(6, 0)), encodeArgs()); + ABI_CHECK(callContractFunctionNoEncoding("f(bytes[])", encodeArgs(0x20, 1, 0x20, 7) + bytes(7, 0)), encodeArgs(7)); + ABI_CHECK(callContractFunctionNoEncoding("f(bytes[])", encodeArgs(0x20, 1, 0x20, 7) + bytes(8, 0)), encodeArgs(7)); + ) +} + +BOOST_AUTO_TEST_CASE(cleanup_int_inside_arrays) +{ + string sourceCode = R"( + contract C { + enum E { A, B } + function f(uint16[] a) public pure returns (uint r) { assembly { r := mload(add(a, 0x20)) } } + function g(int16[] a) public pure returns (uint r) { assembly { r := mload(add(a, 0x20)) } } + function h(E[] a) public pure returns (uint r) { assembly { r := mload(add(a, 0x20)) } } + } + )"; + NEW_ENCODER( + compileAndRun(sourceCode); + ABI_CHECK(callContractFunction("f(uint16[])", 0x20, 1, 7), encodeArgs(7)); + ABI_CHECK(callContractFunction("g(int16[])", 0x20, 1, 7), encodeArgs(7)); + ABI_CHECK(callContractFunction("f(uint16[])", 0x20, 1, u256("0xffff")), encodeArgs(u256("0xffff"))); + ABI_CHECK(callContractFunction("g(int16[])", 0x20, 1, u256("0xffff")), encodeArgs(u256(-1))); + ABI_CHECK(callContractFunction("f(uint16[])", 0x20, 1, u256("0x1ffff")), encodeArgs(u256("0xffff"))); + ABI_CHECK(callContractFunction("g(int16[])", 0x20, 1, u256("0x10fff")), encodeArgs(u256("0x0fff"))); + ABI_CHECK(callContractFunction("h(uint8[])", 0x20, 1, 0), encodeArgs(u256(0))); + ABI_CHECK(callContractFunction("h(uint8[])", 0x20, 1, 1), encodeArgs(u256(1))); + ABI_CHECK(callContractFunction("h(uint8[])", 0x20, 1, 2), encodeArgs()); + ) +} + +BOOST_AUTO_TEST_CASE(storage_ptr) +{ + string sourceCode = R"( + library L { + struct S { uint x; uint y; } + function f(uint[] storage r, S storage s) public returns (uint, uint, uint, uint) { + r[2] = 8; + s.x = 7; + return (r[0], r[1], s.x, s.y); + } + } + contract C { + uint8 x = 3; + L.S s; + uint[] r; + function f() public returns (uint, uint, uint, uint, uint, uint) { + r.length = 6; + r[0] = 1; + r[1] = 2; + r[2] = 3; + s.x = 11; + s.y = 12; + var (a, b, c, d) = L.f(r, s); + return (r[2], s.x, a, b, c, d); + } + } + )"; + BOTH_ENCODERS( + compileAndRun(sourceCode, 0, "L"); + compileAndRun(sourceCode, 0, "C", bytes(), map<string, Address>{{"L", m_contractAddress}}); + ABI_CHECK(callContractFunction("f()"), encodeArgs(8, 7, 1, 2, 7, 12)); + ) +} + +BOOST_AUTO_TEST_CASE(struct_simple) +{ + string sourceCode = R"( + contract C { + struct S { uint a; uint8 b; uint8 c; bytes2 d; } + function f(S s) public pure returns (uint a, uint b, uint c, uint d) { + a = s.a; + b = s.b; + c = s.c; + d = uint(s.d); + } + } + )"; + NEW_ENCODER( + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f((uint256,uint8,uint8,bytes2))", 1, 2, 3, "ab"), encodeArgs(1, 2, 3, 'a' * 0x100 + 'b')); + ) +} + +BOOST_AUTO_TEST_CASE(struct_cleanup) +{ + string sourceCode = R"( + contract C { + struct S { int16 a; uint8 b; bytes2 c; } + function f(S s) public pure returns (uint a, uint b, uint c) { + assembly { + a := mload(s) + b := mload(add(s, 0x20)) + c := mload(add(s, 0x40)) + } + } + } + )"; + NEW_ENCODER( + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK( + callContractFunction("f((int16,uint8,bytes2))", 0xff010, 0xff0002, "abcd"), + encodeArgs(u256("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff010"), 2, "ab") + ); + ) +} + +BOOST_AUTO_TEST_CASE(struct_short) +{ + string sourceCode = R"( + contract C { + struct S { int a; uint b; bytes16 c; } + function f(S s) public pure returns (S q) { + q = s; + } + } + )"; + NEW_ENCODER( + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK( + callContractFunction("f((int256,uint256,bytes16))", 0xff010, 0xff0002, "abcd"), + encodeArgs(0xff010, 0xff0002, "abcd") + ); + ABI_CHECK( + callContractFunctionNoEncoding("f((int256,uint256,bytes16))", encodeArgs(0xff010, 0xff0002) + bytes(32, 0)), + encodeArgs(0xff010, 0xff0002, 0) + ); + ABI_CHECK( + callContractFunctionNoEncoding("f((int256,uint256,bytes16))", encodeArgs(0xff010, 0xff0002) + bytes(31, 0)), + encodeArgs() + ); + ) +} + +BOOST_AUTO_TEST_CASE(struct_function) +{ + string sourceCode = R"( + contract C { + struct S { function () external returns (uint) f; uint b; } + function f(S s) public returns (uint, uint) { + return (s.f(), s.b); + } + function test() public returns (uint, uint) { + return this.f(S(this.g, 3)); + } + function g() public returns (uint) { return 7; } + } + )"; + NEW_ENCODER( + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("test()"), encodeArgs(7, 3)); + ) +} + +BOOST_AUTO_TEST_CASE(empty_struct) +{ + string sourceCode = R"( + contract C { + struct S { } + function f(uint a, S s, uint b) public pure returns (uint x, uint y) { + assembly { x := a y := b } + } + function g() public returns (uint, uint) { + return this.f(7, S(), 8); + } + } + )"; + NEW_ENCODER( + compileAndRun(sourceCode, 0, "C"); + ABI_CHECK(callContractFunction("f(uint256,(),uint256)", 7, 8), encodeArgs(7, 8)); + ABI_CHECK(callContractFunction("g()"), encodeArgs(7, 8)); + ) +} + +BOOST_AUTO_TEST_CASE(mediocre_struct) +{ + string sourceCode = R"( + contract C { + struct S { C c; } + function f(uint a, S[2] s1, uint b) public returns (uint r1, C r2, uint r3) { + r1 = a; + r2 = s1[0].c; + r3 = b; + } + } + )"; + NEW_ENCODER( + compileAndRun(sourceCode, 0, "C"); + string sig = "f(uint256,(address)[2],uint256)"; + ABI_CHECK(callContractFunction(sig, + 7, u256(u160(m_contractAddress)), 0, 8 + ), encodeArgs(7, u256(u160(m_contractAddress)), 8)); + ) +} + +BOOST_AUTO_TEST_CASE(mediocre2_struct) +{ + string sourceCode = R"( + contract C { + struct S { C c; uint[] x; } + function f(uint a, S[2] s1, uint b) public returns (uint r1, C r2, uint r3) { + r1 = a; + r2 = s1[0].c; + r3 = b; + } + } + )"; + NEW_ENCODER( + compileAndRun(sourceCode, 0, "C"); + string sig = "f(uint256,(address,uint256[])[2],uint256)"; + ABI_CHECK(callContractFunction(sig, + 7, 0x60, 8, + 0x40, 7 * 0x20, + u256(u160(m_contractAddress)), 0x40, + 2, 0x11, 0x12, + 0x99, 0x40, + 4, 0x31, 0x32, 0x34, 0x35 + ), encodeArgs(7, u256(u160(m_contractAddress)), 8)); + ) +} + +BOOST_AUTO_TEST_CASE(complex_struct) +{ + string sourceCode = R"( + contract C { + enum E {A, B, C} + struct T { uint x; E e; uint8 y; } + struct S { C c; T[] t;} + function f(uint a, S[2] s1, S[] s2, uint b) public returns + (uint r1, C r2, uint r3, uint r4, C r5, uint r6, E r7, uint8 r8) { + r1 = a; + r2 = s1[0].c; + r3 = b; + r4 = s2.length; + r5 = s2[1].c; + r6 = s2[1].t.length; + r7 = s2[1].t[1].e; + r8 = s2[1].t[1].y; + } + } + )"; + NEW_ENCODER( + compileAndRun(sourceCode, 0, "C"); + string sig = "f(uint256,(address,(uint256,uint8,uint8)[])[2],(address,(uint256,uint8,uint8)[])[],uint256)"; + bytes args = encodeArgs( + 7, 0x80, 0x1e0, 8, + // S[2] s1 + 0x40, + 0x100, + // S s1[0] + u256(u160(m_contractAddress)), + 0x40, + // T s1[0].t + 1, // length + // s1[0].t[0] + 0x11, 1, 0x12, + // S s1[1] + 0, 0x40, + // T s1[1].t + 0, + // S[] s2 (0x1e0) + 2, // length + 0x40, 0xa0, + // S s2[0] + 0, 0x40, 0, + // S s2[1] + 0x1234, 0x40, + // s2[1].t + 3, // length + 0, 0, 0, + 0x21, 2, 0x22, + 0, 0, 0 + ); + ABI_CHECK(callContractFunction(sig, args), encodeArgs(7, u256(u160(m_contractAddress)), 8, 2, 0x1234, 3, 2, 0x22)); + // invalid enum value + args.data()[0x20 * 28] = 3; + ABI_CHECK(callContractFunction(sig, args), encodeArgs()); + ) +} + + + +BOOST_AUTO_TEST_SUITE_END() + +} +} +} // end namespaces diff --git a/test/libsolidity/ABIEncoderTests.cpp b/test/libsolidity/ABIEncoderTests.cpp index af51edcc..49db9ce1 100644 --- a/test/libsolidity/ABIEncoderTests.cpp +++ b/test/libsolidity/ABIEncoderTests.cpp @@ -25,6 +25,8 @@ #include <libsolidity/interface/Exceptions.h> #include <test/libsolidity/SolidityExecutionFramework.h> +#include <test/libsolidity/ABITestsCommon.h> + using namespace std; using namespace std::placeholders; using namespace dev::test; @@ -42,20 +44,6 @@ namespace test BOOST_CHECK_EQUAL(toHex(m_logs[0].data), toHex(DATA)); \ } while (false) -static string const NewEncoderPragma = "pragma experimental ABIEncoderV2;\n"; - -#define NEW_ENCODER(CODE) \ -{ \ - sourceCode = NewEncoderPragma + sourceCode; \ - { CODE } \ -} - -#define BOTH_ENCODERS(CODE) \ -{ \ - { CODE } \ - NEW_ENCODER(CODE) \ -} - BOOST_FIXTURE_TEST_SUITE(ABIEncoderTest, SolidityExecutionFramework) BOOST_AUTO_TEST_CASE(both_encoders_macro) @@ -74,7 +62,7 @@ BOOST_AUTO_TEST_CASE(value_types) string sourceCode = R"( contract C { event E(uint a, uint16 b, uint24 c, int24 d, bytes3 x, bool, C); - function f() { + function f() public { bytes6 x = hex"1bababababa2"; bool b; assembly { b := 7 } @@ -98,7 +86,7 @@ BOOST_AUTO_TEST_CASE(string_literal) string sourceCode = R"( contract C { event E(string, bytes20, string); - function f() { + function f() public { E("abcdef", "abcde", "abcdefabcdefgehabcabcasdfjklabcdefabcedefghabcabcasdfjklabcdefabcdefghabcabcasdfjklabcdeefabcdefghabcabcasdefjklabcdefabcdefghabcabcasdfjkl"); } } @@ -120,7 +108,7 @@ BOOST_AUTO_TEST_CASE(enum_type_cleanup) string sourceCode = R"( contract C { enum E { A, B } - function f(uint x) returns (E en) { + function f(uint x) public returns (E en) { assembly { en := x } } } @@ -138,7 +126,7 @@ BOOST_AUTO_TEST_CASE(conversion) string sourceCode = R"( contract C { event E(bytes4, bytes4, uint16, uint8, int16, int8); - function f() { + function f() public { bytes2 x; assembly { x := 0xf1f2f3f400000000000000000000000000000000000000000000000000000000 } uint8 a; uint16 b = 0x1ff; @@ -164,7 +152,7 @@ BOOST_AUTO_TEST_CASE(memory_array_one_dim) string sourceCode = R"( contract C { event E(uint a, int16[] b, uint c); - function f() { + function f() public { int16[] memory x = new int16[](3); assembly { for { let i := 0 } lt(i, 3) { i := add(i, 1) } { @@ -191,7 +179,7 @@ BOOST_AUTO_TEST_CASE(memory_array_two_dim) string sourceCode = R"( contract C { event E(uint a, int16[][2] b, uint c); - function f() { + function f() public { int16[][2] memory x; x[0] = new int16[](3); x[1] = new int16[](2); @@ -216,7 +204,7 @@ BOOST_AUTO_TEST_CASE(memory_byte_array) string sourceCode = R"( contract C { event E(uint a, bytes[] b, uint c); - function f() { + function f() public { bytes[] memory x = new bytes[](2); x[0] = "abcabcdefghjklmnopqrsuvwabcdefgijklmnopqrstuwabcdefgijklmnoprstuvw"; x[1] = "abcdefghijklmnopqrtuvwabcfghijklmnopqstuvwabcdeghijklmopqrstuvw"; @@ -243,7 +231,7 @@ BOOST_AUTO_TEST_CASE(storage_byte_array) bytes short; bytes long; event E(bytes s, bytes l); - function f() { + function f() public { short = "123456789012345678901234567890a"; long = "ffff123456789012345678901234567890afffffffff123456789012345678901234567890a"; E(short, long); @@ -267,7 +255,7 @@ BOOST_AUTO_TEST_CASE(storage_array) contract C { address[3] addr; event E(address[3] a); - function f() { + function f() public { assembly { sstore(0, sub(0, 1)) sstore(1, sub(0, 2)) @@ -290,7 +278,7 @@ BOOST_AUTO_TEST_CASE(storage_array_dyn) contract C { address[] addr; event E(address[] a); - function f() { + function f() public { addr.push(1); addr.push(2); addr.push(3); @@ -311,7 +299,7 @@ BOOST_AUTO_TEST_CASE(storage_array_compact) contract C { int72[] x; event E(int72[]); - function f() { + function f() public { x.push(-1); x.push(2); x.push(-3); @@ -339,7 +327,7 @@ BOOST_AUTO_TEST_CASE(external_function) contract C { event E(function(uint) external returns (uint), function(uint) external returns (uint)); function(uint) external returns (uint) g; - function f(uint) returns (uint) { + function f(uint) public returns (uint) { g = this.f; E(this.f, g); } @@ -347,7 +335,7 @@ BOOST_AUTO_TEST_CASE(external_function) )"; BOTH_ENCODERS( compileAndRun(sourceCode); - callContractFunction("f(uint256)"); + callContractFunction("f(uint256)", u256(0)); string functionIdF = asString(m_contractAddress.ref()) + asString(FixedHash<4>(dev::keccak256("f(uint256)")).ref()); REQUIRE_LOG_DATA(encodeArgs(functionIdF, functionIdF)); ) @@ -360,7 +348,7 @@ BOOST_AUTO_TEST_CASE(external_function_cleanup) event E(function(uint) external returns (uint), function(uint) external returns (uint)); // This test relies on the fact that g is stored in slot zero. function(uint) external returns (uint) g; - function f(uint) returns (uint) { + function f(uint) public returns (uint) { function(uint) external returns (uint)[1] memory h; assembly { sstore(0, sub(0, 1)) mstore(h, sub(0, 1)) } E(h[0], g); @@ -369,7 +357,7 @@ BOOST_AUTO_TEST_CASE(external_function_cleanup) )"; BOTH_ENCODERS( compileAndRun(sourceCode); - callContractFunction("f(uint256)"); + callContractFunction("f(uint256)", u256(0)); REQUIRE_LOG_DATA(encodeArgs(string(24, char(-1)), string(24, char(-1)))); ) } @@ -404,7 +392,7 @@ BOOST_AUTO_TEST_CASE(function_name_collision) // and by the ABI encoder string sourceCode = R"( contract C { - function f(uint x) returns (uint) { + function f(uint x) public returns (uint) { assembly { function abi_encode_t_uint256_to_t_uint256() { mstore(0, 7) @@ -432,7 +420,7 @@ BOOST_AUTO_TEST_CASE(structs) struct T { uint64[2] x; } S s; event e(uint16, S); - function f() returns (uint, S) { + function f() public returns (uint, S) { uint16 x = 7; s.a = 8; s.b = 9; diff --git a/test/libsolidity/ABITestsCommon.h b/test/libsolidity/ABITestsCommon.h new file mode 100644 index 00000000..2ef555f3 --- /dev/null +++ b/test/libsolidity/ABITestsCommon.h @@ -0,0 +1,43 @@ +/* + 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/>. +*/ + +#include <string> + +namespace dev +{ +namespace solidity +{ +namespace test +{ + +static std::string const NewEncoderPragma = "pragma experimental ABIEncoderV2;\n"; + +#define NEW_ENCODER(CODE) \ +{ \ + sourceCode = NewEncoderPragma + sourceCode; \ + { CODE } \ +} + +#define BOTH_ENCODERS(CODE) \ +{ \ + { CODE } \ + NEW_ENCODER(CODE) \ +} + +} +} +} // end namespaces diff --git a/test/libsolidity/Assembly.cpp b/test/libsolidity/Assembly.cpp index 358d3c72..59af6d41 100644 --- a/test/libsolidity/Assembly.cpp +++ b/test/libsolidity/Assembly.cpp @@ -86,23 +86,59 @@ eth::AssemblyItems compileContract(const string& _sourceCode) return AssemblyItems(); } +void printAssemblyLocations(AssemblyItems const& _items) +{ + auto printRepeated = [](SourceLocation const& _loc, size_t _repetitions) + { + cout << + "\t\tvector<SourceLocation>(" << + _repetitions << + ", SourceLocation(" << + _loc.start << + ", " << + _loc.end << + ", make_shared<string>(\"" << + *_loc.sourceName << + "\"))) +" << endl; + }; + + vector<SourceLocation> locations; + for (auto const& item: _items) + locations.push_back(item.location()); + size_t repetitions = 0; + SourceLocation const* previousLoc = nullptr; + for (size_t i = 0; i < locations.size(); ++i) + { + SourceLocation& loc = locations[i]; + if (previousLoc && *previousLoc == loc) + repetitions++; + else + { + if (previousLoc) + printRepeated(*previousLoc, repetitions); + previousLoc = &loc; + repetitions = 1; + } + } + if (previousLoc) + printRepeated(*previousLoc, repetitions); +} + void checkAssemblyLocations(AssemblyItems const& _items, vector<SourceLocation> const& _locations) { BOOST_CHECK_EQUAL(_items.size(), _locations.size()); for (size_t i = 0; i < min(_items.size(), _locations.size()); ++i) { - BOOST_CHECK_MESSAGE( - _items[i].location() == _locations[i], - "Location mismatch for assembly item " + to_string(i) + ". Found: " + - (_items[i].location().sourceName ? *_items[i].location().sourceName + ":" : "(null source name)") + - to_string(_items[i].location().start) + "-" + - to_string(_items[i].location().end) + ", expected: " + - (_locations[i].sourceName ? *_locations[i].sourceName + ":" : "(null source name)") + - to_string(_locations[i].start) + "-" + - to_string(_locations[i].end)); + if (_items[i].location() != _locations[i]) + { + BOOST_CHECK_MESSAGE(false, "Location mismatch for item " + to_string(i) + ". Found the following locations:"); + printAssemblyLocations(_items); + return; + } } } + } // end anonymous namespace BOOST_AUTO_TEST_SUITE(Assembly) diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp index e9fb8431..b09eb261 100644 --- a/test/libsolidity/InlineAssembly.cpp +++ b/test/libsolidity/InlineAssembly.cpp @@ -51,10 +51,11 @@ boost::optional<Error> parseAndReturnFirstError( string const& _source, bool _assemble = false, bool _allowWarnings = true, + AssemblyStack::Language _language = AssemblyStack::Language::Assembly, AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM ) { - AssemblyStack stack; + AssemblyStack stack(_language); bool success = false; try { @@ -87,22 +88,29 @@ bool successParse( string const& _source, bool _assemble = false, bool _allowWarnings = true, + AssemblyStack::Language _language = AssemblyStack::Language::Assembly, AssemblyStack::Machine _machine = AssemblyStack::Machine::EVM ) { - return !parseAndReturnFirstError(_source, _assemble, _allowWarnings, _machine); + return !parseAndReturnFirstError(_source, _assemble, _allowWarnings, _language, _machine); } -bool successAssemble(string const& _source, bool _allowWarnings = true) +bool successAssemble(string const& _source, bool _allowWarnings = true, AssemblyStack::Language _language = AssemblyStack::Language::Assembly) { - return successParse(_source, true, _allowWarnings, AssemblyStack::Machine::EVM) && - successParse(_source, true, _allowWarnings, AssemblyStack::Machine::EVM15); + return + successParse(_source, true, _allowWarnings, _language, AssemblyStack::Machine::EVM) && + successParse(_source, true, _allowWarnings, _language, AssemblyStack::Machine::EVM15); } -Error expectError(std::string const& _source, bool _assemble, bool _allowWarnings = false) +Error expectError( + std::string const& _source, + bool _assemble, + bool _allowWarnings = false, + AssemblyStack::Language _language = AssemblyStack::Language::Assembly +) { - auto error = parseAndReturnFirstError(_source, _assemble, _allowWarnings); + auto error = parseAndReturnFirstError(_source, _assemble, _allowWarnings, _language); BOOST_REQUIRE(error); return *error; } @@ -120,14 +128,17 @@ void parsePrintCompare(string const& _source, bool _canWarn = false) } -#define CHECK_ERROR(text, assemble, typ, substring, warnings) \ +#define CHECK_ERROR_LANG(text, assemble, typ, substring, warnings, language) \ do \ { \ - Error err = expectError((text), (assemble), warnings); \ + Error err = expectError((text), (assemble), warnings, (language)); \ BOOST_CHECK(err.type() == (Error::Type::typ)); \ BOOST_CHECK(searchErrorMessage(err, (substring))); \ } while(0) +#define CHECK_ERROR(text, assemble, typ, substring, warnings) \ +CHECK_ERROR_LANG(text, assemble, typ, substring, warnings, AssemblyStack::Language::Assembly) + #define CHECK_PARSE_ERROR(text, type, substring) \ CHECK_ERROR(text, false, type, substring, false) @@ -137,6 +148,14 @@ CHECK_ERROR(text, false, type, substring, false) #define CHECK_ASSEMBLE_ERROR(text, type, substring) \ CHECK_ERROR(text, true, type, substring, false) +#define CHECK_STRICT_ERROR(text, type, substring) \ +CHECK_ERROR_LANG(text, false, type, substring, false, AssemblyStack::Language::StrictAssembly) + +#define CHECK_STRICT_WARNING(text, type, substring) \ +CHECK_ERROR(text, false, type, substring, false, AssemblyStack::Language::StrictAssembly) + +#define SUCCESS_STRICT(text) \ +do { successParse((text), false, false, AssemblyStack::Language::StrictAssembly); } while (false) BOOST_AUTO_TEST_SUITE(SolidityInlineAssembly) @@ -266,7 +285,7 @@ BOOST_AUTO_TEST_CASE(if_statement_scope) BOOST_AUTO_TEST_CASE(if_statement_invalid) { - CHECK_PARSE_ERROR("{ if calldatasize {}", ParserError, "Instructions are not supported as conditions for if"); + CHECK_PARSE_ERROR("{ if mload {} }", ParserError, "Expected token \"(\""); BOOST_CHECK("{ if calldatasize() {}"); CHECK_PARSE_ERROR("{ if mstore(1, 1) {} }", ParserError, "Instruction \"mstore\" not allowed in this context"); CHECK_PARSE_ERROR("{ if 32 let x := 3 }", ParserError, "Expected token LBrace"); @@ -296,7 +315,7 @@ BOOST_AUTO_TEST_CASE(switch_duplicate_case) 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 mload default {} }", ParserError, "Expected token \"(\""); CHECK_PARSE_ERROR("{ switch mstore(1, 1) default {} }", ParserError, "Instruction \"mstore\" not allowed in this context"); } @@ -332,7 +351,7 @@ BOOST_AUTO_TEST_CASE(for_invalid_expression) 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 {} mload {} {} }", ParserError, "Expected token \"(\""); CHECK_PARSE_ERROR("{ for {} mstore(1, 1) {} {} }", ParserError, "Instruction \"mstore\" not allowed in this context"); } @@ -455,6 +474,47 @@ BOOST_AUTO_TEST_CASE(multiple_assignment) BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(LooseStrictMode) + +BOOST_AUTO_TEST_CASE(no_opcodes_in_strict) +{ + BOOST_CHECK(successParse("{ pop(callvalue) }")); + BOOST_CHECK(successParse("{ callvalue pop }")); + CHECK_STRICT_ERROR("{ pop(callvalue) }", ParserError, "Non-functional instructions are not allowed in this context."); + CHECK_STRICT_ERROR("{ callvalue pop }", ParserError, "Call or assignment expected"); + SUCCESS_STRICT("{ pop(callvalue()) }"); + BOOST_CHECK(successParse("{ switch callvalue case 0 {} }")); + CHECK_STRICT_ERROR("{ switch callvalue case 0 {} }", ParserError, "Non-functional instructions are not allowed in this context."); +} + +BOOST_AUTO_TEST_CASE(no_labels_in_strict) +{ + BOOST_CHECK(successParse("{ a: }")); + CHECK_STRICT_ERROR("{ a: }", ParserError, "Labels are not supported"); +} + +BOOST_AUTO_TEST_CASE(no_stack_assign_in_strict) +{ + BOOST_CHECK(successParse("{ let x 4 =: x }")); + CHECK_STRICT_ERROR("{ let x 4 =: x }", ParserError, "Call or assignment expected."); +} + +BOOST_AUTO_TEST_CASE(no_dup_swap_in_strict) +{ + BOOST_CHECK(successParse("{ swap1 }")); + CHECK_STRICT_ERROR("{ swap1 }", ParserError, "Call or assignment expected."); + BOOST_CHECK(successParse("{ dup1 pop }")); + CHECK_STRICT_ERROR("{ dup1 pop }", ParserError, "Call or assignment expected."); + BOOST_CHECK(successParse("{ swap2 }")); + CHECK_STRICT_ERROR("{ swap2 }", ParserError, "Call or assignment expected."); + BOOST_CHECK(successParse("{ dup2 pop }")); + CHECK_STRICT_ERROR("{ dup2 pop }", ParserError, "Call or assignment expected."); + CHECK_PARSE_ERROR("{ switch dup1 case 0 {} }", ParserError, "Instruction \"dup1\" not allowed in this context"); + CHECK_STRICT_ERROR("{ switch dup1 case 0 {} }", ParserError, "Instruction \"dup1\" not allowed in this context"); +} + +BOOST_AUTO_TEST_SUITE_END() + BOOST_AUTO_TEST_SUITE(Printing) BOOST_AUTO_TEST_CASE(print_smoke) @@ -540,7 +600,7 @@ BOOST_AUTO_TEST_CASE(function_calls) function g(a, b, c) { } - g(1, mul(2, address), f(mul(2, caller))) + g(1, mul(2, address()), f(mul(2, caller()))) y() })"; boost::replace_all(source, "\t", " "); @@ -712,8 +772,9 @@ 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"); + CHECK_PARSE_WARNING("{ jump(44) }", Warning, "Jump instructions"); + CHECK_PARSE_WARNING("{ jumpi(44, 2) }", Warning, "Jump instructions"); + CHECK_PARSE_WARNING("{ a: }", Warning, "Jump instructions"); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp index 7dc4808b..0c904c77 100644 --- a/test/libsolidity/JSONCompiler.cpp +++ b/test/libsolidity/JSONCompiler.cpp @@ -23,7 +23,7 @@ #include <boost/test/unit_test.hpp> #include <libdevcore/JSON.h> #include <libsolidity/interface/Version.h> -#include <solc/jsonCompiler.h> +#include <libsolc/libsolc.h> #include "../Metadata.h" #include "../TestHelper.h" diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 667d666b..2a1609cc 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -94,6 +94,7 @@ BOOST_AUTO_TEST_CASE(warn_on_typecast) BOOST_AUTO_TEST_CASE(warn_on_struct) { string text = R"( + pragma experimental ABIEncoderV2; contract C { struct A { uint a; uint b; } function f() public pure returns (A) { @@ -167,9 +168,9 @@ BOOST_AUTO_TEST_CASE(function_call_does_not_clear_local_vars) CHECK_SUCCESS_NO_WARNINGS(text); } -BOOST_AUTO_TEST_CASE(branches_clear_variables) +BOOST_AUTO_TEST_CASE(branches_merge_variables) { - // Only clears accessed variables + // Branch does not touch variable a string text = R"( contract C { function f(uint x) public pure { @@ -181,7 +182,7 @@ BOOST_AUTO_TEST_CASE(branches_clear_variables) } )"; CHECK_SUCCESS_NO_WARNINGS(text); - // It is just a plain clear and will not combine branches. + // Positive branch touches variable a, but assertion should still hold. text = R"( contract C { function f(uint x) public pure { @@ -193,8 +194,8 @@ BOOST_AUTO_TEST_CASE(branches_clear_variables) } } )"; - CHECK_WARNING(text, "Assertion violation happens here"); - // Clear also works on the else branch + CHECK_SUCCESS_NO_WARNINGS(text); + // Negative branch touches variable a, but assertion should still hold. text = R"( contract C { function f(uint x) public pure { @@ -207,8 +208,8 @@ BOOST_AUTO_TEST_CASE(branches_clear_variables) } } )"; - CHECK_WARNING(text, "Assertion violation happens here"); - // Variable is not cleared, if it is only read. + CHECK_SUCCESS_NO_WARNINGS(text); + // Variable is not merged, if it is only read. text = R"( contract C { function f(uint x) public pure { @@ -223,6 +224,36 @@ BOOST_AUTO_TEST_CASE(branches_clear_variables) } )"; CHECK_SUCCESS_NO_WARNINGS(text); + // Variable is reset in both branches + text = R"( + contract C { + function f(uint x) public pure { + uint a = 2; + if (x > 10) { + a = 3; + } else { + a = 3; + } + assert(a == 3); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + // Variable is reset in both branches + text = R"( + contract C { + function f(uint x) public pure { + uint a = 2; + if (x > 10) { + a = 3; + } else { + a = 4; + } + assert(a >= 3); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); } BOOST_AUTO_TEST_CASE(branches_assert_condition) @@ -261,7 +292,7 @@ BOOST_AUTO_TEST_CASE(branches_assert_condition) CHECK_SUCCESS_NO_WARNINGS(text); } -BOOST_AUTO_TEST_CASE(ways_to_clear_variables) +BOOST_AUTO_TEST_CASE(ways_to_merge_variables) { string text = R"( contract C { @@ -274,6 +305,7 @@ BOOST_AUTO_TEST_CASE(ways_to_clear_variables) } } )"; + CHECK_WARNING(text, "Assertion violation happens here"); text = R"( contract C { function f(uint x) public pure { @@ -351,9 +383,9 @@ BOOST_AUTO_TEST_CASE(while_loop_simple) // Check that side-effects of condition are taken into account text = R"( contract C { - function f(uint x) public pure { + function f(uint x, uint y) public pure { x = 7; - while ((x = 5) > 0) { + while ((x = y) > 0) { } assert(x == 7); } @@ -458,6 +490,100 @@ BOOST_AUTO_TEST_CASE(for_loop) CHECK_WARNING(text, "Assertion violation"); } +BOOST_AUTO_TEST_CASE(division) +{ + string text = R"( + contract C { + function f(uint x, uint y) public pure returns (uint) { + return x / y; + } + } + )"; + CHECK_WARNING(text, "Division by zero"); + text = R"( + contract C { + function f(uint x, uint y) public pure returns (uint) { + require(y != 0); + return x / y; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(int x, int y) public pure returns (int) { + require(y != 0); + return x / y; + } + } + )"; + CHECK_WARNING(text, "Overflow"); + text = R"( + contract C { + function f(int x, int y) public pure returns (int) { + require(y != 0); + require(y != -1); + return x / y; + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + +BOOST_AUTO_TEST_CASE(division_truncates_correctly) +{ + string text = R"( + contract C { + function f(uint x, uint y) public pure { + x = 7; + y = 2; + assert(x / y == 3); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(int x, int y) public pure { + x = 7; + y = 2; + assert(x / y == 3); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(int x, int y) public pure { + x = -7; + y = 2; + assert(x / y == -3); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(int x, int y) public pure { + x = 7; + y = -2; + assert(x / y == -3); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); + text = R"( + contract C { + function f(int x, int y) public pure { + x = -7; + y = -2; + assert(x / y == 3); + } + } + )"; + CHECK_SUCCESS_NO_WARNINGS(text); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 33962730..26bfb6d0 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -942,6 +942,7 @@ BOOST_AUTO_TEST_CASE(function_type) BOOST_AUTO_TEST_CASE(return_structs) { char const* text = R"( + pragma experimental ABIEncoderV2; contract C { struct S { uint a; T[] sub; } struct T { uint[2] x; } @@ -991,6 +992,7 @@ BOOST_AUTO_TEST_CASE(return_structs) BOOST_AUTO_TEST_CASE(return_structs_with_contracts) { char const* text = R"( + pragma experimental ABIEncoderV2; contract C { struct S { C[] x; C y; } function f() returns (S s, C c) { @@ -1090,6 +1092,7 @@ BOOST_AUTO_TEST_CASE(event_structs) BOOST_AUTO_TEST_CASE(structs_in_libraries) { char const* text = R"( + pragma experimental ABIEncoderV2; library L { struct S { uint a; T[] sub; bytes b; } struct T { uint[2] x; } diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 05dc9ba3..f5f7e64a 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2971,7 +2971,7 @@ BOOST_AUTO_TEST_CASE(event_no_arguments) { char const* sourceCode = R"( contract ClientReceipt { - event Deposit; + event Deposit(); function deposit() { Deposit(); } @@ -3013,7 +3013,7 @@ BOOST_AUTO_TEST_CASE(events_with_same_name) { char const* sourceCode = R"( contract ClientReceipt { - event Deposit; + event Deposit(); event Deposit(address _addr); event Deposit(address _addr, uint _amount); function deposit() returns (uint) { @@ -3059,7 +3059,7 @@ BOOST_AUTO_TEST_CASE(events_with_same_name_inherited) { char const* sourceCode = R"( contract A { - event Deposit; + event Deposit(); } contract B { diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 97d359e8..eb6a440e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -602,6 +602,7 @@ BOOST_AUTO_TEST_CASE(enum_external_type) BOOST_AUTO_TEST_CASE(external_structs) { char const* text = R"( + pragma experimental ABIEncoderV2; contract Test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } struct Empty {} @@ -629,6 +630,7 @@ BOOST_AUTO_TEST_CASE(external_structs) BOOST_AUTO_TEST_CASE(external_structs_in_libraries) { char const* text = R"( + pragma experimental ABIEncoderV2; library Test { enum ActionChoices { GoLeft, GoRight, GoStraight, Sit } struct Empty {} @@ -2107,7 +2109,7 @@ BOOST_AUTO_TEST_CASE(array_with_nonconstant_length) function f(uint a) public { uint8[a] x; } } )"; - CHECK_ERROR(text, TypeError, "Identifier must be declared constant."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_with_negative_length) @@ -3511,6 +3513,7 @@ BOOST_AUTO_TEST_CASE(using_for_not_used) BOOST_AUTO_TEST_CASE(library_memory_struct) { char const* text = R"( + pragma experimental ABIEncoderV2; library c { struct S { uint x; } function f() public returns (S ) {} @@ -3605,6 +3608,20 @@ BOOST_AUTO_TEST_CASE(invalid_args_creating_memory_array) CHECK_ERROR(text, TypeError, "Wrong argument count for function call: 0 arguments given but expected 1."); } +BOOST_AUTO_TEST_CASE(invalid_args_creating_struct) +{ + char const* text = R"( + contract C { + struct S { uint a; uint b; } + + function f() public { + var s = S({a: 1}); + } + } + )"; + CHECK_ERROR(text, TypeError, "Wrong argument count for struct constructor: 1 arguments given but expected 2."); +} + BOOST_AUTO_TEST_CASE(function_overload_array_type) { char const* text = R"( @@ -4395,7 +4412,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_signed_fixed_type) } } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_unsigned_fixed_type) @@ -4407,7 +4424,7 @@ BOOST_AUTO_TEST_CASE(invalid_array_declaration_with_unsigned_fixed_type) } } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(rational_to_bytes_implicit_conversion) @@ -5696,6 +5713,7 @@ BOOST_AUTO_TEST_CASE(constructible_internal_constructor) BOOST_AUTO_TEST_CASE(return_structs) { char const* text = R"( + pragma experimental ABIEncoderV2; contract C { struct S { uint a; T[] sub; } struct T { uint[] x; } @@ -7250,7 +7268,7 @@ BOOST_AUTO_TEST_CASE(array_length_too_large) uint[8**90] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_length_not_convertible_to_integer) @@ -7260,7 +7278,7 @@ BOOST_AUTO_TEST_CASE(array_length_not_convertible_to_integer) uint[true] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_length_constant_var) @@ -7282,7 +7300,7 @@ BOOST_AUTO_TEST_CASE(array_length_non_integer_constant_var) uint[LEN] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_length_cannot_be_function) @@ -7293,7 +7311,7 @@ BOOST_AUTO_TEST_CASE(array_length_cannot_be_function) uint[f] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_length_can_be_recursive_constant) @@ -7317,7 +7335,7 @@ BOOST_AUTO_TEST_CASE(array_length_cannot_be_function_call) uint[LEN] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_length_const_cannot_be_fractional) @@ -7366,7 +7384,7 @@ BOOST_AUTO_TEST_CASE(array_length_cannot_be_constant_function_parameter) } } )"; - CHECK_ERROR(text, TypeError, "Constant identifier declaration must have a constant value."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_length_with_cyclic_constant) @@ -7405,7 +7423,7 @@ BOOST_AUTO_TEST_CASE(array_length_with_pure_functions) uint[LEN] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); } BOOST_AUTO_TEST_CASE(array_length_invalid_expression) @@ -7415,25 +7433,25 @@ BOOST_AUTO_TEST_CASE(array_length_invalid_expression) uint[-true] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid constant expression."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); text = R"( contract C { uint[true/1] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid constant expression."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); text = R"( contract C { uint[1/true] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid constant expression."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); text = R"( contract C { uint[1.111111E1111111111111] ids; } )"; - CHECK_ERROR(text, TypeError, "Invalid literal value."); + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal or constant expression."); text = R"( contract C { uint[3/0] ids; diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 72473c3e..861e6408 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -960,6 +960,16 @@ BOOST_AUTO_TEST_CASE(event_arguments_indexed) BOOST_CHECK(successParse(text)); } +BOOST_AUTO_TEST_CASE(event_with_no_argument_list_fails) +{ + char const* text = R"( + contract c { + event e; + } + )"; + CHECK_PARSE_ERROR(text, "Expected token LParen got 'Semicolon'"); +} + BOOST_AUTO_TEST_CASE(visibility_specifiers) { char const* text = R"( diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp index 4504946b..e48624e5 100644 --- a/test/libsolidity/StandardCompiler.cpp +++ b/test/libsolidity/StandardCompiler.cpp @@ -179,6 +179,14 @@ BOOST_AUTO_TEST_CASE(basic_compilation) "fileA": { "content": "contract A { }" } + }, + "settings": { + "outputSelection": { + "fileA": { + "A": [ "abi", "devdoc", "userdoc", "evm.bytecode", "evm.assembly", "evm.gasEstimates", "metadata" ], + "": [ "legacyAST" ] + } + } } } )"; @@ -226,6 +234,46 @@ BOOST_AUTO_TEST_CASE(basic_compilation) ); } +BOOST_AUTO_TEST_CASE(compilation_error) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "outputSelection": { + "fileA": { + "A": [ + "abi" + ] + } + } + }, + "sources": { + "fileA": { + "content": "contract A { function }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(result.isMember("errors")); + BOOST_CHECK(result["errors"].size() >= 1); + for (auto const& error: result["errors"]) + { + BOOST_REQUIRE(error.isObject()); + BOOST_REQUIRE(error["message"].isString()); + if (error["message"].asString().find("pre-release compiler") == string::npos) + { + BOOST_CHECK_EQUAL( + dev::jsonCompactPrint(error), + "{\"component\":\"general\",\"formattedMessage\":\"fileA:1:23: ParserError: Expected identifier, got 'RBrace'\\n" + "contract A { function }\\n ^\\n\",\"message\":\"Expected identifier, got 'RBrace'\"," + "\"severity\":\"error\",\"sourceLocation\":{\"end\":22,\"file\":\"fileA\",\"start\":22},\"type\":\"ParserError\"}" + ); + } + } +} + BOOST_AUTO_TEST_CASE(output_selection_explicit) { char const* input = R"( @@ -403,6 +451,71 @@ BOOST_AUTO_TEST_CASE(output_selection_dependent_contract_with_import) BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["abi"]), "[{\"constant\":false,\"inputs\":[],\"name\":\"f\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]"); } +BOOST_AUTO_TEST_CASE(filename_with_colon) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "outputSelection": { + "http://github.com/ethereum/solidity/std/StandardToken.sol": { + "A": [ + "abi" + ] + } + } + }, + "sources": { + "http://github.com/ethereum/solidity/std/StandardToken.sol": { + "content": "contract A { }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); + Json::Value contract = getContractResult(result, "http://github.com/ethereum/solidity/std/StandardToken.sol", "A"); + BOOST_CHECK(contract.isObject()); + BOOST_CHECK(contract["abi"].isArray()); + BOOST_CHECK_EQUAL(dev::jsonCompactPrint(contract["abi"]), "[]"); +} + +BOOST_AUTO_TEST_CASE(library_filename_with_colon) +{ + char const* input = R"( + { + "language": "Solidity", + "settings": { + "outputSelection": { + "fileA": { + "A": [ + "evm.bytecode" + ] + } + } + }, + "sources": { + "fileA": { + "content": "import \"git:library.sol\"; contract A { function f() returns (uint) { return L.g(); } }" + }, + "git:library.sol": { + "content": "library L { function g() returns (uint) { return 1; } }" + } + } + } + )"; + Json::Value result = compile(input); + BOOST_CHECK(containsAtMostWarnings(result)); + Json::Value contract = getContractResult(result, "fileA", "A"); + BOOST_CHECK(contract.isObject()); + BOOST_CHECK(contract["evm"]["bytecode"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"].isObject()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"].isArray()); + BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"][0].isObject()); +} + + BOOST_AUTO_TEST_SUITE_END() } |