From 9544df34d70dc99f43819e957d5c2d1e2c527b5e Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Fri, 28 Feb 2020 12:48:59 +0100 Subject: [PATCH 01/76] solc fuzzers: Use compiler stack for fuzzing --- test/tools/afl_fuzzer.cpp | 2 +- test/tools/fuzzer_common.cpp | 99 +++++++++++++++-------- test/tools/fuzzer_common.h | 3 +- test/tools/ossfuzz/solc_noopt_ossfuzz.cpp | 2 +- test/tools/ossfuzz/solc_opt_ossfuzz.cpp | 2 +- 5 files changed, 72 insertions(+), 36 deletions(-) diff --git a/test/tools/afl_fuzzer.cpp b/test/tools/afl_fuzzer.cpp index 1ca5428e1..eff4bc55a 100644 --- a/test/tools/afl_fuzzer.cpp +++ b/test/tools/afl_fuzzer.cpp @@ -124,7 +124,7 @@ Allowed options)", else if (arguments.count("standard-json")) FuzzerUtil::testStandardCompiler(input, quiet); else - FuzzerUtil::testCompiler(input, optimize, quiet); + FuzzerUtil::testCompilerJsonInterface(input, optimize, quiet); } catch (...) { diff --git a/test/tools/fuzzer_common.cpp b/test/tools/fuzzer_common.cpp index f66c2e841..e4de0f185 100644 --- a/test/tools/fuzzer_common.cpp +++ b/test/tools/fuzzer_common.cpp @@ -17,28 +17,87 @@ #include +#include + #include + #include #include + #include +#include + #include using namespace std; using namespace solidity; using namespace solidity::util; using namespace solidity::evmasm; +using namespace solidity::langutil; -static vector s_evmVersions = { - "homestead", - "tangerineWhistle", - "spuriousDragon", - "byzantium", - "constantinople", - "petersburg", - "istanbul" +static vector s_evmVersions = { + EVMVersion::homestead(), + EVMVersion::tangerineWhistle(), + EVMVersion::spuriousDragon(), + EVMVersion::byzantium(), + EVMVersion::constantinople(), + EVMVersion::petersburg(), + EVMVersion::istanbul(), + EVMVersion::berlin() }; +void FuzzerUtil::testCompilerJsonInterface(string const& _input, bool _optimize, bool _quiet) +{ + if (!_quiet) + cout << "Testing compiler " << (_optimize ? "with" : "without") << " optimizer." << endl; + + Json::Value config = Json::objectValue; + config["language"] = "Solidity"; + config["sources"] = Json::objectValue; + config["sources"][""] = Json::objectValue; + config["sources"][""]["content"] = _input; + config["settings"] = Json::objectValue; + config["settings"]["optimizer"] = Json::objectValue; + config["settings"]["optimizer"]["enabled"] = _optimize; + config["settings"]["optimizer"]["runs"] = 200; + config["settings"]["evmVersion"] = "berlin"; + + // Enable all SourceUnit-level outputs. + config["settings"]["outputSelection"]["*"][""][0] = "*"; + // Enable all Contract-level outputs. + config["settings"]["outputSelection"]["*"]["*"][0] = "*"; + + runCompiler(jsonCompactPrint(config), _quiet); +} + +void FuzzerUtil::testCompiler(string const& _input, bool _optimize) +{ + frontend::CompilerStack compiler; + EVMVersion evmVersion = s_evmVersions[_input.size() % s_evmVersions.size()]; + frontend::OptimiserSettings optimiserSettings; + if (_optimize) + optimiserSettings = frontend::OptimiserSettings::standard(); + else + optimiserSettings = frontend::OptimiserSettings::minimal(); + compiler.setSources({{"", _input}}); + compiler.setEVMVersion(evmVersion); + compiler.setOptimiserSettings(optimiserSettings); + try + { + compiler.compile(); + } + catch (Error const&) + { + } + catch (FatalError const&) + { + } + catch (UnimplementedFeatureError const&) + { + } +} + void FuzzerUtil::runCompiler(string const& _input, bool _quiet) { if (!_quiet) @@ -73,30 +132,6 @@ void FuzzerUtil::runCompiler(string const& _input, bool _quiet) } } -void FuzzerUtil::testCompiler(string const& _input, bool _optimize, bool _quiet) -{ - if (!_quiet) - cout << "Testing compiler " << (_optimize ? "with" : "without") << " optimizer." << endl; - - Json::Value config = Json::objectValue; - config["language"] = "Solidity"; - config["sources"] = Json::objectValue; - config["sources"][""] = Json::objectValue; - config["sources"][""]["content"] = _input; - config["settings"] = Json::objectValue; - config["settings"]["optimizer"] = Json::objectValue; - config["settings"]["optimizer"]["enabled"] = _optimize; - config["settings"]["optimizer"]["runs"] = 200; - config["settings"]["evmVersion"] = s_evmVersions[_input.size() % s_evmVersions.size()]; - - // Enable all SourceUnit-level outputs. - config["settings"]["outputSelection"]["*"][""][0] = "*"; - // Enable all Contract-level outputs. - config["settings"]["outputSelection"]["*"]["*"][0] = "*"; - - runCompiler(jsonCompactPrint(config), _quiet); -} - void FuzzerUtil::testConstantOptimizer(string const& _input, bool _quiet) { if (!_quiet) diff --git a/test/tools/fuzzer_common.h b/test/tools/fuzzer_common.h index edf196c1c..801c8c1c1 100644 --- a/test/tools/fuzzer_common.h +++ b/test/tools/fuzzer_common.h @@ -24,7 +24,8 @@ struct FuzzerUtil { static void runCompiler(std::string const& _input, bool _quiet); - static void testCompiler(std::string const& _input, bool _optimize, bool _quiet); + static void testCompilerJsonInterface(std::string const& _input, bool _optimize, bool _quiet); static void testConstantOptimizer(std::string const& _input, bool _quiet); static void testStandardCompiler(std::string const& _input, bool _quiet); + static void testCompiler(std::string const& _input, bool _optimize); }; diff --git a/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp b/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp index 3a2ac5f27..efed3769f 100644 --- a/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp +++ b/test/tools/ossfuzz/solc_noopt_ossfuzz.cpp @@ -24,7 +24,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) if (_size <= 600) { string input(reinterpret_cast(_data), _size); - FuzzerUtil::testCompiler(input, /*optimize=*/false, /*quiet=*/true); + FuzzerUtil::testCompiler(input, /*optimize=*/false); } return 0; } diff --git a/test/tools/ossfuzz/solc_opt_ossfuzz.cpp b/test/tools/ossfuzz/solc_opt_ossfuzz.cpp index 72a59cba4..fc431ffe5 100644 --- a/test/tools/ossfuzz/solc_opt_ossfuzz.cpp +++ b/test/tools/ossfuzz/solc_opt_ossfuzz.cpp @@ -24,7 +24,7 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t const* _data, size_t _size) if (_size <= 600) { string input(reinterpret_cast(_data), _size); - FuzzerUtil::testCompiler(input, /*optimize=*/true, /*quiet=*/true); + FuzzerUtil::testCompiler(input, /*optimize=*/true); } return 0; } From 7107ef13a7a3cd65cb805645f8e445fe692145bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 11 Mar 2020 21:15:12 +0100 Subject: [PATCH 02/76] [yul-phaser] Program: Add parseObject() --- tools/yulPhaser/Program.cpp | 41 +++++++++++++++++++++++++++++++++++++ tools/yulPhaser/Program.h | 4 ++++ 2 files changed, 45 insertions(+) diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp index c397cd1f4..7dcfddd79 100644 --- a/tools/yulPhaser/Program.cpp +++ b/tools/yulPhaser/Program.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -136,6 +137,46 @@ variant, ErrorList> Program::parseSource(Dialect const& _diale return variant, ErrorList>(move(ast)); } +variant, ErrorList> Program::parseObject(Dialect const& _dialect, CharStream _source) +{ + ErrorList errors; + ErrorReporter errorReporter(errors); + auto scanner = make_shared(move(_source)); + + ObjectParser parser(errorReporter, _dialect); + shared_ptr object = parser.parse(scanner, false); + if (object == nullptr || !errorReporter.errors().empty()) + // NOTE: It's possible to get errors even if the returned object is non-null. + // For example when there are errors in a nested object. + return errors; + + Object* deployedObject = nullptr; + if (object->subObjects.size() > 0) + for (auto& subObject: object->subObjects) + // solc --ir produces an object with a subobject of the same name as the outer object + // but suffixed with "_deployed". + // The other object references the nested one which makes analysis fail. Below we try to + // extract just the nested one for that reason. This is just a heuristic. If there's no + // subobject with such a suffix we fall back to accepting the whole object as is. + if (subObject != nullptr && subObject->name.str() == object->name.str() + "_deployed") + { + deployedObject = dynamic_cast(subObject.get()); + if (deployedObject != nullptr) + break; + } + Object* selectedObject = (deployedObject != nullptr ? deployedObject : object.get()); + + // NOTE: I'm making a copy of the whole AST to get unique_ptr rather than shared_ptr. + // This is a slight performance hit but it's much less than the parsing itself. + // unique_ptr lets me be sure that two Program instances can never share the AST by mistake. + // The public API of the class does not provide access to the smart pointer so it won't be hard + // to switch to shared_ptr if the copying turns out to be an issue (though it would be better + // to refactor ObjectParser and Object to use unique_ptr instead). + auto astCopy = make_unique(get(ASTCopier{}(*selectedObject->code))); + + return variant, ErrorList>(move(astCopy)); +} + variant, ErrorList> Program::analyzeAST(Dialect const& _dialect, Block const& _ast) { ErrorList errors; diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h index 6da9751b9..df44f2ba4 100644 --- a/tools/yulPhaser/Program.h +++ b/tools/yulPhaser/Program.h @@ -98,6 +98,10 @@ private: yul::Dialect const& _dialect, langutil::CharStream _source ); + static std::variant, langutil::ErrorList> parseObject( + yul::Dialect const& _dialect, + langutil::CharStream _source + ); static std::variant, langutil::ErrorList> analyzeAST( yul::Dialect const& _dialect, yul::Block const& _ast From 29186f9951097a262ad97f0eba5afd2b07f911c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 11 Mar 2020 21:16:01 +0100 Subject: [PATCH 03/76] [yul-phaser] Program: Switch from using parseCode() to parseObject() --- test/yulPhaser/Program.cpp | 126 ++++++++++++++++++++++++++++++++++++ tools/yulPhaser/Program.cpp | 17 +---- tools/yulPhaser/Program.h | 4 -- 3 files changed, 127 insertions(+), 20 deletions(-) diff --git a/test/yulPhaser/Program.cpp b/test/yulPhaser/Program.cpp index 5d4e012f3..e69f0fc5c 100644 --- a/test/yulPhaser/Program.cpp +++ b/test/yulPhaser/Program.cpp @@ -189,6 +189,132 @@ BOOST_AUTO_TEST_CASE(load_should_throw_InvalidProgram_if_program_cant_be_analyze BOOST_TEST(holds_alternative(Program::load(sourceStream))); } +BOOST_AUTO_TEST_CASE(load_should_accept_yul_objects_as_input) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " mstore(64, 128)\n" + " if iszero(calldatasize()) {}\n" + " revert(0, 0)\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); +} + +BOOST_AUTO_TEST_CASE(load_should_return_errors_if_analysis_of_object_code_fails) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " return(0, datasize(\"C_178_deployed\"))\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); +} + +BOOST_AUTO_TEST_CASE(load_should_return_errors_if_parsing_of_nested_object_fails) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " return(0, datasize(\"C_178_deployed\"))\n" + " }\n" + " object \"duplicate_name\" {\n" + " code {\n" + " mstore(64, 128)\n" + " }\n" + " }\n" + " object \"duplicate_name\" {\n" + " code {\n" + " mstore(64, 128)\n" + " }\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); +} + +BOOST_AUTO_TEST_CASE(load_should_extract_nested_object_with_deployed_suffix_if_present) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " return(0, datasize(\"C_178_deployed\"))\n" + " }\n" + " object \"C_178_deployed\" {\n" + " code {\n" + " mstore(64, 128)\n" + " if iszero(calldatasize()) {}\n" + " revert(0, 0)\n" + " }\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); +} + +BOOST_AUTO_TEST_CASE(load_should_fall_back_to_parsing_the_whole_object_if_there_is_no_subobject_with_the_right_name) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " mstore(64, 128)\n" + " }\n" + " object \"subobject\" {\n" + " code {\n" + " if iszero(calldatasize()) {}\n" + " revert(0, 0)\n" + " }\n" + " }\n" + " object \"C_177_deployed\" {\n" + " code {\n" + " if iszero(calldatasize()) {}\n" + " revert(0, 0)\n" + " }\n" + " }\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); + + Block const& parentBlock = skipRedundantBlocks(get(programOrErrors).ast()); + BOOST_TEST(parentBlock.statements.size() == 1); + BOOST_TEST(holds_alternative(parentBlock.statements[0])); +} + +BOOST_AUTO_TEST_CASE(load_should_ignore_data_in_objects) +{ + string sourceCode( + "object \"C_178\" {\n" + " code {\n" + " mstore(64, 128)\n" + " }\n" + " data \"C_178_deployed\" hex\"4123\"\n" + "}\n" + ); + CharStream sourceStream(sourceCode, current_test_case().p_name); + auto programOrErrors = Program::load(sourceStream); + + BOOST_TEST(holds_alternative(programOrErrors)); +} + BOOST_AUTO_TEST_CASE(optimise) { string sourceCode( diff --git a/tools/yulPhaser/Program.cpp b/tools/yulPhaser/Program.cpp index 7dcfddd79..a457750d2 100644 --- a/tools/yulPhaser/Program.cpp +++ b/tools/yulPhaser/Program.cpp @@ -78,7 +78,7 @@ variant Program::load(CharStream& _sourceCode) // ASSUMPTION: parseSource() rewinds the stream on its own Dialect const& dialect = EVMDialect::strictAssemblyForEVMObjects(EVMVersion{}); - variant, ErrorList> astOrErrors = parseSource(dialect, _sourceCode); + variant, ErrorList> astOrErrors = parseObject(dialect, _sourceCode); if (holds_alternative(astOrErrors)) return get(astOrErrors); @@ -122,21 +122,6 @@ string Program::toJson() const return jsonPrettyPrint(serializedAst); } -variant, ErrorList> Program::parseSource(Dialect const& _dialect, CharStream _source) -{ - ErrorList errors; - ErrorReporter errorReporter(errors); - auto scanner = make_shared(move(_source)); - Parser parser(errorReporter, _dialect); - - unique_ptr ast = parser.parse(scanner, false); - if (ast == nullptr) - return errors; - - assert(errorReporter.errors().empty()); - return variant, ErrorList>(move(ast)); -} - variant, ErrorList> Program::parseObject(Dialect const& _dialect, CharStream _source) { ErrorList errors; diff --git a/tools/yulPhaser/Program.h b/tools/yulPhaser/Program.h index df44f2ba4..10fed6de2 100644 --- a/tools/yulPhaser/Program.h +++ b/tools/yulPhaser/Program.h @@ -94,10 +94,6 @@ private: m_nameDispenser(_dialect, *m_ast, {}) {} - static std::variant, langutil::ErrorList> parseSource( - yul::Dialect const& _dialect, - langutil::CharStream _source - ); static std::variant, langutil::ErrorList> parseObject( yul::Dialect const& _dialect, langutil::CharStream _source From a66ceb11c683d1a2cc1c62f028163ca3e9e2305e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 20 Mar 2020 17:44:34 +0100 Subject: [PATCH 04/76] [yul-phaser] Add --prefix option --- test/yulPhaser/Phaser.cpp | 38 +++++++++++++++++++++++++++++++++----- tools/yulPhaser/Phaser.cpp | 15 +++++++++++++++ tools/yulPhaser/Phaser.h | 1 + 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/test/yulPhaser/Phaser.cpp b/test/yulPhaser/Phaser.cpp index aab34db61..04dfbe3dc 100644 --- a/test/yulPhaser/Phaser.cpp +++ b/test/yulPhaser/Phaser.cpp @@ -324,11 +324,14 @@ BOOST_AUTO_TEST_CASE(build_should_load_programs_from_files) { TemporaryDirectory tempDir; vector sources{"{}", "{{}}", "{{{}}}"}; - ProgramFactory::Options options{/* inputFiles = */ { - tempDir.memberPath("program1.yul"), - tempDir.memberPath("program2.yul"), - tempDir.memberPath("program3.yul"), - }}; + ProgramFactory::Options options{ + /* inputFiles = */ { + tempDir.memberPath("program1.yul"), + tempDir.memberPath("program2.yul"), + tempDir.memberPath("program3.yul"), + }, + /* prefix = */ "", + }; for (size_t i = 0; i < sources.size(); ++i) { @@ -346,6 +349,31 @@ BOOST_AUTO_TEST_CASE(build_should_load_programs_from_files) } } +BOOST_AUTO_TEST_CASE(build_should_apply_prefix) +{ + TemporaryDirectory tempDir; + ProgramFactory::Options options{ + /* inputFiles = */ {tempDir.memberPath("program1.yul")}, + /* prefix = */ "f", + }; + + CharStream nestedSource("{{{let x:= 1}}}", ""); + Program nestedProgram = get(Program::load(nestedSource)); + Program flatProgram = get(Program::load(nestedSource)); + flatProgram.optimise(Chromosome("f").optimisationSteps()); + assert(toString(nestedProgram) != toString(flatProgram)); + + { + ofstream tmpFile(options.inputFiles[0]); + tmpFile << nestedSource.source() << endl; + } + + vector programs = ProgramFactory::build(options); + + BOOST_TEST(programs.size() == 1); + BOOST_TEST(toString(programs[0]) == toString(flatProgram)); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index da0296eff..b29938f22 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -285,6 +285,7 @@ ProgramFactory::Options ProgramFactory::Options::fromCommandLine(po::variables_m { return { _arguments["input-files"].as>(), + _arguments["prefix"].as(), }; } @@ -300,6 +301,8 @@ vector ProgramFactory::build(Options const& _options) cerr << get(programOrErrors) << endl; assertThrow(false, InvalidProgram, "Failed to load program " + path); } + + get(programOrErrors).optimise(Chromosome(_options.prefix).optimisationSteps()); inputPrograms.push_back(move(get(programOrErrors))); } @@ -348,6 +351,18 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription() generalDescription.add_options() ("help", "Show help message and exit.") ("input-files", po::value>()->required()->value_name(""), "Input files.") + ( + "prefix", + po::value()->value_name("")->default_value(""), + "Initial optimisation steps automatically applied to every input program.\n" + "The result is treated as if it was the actual input, i.e. the steps are not considered " + "a part of the chromosomes and cannot be mutated. The values of relative metric values " + "are also relative to the fitness of a program with these steps applied rather than the " + "fitness of the original program.\n" + "Note that phaser always adds a 'hgo' prefix to ensure that chromosomes can " + "contain arbitrary optimisation steps. This implicit prefix cannot be changed or " + "or removed using this option. The value given here is applied after it." + ) ("seed", po::value()->value_name(""), "Seed for the random number generator.") ( "rounds", diff --git a/tools/yulPhaser/Phaser.h b/tools/yulPhaser/Phaser.h index 2e72c31f6..9814c9111 100644 --- a/tools/yulPhaser/Phaser.h +++ b/tools/yulPhaser/Phaser.h @@ -169,6 +169,7 @@ public: struct Options { std::vector inputFiles; + std::string prefix; static Options fromCommandLine(boost::program_options::variables_map const& _arguments); }; From 42b143098e4fdea625da062575f400261b806558 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 24 Mar 2020 13:04:07 +0100 Subject: [PATCH 05/76] Add natspec test with too many return tags --- .../natspec/docstring_too_many_return_tags.sol | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 test/libsolidity/syntaxTests/natspec/docstring_too_many_return_tags.sol diff --git a/test/libsolidity/syntaxTests/natspec/docstring_too_many_return_tags.sol b/test/libsolidity/syntaxTests/natspec/docstring_too_many_return_tags.sol new file mode 100644 index 000000000..7197a4edb --- /dev/null +++ b/test/libsolidity/syntaxTests/natspec/docstring_too_many_return_tags.sol @@ -0,0 +1,8 @@ +abstract contract C { + /// @param id Some identifier + /// @return value Some value + /// @return value2 Some value 2 + function vote(uint id) public virtual returns (uint value); +} +// ---- +// DocstringParsingError: (26-121): Documentation tag "@return value2 Some value 2" exceeds the number of return parameters. From 04d8ad2ae1425667d4340cf93b047f40dfbd8294 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Tue, 10 Mar 2020 14:30:04 +0100 Subject: [PATCH 06/76] Legacy codegeneration for immutable state variables. --- libevmasm/Assembly.cpp | 89 +++++++++++++++- libevmasm/Assembly.h | 5 + libevmasm/AssemblyItem.cpp | 23 ++++ libevmasm/AssemblyItem.h | 8 +- libevmasm/KnownState.cpp | 4 + libevmasm/LinkerObject.h | 4 + libevmasm/SemanticInformation.cpp | 2 + libsolidity/ast/Types.cpp | 10 ++ libsolidity/ast/Types.h | 2 + libsolidity/codegen/CompilerContext.cpp | 56 ++++++++++ libsolidity/codegen/CompilerContext.h | 23 +++- libsolidity/codegen/CompilerUtils.cpp | 4 +- libsolidity/codegen/ContractCompiler.cpp | 27 ++++- libsolidity/codegen/ContractCompiler.h | 1 + libsolidity/codegen/ExpressionCompiler.cpp | 2 +- libsolidity/codegen/LValue.cpp | 36 +++++++ libsolidity/codegen/LValue.h | 45 ++++++-- libyul/AssemblyStack.cpp | 1 + test/libevmasm/Optimiser.cpp | 100 ++++++++++++++++++ .../immutable/external_function_pointer.sol | 20 ++++ .../immutable/read_during_creation.sol | 13 +++ .../semanticTests/immutable/stub.sol | 13 +++ 22 files changed, 471 insertions(+), 17 deletions(-) create mode 100644 test/libsolidity/semanticTests/immutable/external_function_pointer.sol create mode 100644 test/libsolidity/semanticTests/immutable/read_during_creation.sol create mode 100644 test/libsolidity/semanticTests/immutable/stub.sol diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index 7ed44d236..e67ccb8f5 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -283,6 +283,24 @@ Json::Value Assembly::assemblyJSON(map const& _sourceIndices) createJsonValue("PUSHDEPLOYADDRESS", sourceIndex, i.location().start, i.location().end) ); break; + case PushImmutable: + collection.append(createJsonValue( + "PUSHIMMUTABLE", + sourceIndex, + i.location().start, + i.location().end, + m_immutables.at(h256(i.data())) + )); + break; + case AssignImmutable: + collection.append(createJsonValue( + "ASSIGNIMMUTABLE", + sourceIndex, + i.location().start, + i.location().end, + m_immutables.at(h256(i.data())) + )); + break; case Tag: collection.append( createJsonValue("tag", sourceIndex, i.location().start, i.location().end, toString(i.data()))); @@ -333,6 +351,20 @@ AssemblyItem Assembly::newPushLibraryAddress(string const& _identifier) return AssemblyItem{PushLibraryAddress, h}; } +AssemblyItem Assembly::newPushImmutable(string const& _identifier) +{ + h256 h(util::keccak256(_identifier)); + m_immutables[h] = _identifier; + return AssemblyItem{PushImmutable, h}; +} + +AssemblyItem Assembly::newImmutableAssignment(string const& _identifier) +{ + h256 h(util::keccak256(_identifier)); + m_immutables[h] = _identifier; + return AssemblyItem{AssignImmutable, h}; +} + Assembly& Assembly::optimise(bool _enable, EVMVersion _evmVersion, bool _isCreation, size_t _runs) { OptimiserSettings settings; @@ -495,16 +527,44 @@ LinkerObject const& Assembly::assemble() const // Otherwise ensure the object is actually clear. assertThrow(m_assembledObject.linkReferences.empty(), AssemblyException, "Unexpected link references."); + LinkerObject& ret = m_assembledObject; + size_t subTagSize = 1; + map> immutableReferencesBySub; for (auto const& sub: m_subs) { - sub->assemble(); + auto const& linkerObject = sub->assemble(); + if (!linkerObject.immutableReferences.empty()) + { + assertThrow( + immutableReferencesBySub.empty(), + AssemblyException, + "More than one sub-assembly references immutables." + ); + immutableReferencesBySub = linkerObject.immutableReferences; + } for (size_t tagPos: sub->m_tagPositionsInBytecode) if (tagPos != size_t(-1) && tagPos > subTagSize) subTagSize = tagPos; } - LinkerObject& ret = m_assembledObject; + bool setsImmutables = false; + bool pushesImmutables = false; + + for (auto const& i: m_items) + if (i.type() == AssignImmutable) + { + i.setImmutableOccurrences(immutableReferencesBySub[i.data()].size()); + setsImmutables = true; + } + else if (i.type() == PushImmutable) + pushesImmutables = true; + if (setsImmutables || pushesImmutables) + assertThrow( + setsImmutables != pushesImmutables, + AssemblyException, + "Cannot push and assign immutables in the same assembly subroutine." + ); size_t bytesRequiredForCode = bytesRequired(subTagSize); m_tagPositionsInBytecode = vector(m_usedTags, -1); @@ -598,6 +658,24 @@ LinkerObject const& Assembly::assemble() const ret.linkReferences[ret.bytecode.size()] = m_libraries.at(i.data()); ret.bytecode.resize(ret.bytecode.size() + 20); break; + case PushImmutable: + ret.bytecode.push_back(uint8_t(Instruction::PUSH32)); + ret.immutableReferences[i.data()].emplace_back(ret.bytecode.size()); + ret.bytecode.resize(ret.bytecode.size() + 32); + break; + case AssignImmutable: + for (auto const& offset: immutableReferencesBySub[i.data()]) + { + ret.bytecode.push_back(uint8_t(Instruction::DUP1)); + // TODO: should we make use of the constant optimizer methods for pushing the offsets? + bytes offsetBytes = toCompactBigEndian(u256(offset)); + ret.bytecode.push_back(uint8_t(Instruction::PUSH1) - 1 + offsetBytes.size()); + ret.bytecode += offsetBytes; + ret.bytecode.push_back(uint8_t(Instruction::MSTORE)); + } + immutableReferencesBySub.erase(i.data()); + ret.bytecode.push_back(uint8_t(Instruction::POP)); + break; case PushDeployTimeAddress: ret.bytecode.push_back(uint8_t(Instruction::PUSH20)); ret.bytecode.resize(ret.bytecode.size() + 20); @@ -615,6 +693,13 @@ LinkerObject const& Assembly::assemble() const } } + assertThrow( + immutableReferencesBySub.empty(), + AssemblyException, + "Some immutables were read from but never assigned." + ); + + if (!m_subs.empty() || !m_data.empty() || !m_auxiliaryData.empty()) // Append an INVALID here to help tests find miscompilation. ret.bytecode.push_back(uint8_t(Instruction::INVALID)); diff --git a/libevmasm/Assembly.h b/libevmasm/Assembly.h index a76538375..e9e3630a8 100644 --- a/libevmasm/Assembly.h +++ b/libevmasm/Assembly.h @@ -54,6 +54,8 @@ public: Assembly& sub(size_t _sub) { return *m_subs.at(_sub); } AssemblyItem newPushSubSize(u256 const& _subId) { return AssemblyItem(PushSubSize, _subId); } AssemblyItem newPushLibraryAddress(std::string const& _identifier); + AssemblyItem newPushImmutable(std::string const& _identifier); + AssemblyItem newImmutableAssignment(std::string const& _identifier); AssemblyItem const& append(AssemblyItem const& _i); AssemblyItem const& append(bytes const& _data) { return append(newData(_data)); } @@ -64,6 +66,8 @@ public: /// after compilation and CODESIZE is not an option. void appendProgramSize() { append(AssemblyItem(PushProgramSize)); } void appendLibraryAddress(std::string const& _identifier) { append(newPushLibraryAddress(_identifier)); } + void appendImmutable(std::string const& _identifier) { append(newPushImmutable(_identifier)); } + void appendImmutableAssignment(std::string const& _identifier) { append(newImmutableAssignment(_identifier)); } AssemblyItem appendJump() { auto ret = append(newPushTag()); append(Instruction::JUMP); return ret; } AssemblyItem appendJumpI() { auto ret = append(newPushTag()); append(Instruction::JUMPI); return ret; } @@ -166,6 +170,7 @@ protected: std::vector> m_subs; std::map m_strings; std::map m_libraries; ///< Identifiers of libraries to be linked. + std::map m_immutables; ///< Identifiers of immutables. mutable LinkerObject m_assembledObject; mutable std::vector m_tagPositionsInBytecode; diff --git a/libevmasm/AssemblyItem.cpp b/libevmasm/AssemblyItem.cpp index c238a3a3c..610c6f5dd 100644 --- a/libevmasm/AssemblyItem.cpp +++ b/libevmasm/AssemblyItem.cpp @@ -80,6 +80,13 @@ unsigned AssemblyItem::bytesRequired(unsigned _addressLength) const case PushLibraryAddress: case PushDeployTimeAddress: return 1 + 20; + case PushImmutable: + return 1 + 32; + case AssignImmutable: + if (m_immutableOccurrences) + return 1 + (3 + 32) * *m_immutableOccurrences; + else + return 1 + (3 + 32) * 1024; // 1024 occurrences are beyond the maximum code size anyways. default: break; } @@ -90,6 +97,8 @@ int AssemblyItem::arguments() const { if (type() == Operation) return instructionInfo(instruction()).args; + else if (type() == AssignImmutable) + return 1; else return 0; } @@ -108,6 +117,7 @@ int AssemblyItem::returnValues() const case PushSubSize: case PushProgramSize: case PushLibraryAddress: + case PushImmutable: case PushDeployTimeAddress: return 1; case Tag: @@ -135,6 +145,7 @@ bool AssemblyItem::canBeFunctional() const case PushProgramSize: case PushLibraryAddress: case PushDeployTimeAddress: + case PushImmutable: return true; case Tag: return false; @@ -210,6 +221,12 @@ string AssemblyItem::toAssemblyText() const case PushDeployTimeAddress: text = string("deployTimeAddress()"); break; + case PushImmutable: + text = string("immutable(\"") + toHex(util::toCompactBigEndian(data(), 1), util::HexPrefix::Add) + "\")"; + break; + case AssignImmutable: + text = string("assignImmutable(\"") + toHex(util::toCompactBigEndian(data(), 1), util::HexPrefix::Add) + "\")"; + break; case UndefinedItem: assertThrow(false, AssemblyException, "Invalid assembly item."); break; @@ -275,6 +292,12 @@ ostream& solidity::evmasm::operator<<(ostream& _out, AssemblyItem const& _item) case PushDeployTimeAddress: _out << " PushDeployTimeAddress"; break; + case PushImmutable: + _out << " PushImmutable"; + break; + case AssignImmutable: + _out << " AssignImmutable"; + break; case UndefinedItem: _out << " ???"; break; diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index e506a9fdb..fc8c63c67 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -44,7 +44,9 @@ enum AssemblyItemType { Tag, PushData, PushLibraryAddress, ///< Push a currently unknown address of another (library) contract. - PushDeployTimeAddress ///< Push an address to be filled at deploy time. Should not be touched by the optimizer. + PushDeployTimeAddress, ///< Push an address to be filled at deploy time. Should not be touched by the optimizer. + PushImmutable, ///< Push the currently unknown value of an immutable variable. The actual value will be filled in by the constructor. + AssignImmutable ///< Assigns the current value on the stack to an immutable variable. Only valid during creation code. }; class Assembly; @@ -153,6 +155,8 @@ public: size_t m_modifierDepth = 0; + void setImmutableOccurrences(size_t _n) const { m_immutableOccurrences = std::make_shared(_n); } + private: AssemblyItemType m_type; Instruction m_instruction; ///< Only valid if m_type == Operation @@ -162,6 +166,8 @@ private: /// Pushed value for operations with data to be determined during assembly stage, /// e.g. PushSubSize, PushTag, PushSub, etc. mutable std::shared_ptr m_pushedValue; + /// Number of PushImmutable's with the same hash. Only used for AssignImmutable. + mutable std::shared_ptr m_immutableOccurrences; }; inline size_t bytesRequired(AssemblyItems const& _items, size_t _addressLength) diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index 7e447a9a1..5f1ca736c 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -91,6 +91,10 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool { // can be ignored } + else if (_item.type() == AssignImmutable) + // Since AssignImmutable breaks blocks, it should be fine to only consider its changes to the stack, which + // is the same as POP. + return feedItem(AssemblyItem(Instruction::POP), _copyItem); else if (_item.type() != Operation) { assertThrow(_item.deposit() == 1, InvalidDeposit, ""); diff --git a/libevmasm/LinkerObject.h b/libevmasm/LinkerObject.h index e7a1e9667..ccf5588bb 100644 --- a/libevmasm/LinkerObject.h +++ b/libevmasm/LinkerObject.h @@ -40,6 +40,10 @@ struct LinkerObject /// need to be replaced by the actual addresses by the linker. std::map linkReferences; + /// Map from hashes of the identifiers of immutable variables to a list of offsets into the bytecode + /// that refer to their values. + std::map> immutableReferences; + /// Appends the bytecode of @a _other and incorporates its link references. void append(LinkerObject const& _other); diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 4ea9a01ae..76eeb5956 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -36,6 +36,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool case UndefinedItem: case Tag: case PushDeployTimeAddress: + case AssignImmutable: return true; case Push: case PushString: @@ -45,6 +46,7 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool case PushProgramSize: case PushData: case PushLibraryAddress: + case PushImmutable: return false; case Operation: { diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 9ed35e694..76eeb4fce 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2012,6 +2012,16 @@ vector> ContractType::stateVar return variablesAndOffsets; } +vector ContractType::immutableVariables() const +{ + vector variables; + for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts)) + for (VariableDeclaration const* variable: contract->stateVariables()) + if (variable->immutable()) + variables.push_back(variable); + return variables; +} + vector> ContractType::makeStackItems() const { if (m_super) diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 73943b18a..d0e774cb7 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -895,6 +895,8 @@ public: /// @returns a list of all state variables (including inherited) of the contract and their /// offsets in storage. std::vector> stateVariables() const; + /// @returns a list of all immutable variables (including inherited) of the contract. + std::vector immutableVariables() const; protected: std::vector> makeStackItems() const override; private: diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index cb195c49f..26cff59ec 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -71,6 +71,55 @@ void CompilerContext::addStateVariable( m_stateVariables[&_declaration] = make_pair(_storageOffset, _byteOffset); } +void CompilerContext::addImmutable(VariableDeclaration const& _variable) +{ + solAssert(_variable.immutable(), "Attempted to register a non-immutable variable as immutable."); + solUnimplementedAssert(_variable.annotation().type->isValueType(), "Only immutable variables of value type are supported."); + solAssert(m_runtimeContext, "Attempted to register an immutable variable for runtime code generation."); + m_immutableVariables[&_variable] = CompilerUtils::generalPurposeMemoryStart + *m_reservedMemory; + solAssert(_variable.annotation().type->memoryHeadSize() == 32, "Memory writes might overlap."); + *m_reservedMemory += _variable.annotation().type->memoryHeadSize(); +} + +size_t CompilerContext::immutableMemoryOffset(VariableDeclaration const& _variable) const +{ + solAssert(m_immutableVariables.count(&_variable), "Memory offset of unknown immutable queried."); + solAssert(m_runtimeContext, "Attempted to fetch the memory offset of an immutable variable during runtime code generation."); + return m_immutableVariables.at(&_variable); +} + +vector CompilerContext::immutableVariableSlotNames(VariableDeclaration const& _variable) +{ + string baseName = + _variable.annotation().contract->fullyQualifiedName() + + "." + + _variable.name() + + " (" + + to_string(_variable.id()) + + ")"; + solAssert(_variable.annotation().type->sizeOnStack() > 0, ""); + if (_variable.annotation().type->sizeOnStack() == 1) + return {baseName}; + vector names; + auto collectSlotNames = [&](string const& _baseName, TypePointer type, auto const& _recurse) -> void { + for (auto const& [slot, type]: type->stackItems()) + if (type) + _recurse(_baseName + " " + slot, type, _recurse); + else + names.emplace_back(_baseName); + }; + collectSlotNames(baseName, _variable.annotation().type, collectSlotNames); + return names; +} + +size_t CompilerContext::reservedMemory() +{ + solAssert(m_reservedMemory.has_value(), "Reserved memory was used before "); + size_t reservedMemory = *m_reservedMemory; + m_reservedMemory = std::nullopt; + return reservedMemory; +} + void CompilerContext::startFunction(Declaration const& _function) { m_functionCompilationQueue.startFunction(_function); @@ -500,6 +549,13 @@ void CompilerContext::optimizeYul(yul::Object& _object, yul::EVMDialect const& _ #endif } +LinkerObject const& CompilerContext::assembledObject() const +{ + LinkerObject const& object = m_asm->assemble(); + solAssert(object.immutableReferences.empty(), "Leftover immutables."); + return object; +} + FunctionDefinition const& CompilerContext::resolveVirtualFunction( FunctionDefinition const& _function, vector::const_iterator _searchStart diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index 8c5775239..ec846af83 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -64,6 +64,7 @@ public: m_asm(std::make_shared()), m_evmVersion(_evmVersion), m_revertStrings(_revertStrings), + m_reservedMemory{0}, m_runtimeContext(_runtimeContext), m_abiFunctions(m_evmVersion, m_revertStrings, m_yulFunctionCollector), m_yulUtilFunctions(m_evmVersion, m_revertStrings, m_yulFunctionCollector) @@ -80,6 +81,16 @@ public: bool experimentalFeatureActive(ExperimentalFeature _feature) const { return m_experimentalFeatures.count(_feature); } void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset); + void addImmutable(VariableDeclaration const& _declaration); + + /// @returns the reserved memory for storing the value of the immutable @a _variable during contract creation. + size_t immutableMemoryOffset(VariableDeclaration const& _variable) const; + /// @returns a list of slot names referring to the stack slots of an immutable variable. + static std::vector immutableVariableSlotNames(VariableDeclaration const& _variable); + + /// @returns the reserved memory and resets it to mark it as used. + size_t reservedMemory(); + void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0); void removeVariable(Declaration const& _declaration); /// Removes all local variables currently allocated above _stackHeight. @@ -217,6 +228,10 @@ public: evmasm::AssemblyItem appendData(bytes const& _data) { return m_asm->append(_data); } /// Appends the address (virtual, will be filled in by linker) of a library. void appendLibraryAddress(std::string const& _identifier) { m_asm->appendLibraryAddress(_identifier); } + /// Appends an immutable variable. The value will be filled in by the constructor. + void appendImmutable(std::string const& _identifier) { m_asm->appendImmutable(_identifier); } + /// Appends an assignment to an immutable variable. Only valid in creation code. + void appendImmutableAssignment(std::string const& _identifier) { m_asm->appendImmutableAssignment(_identifier); } /// Appends a zero-address that can be replaced by something else at deploy time (if the /// position in bytecode is known). void appendDeployTimeAddress() { m_asm->append(evmasm::PushDeployTimeAddress); } @@ -282,7 +297,7 @@ public: return m_asm->assemblyJSON(_indicies); } - evmasm::LinkerObject const& assembledObject() const { return m_asm->assemble(); } + evmasm::LinkerObject const& assembledObject() const; evmasm::LinkerObject const& assembledRuntimeObject(size_t _subIndex) const { return m_asm->sub(_subIndex).assemble(); } /** @@ -355,6 +370,12 @@ private: std::map> m_otherCompilers; /// Storage offsets of state variables std::map> m_stateVariables; + /// Memory offsets reserved for the values of immutable variables during contract creation. + std::map m_immutableVariables; + /// Total amount of reserved memory. Reserved memory is used to store immutable variables during contract creation. + /// This has to be finalized before initialiseFreeMemoryPointer() is called. That function + /// will reset the optional to verify that. + std::optional m_reservedMemory = {0}; /// Offsets of local variables on the stack (relative to stack base). /// This needs to be a stack because if a modifier contains a local variable and this /// modifier is applied twice, the position of the variable needs to be restored diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index c559c0b7c..d4f35ad79 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -51,7 +51,9 @@ static_assert(CompilerUtils::generalPurposeMemoryStart >= CompilerUtils::zeroPoi void CompilerUtils::initialiseFreeMemoryPointer() { - m_context << u256(generalPurposeMemoryStart); + size_t reservedMemory = m_context.reservedMemory(); + solAssert(bigint(generalPurposeMemoryStart) + bigint(reservedMemory) < bigint(1) << 63, ""); + m_context << (u256(generalPurposeMemoryStart) + reservedMemory); storeFreeMemoryPointer(); } diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 6fffbd5b5..d5f5ffe33 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -130,6 +130,8 @@ void ContractCompiler::initializeContext( m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures); m_context.setOtherCompilers(_otherCompilers); m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); + if (m_runtimeCompiler) + registerImmutableVariables(_contract); CompilerUtils(m_context).initialiseFreeMemoryPointer(); registerStateVariables(_contract); m_context.resetVisitedNodes(&_contract); @@ -183,10 +185,26 @@ size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _cont m_context << deployRoutine; solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered"); + + ContractType contractType(_contract); + auto const& immutables = contractType.immutableVariables(); + // Push all immutable values on the stack. + for (auto const& immutable: immutables) + CompilerUtils(m_context).loadFromMemory(m_context.immutableMemoryOffset(*immutable), *immutable->annotation().type); m_context.pushSubroutineSize(m_context.runtimeSub()); - m_context << Instruction::DUP1; + if (immutables.empty()) + m_context << Instruction::DUP1; m_context.pushSubroutineOffset(m_context.runtimeSub()); m_context << u256(0) << Instruction::CODECOPY; + // Assign immutable values from stack in reversed order. + for (auto const& immutable: immutables | boost::adaptors::reversed) + { + auto slotNames = m_context.immutableVariableSlotNames(*immutable); + for (auto&& slotName: slotNames | boost::adaptors::reversed) + m_context.appendImmutableAssignment(slotName); + } + if (!immutables.empty()) + m_context.pushSubroutineSize(m_context.runtimeSub()); m_context << u256(0) << Instruction::RETURN; return m_context.runtimeSub(); @@ -521,6 +539,13 @@ void ContractCompiler::registerStateVariables(ContractDefinition const& _contrac m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var)); } +void ContractCompiler::registerImmutableVariables(ContractDefinition const& _contract) +{ + solAssert(m_runtimeCompiler, "Attempted to register immutables for runtime code generation."); + for (auto const& var: ContractType(_contract).immutableVariables()) + m_context.addImmutable(*var); +} + void ContractCompiler::initializeStateVariables(ContractDefinition const& _contract) { solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library."); diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h index 0a2ecf7e8..0916da281 100644 --- a/libsolidity/codegen/ContractCompiler.h +++ b/libsolidity/codegen/ContractCompiler.h @@ -99,6 +99,7 @@ private: void appendReturnValuePacker(TypePointers const& _typeParameters, bool _isLibrary); void registerStateVariables(ContractDefinition const& _contract); + void registerImmutableVariables(ContractDefinition const& _contract); void initializeStateVariables(ContractDefinition const& _contract); bool visit(VariableDeclaration const& _variableDeclaration) override; diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index d4f3d460d..c71d067c4 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -2436,7 +2436,7 @@ void ExpressionCompiler::appendVariable(VariableDeclaration const& _variable, Ex if (_variable.isConstant()) acceptAndConvert(*_variable.value(), *_variable.annotation().type); else if (_variable.immutable()) - solUnimplemented(""); + setLValue(_expression, _variable); else setLValueFromDeclaration(_variable, _expression); } diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 5fd17bf5c..4b1c80ede 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -144,6 +144,42 @@ void MemoryItem::setToZero(SourceLocation const&, bool _removeReference) const m_context << Instruction::POP; } + +ImmutableItem::ImmutableItem(CompilerContext& _compilerContext, VariableDeclaration const& _variable): + LValue(_compilerContext, _variable.annotation().type), m_variable(_variable) +{ + solAssert(_variable.immutable(), ""); +} + +void ImmutableItem::retrieveValue(SourceLocation const&, bool) const +{ + solUnimplementedAssert(m_dataType->isValueType(), ""); + solAssert(!m_context.runtimeContext(), "Tried to read immutable at construction time."); + for (auto&& slotName: m_context.immutableVariableSlotNames(m_variable)) + m_context.appendImmutable(slotName); +} + +void ImmutableItem::storeValue(Type const& _sourceType, SourceLocation const&, bool _move) const +{ + CompilerUtils utils(m_context); + solUnimplementedAssert(m_dataType->isValueType(), ""); + solAssert(_sourceType.isValueType(), ""); + + utils.convertType(_sourceType, *m_dataType, true); + m_context << m_context.immutableMemoryOffset(m_variable); + if (_move) + utils.moveIntoStack(m_dataType->sizeOnStack()); + else + utils.copyToStackTop(m_dataType->sizeOnStack() + 1, m_dataType->sizeOnStack()); + utils.storeInMemoryDynamic(*m_dataType, false); + m_context << Instruction::POP; +} + +void ImmutableItem::setToZero(SourceLocation const&, bool) const +{ + solAssert(false, "Attempted to set immutable variable to zero."); +} + StorageItem::StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration): StorageItem(_compilerContext, *_declaration.annotation().type) { diff --git a/libsolidity/codegen/LValue.h b/libsolidity/codegen/LValue.h index 1cd4def01..cd72f37e0 100644 --- a/libsolidity/codegen/LValue.h +++ b/libsolidity/codegen/LValue.h @@ -23,6 +23,7 @@ #pragma once #include +#include #include #include #include @@ -82,12 +83,12 @@ public: unsigned sizeOnStack() const override { return 0; } void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; @@ -108,12 +109,12 @@ public: MemoryItem(CompilerContext& _compilerContext, Type const& _type, bool _padded = true); unsigned sizeOnStack() const override { return 1; } void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; @@ -122,6 +123,30 @@ private: bool m_padded = false; }; +/** + * Reference to an immutable variable. During contract creation this refers to a location in memory. At the + * end of contract creation the values from these memory locations are copied into all occurrences of the immutable + * variable in the runtime code. + */ +class ImmutableItem: public LValue +{ +public: + ImmutableItem(CompilerContext& _compilerContext, VariableDeclaration const& _variable); + unsigned sizeOnStack() const override { return 0; } + void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; + void storeValue( + Type const& _sourceType, + langutil::SourceLocation const& _location = {}, + bool _move = false + ) const override; + void setToZero( + langutil::SourceLocation const& _location = {}, + bool _removeReference = true + ) const override; +private: + VariableDeclaration const& m_variable; +}; + /** * Reference to some item in storage. On the stack this is , * where 0 <= offset_inside_value < 32 and an offset of i means that the value is multiplied @@ -136,12 +161,12 @@ public: StorageItem(CompilerContext& _compilerContext, Type const& _type); unsigned sizeOnStack() const override { return 2; } void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; @@ -158,12 +183,12 @@ public: StorageByteArrayElement(CompilerContext& _compilerContext); unsigned sizeOnStack() const override { return 2; } void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; @@ -180,12 +205,12 @@ public: TupleObject(CompilerContext& _compilerContext, std::vector>&& _lvalues); unsigned sizeOnStack() const override; void retrieveValue(langutil::SourceLocation const& _location, bool _remove = false) const override; - virtual void storeValue( + void storeValue( Type const& _sourceType, langutil::SourceLocation const& _location = {}, bool _move = false ) const override; - virtual void setToZero( + void setToZero( langutil::SourceLocation const& _location = {}, bool _removeReference = true ) const override; diff --git a/libyul/AssemblyStack.cpp b/libyul/AssemblyStack.cpp index 7c0eca276..e64420bd9 100644 --- a/libyul/AssemblyStack.cpp +++ b/libyul/AssemblyStack.cpp @@ -203,6 +203,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const EthAssemblyAdapter adapter(assembly); compileEVM(adapter, false, m_optimiserSettings.optimizeStackAllocation); object.bytecode = make_shared(assembly.assemble()); + yulAssert(object.bytecode->immutableReferences.empty(), "Leftover immutables."); object.assembly = assembly.assemblyString(); object.sourceMappings = make_unique( evmasm::AssemblyItem::computeSourceMapping( diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index 4d93f4e76..782b687fc 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -112,6 +112,44 @@ namespace BOOST_AUTO_TEST_SUITE(Optimiser) +BOOST_AUTO_TEST_CASE(cse_push_immutable_same) +{ + AssemblyItem pushImmutable{PushImmutable, 0x1234}; + checkCSE({pushImmutable, pushImmutable}, {pushImmutable, Instruction::DUP1}); +} + +BOOST_AUTO_TEST_CASE(cse_push_immutable_different) +{ + AssemblyItems input{{PushImmutable, 0x1234},{PushImmutable, 0xABCD}}; + checkCSE(input, input); +} + +BOOST_AUTO_TEST_CASE(cse_assign_immutable) +{ + { + AssemblyItems input{u256(0x42), {AssignImmutable, 0x1234}}; + checkCSE(input, input); + } + { + AssemblyItems input{{AssignImmutable, 0x1234}}; + checkCSE(input, input); + } +} + + +BOOST_AUTO_TEST_CASE(cse_assign_immutable_breaks) +{ + AssemblyItems input = addDummyLocations(AssemblyItems{ + u256(0x42), + {AssignImmutable, 0x1234}, + Instruction::ORIGIN + }); + + evmasm::CommonSubexpressionEliminator cse{evmasm::KnownState()}; + // Make sure CSE breaks after AssignImmutable. + BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), false) == input.begin() + 2); +} + BOOST_AUTO_TEST_CASE(cse_intermediate_swap) { evmasm::KnownState state; @@ -798,6 +836,68 @@ BOOST_AUTO_TEST_CASE(block_deduplicator) BOOST_CHECK_EQUAL(pushTags.size(), 2); } +BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_same) +{ + AssemblyItems blocks{ + AssemblyItem(Tag, 1), + u256(42), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP, + AssemblyItem(Tag, 2), + u256(42), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP + }; + + AssemblyItems input = AssemblyItems{ + AssemblyItem(PushTag, 2), + AssemblyItem(PushTag, 1), + } + blocks; + AssemblyItems output = AssemblyItems{ + AssemblyItem(PushTag, 1), + AssemblyItem(PushTag, 1), + } + blocks; + BlockDeduplicator dedup(input); + dedup.deduplicate(); + BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); +} + +BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_different_value) +{ + AssemblyItems input{ + AssemblyItem(PushTag, 2), + AssemblyItem(PushTag, 1), + AssemblyItem(Tag, 1), + u256(42), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP, + AssemblyItem(Tag, 2), + u256(23), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP + }; + BlockDeduplicator dedup(input); + BOOST_CHECK(!dedup.deduplicate()); +} + +BOOST_AUTO_TEST_CASE(block_deduplicator_assign_immutable_different_hash) +{ + AssemblyItems input{ + AssemblyItem(PushTag, 2), + AssemblyItem(PushTag, 1), + AssemblyItem(Tag, 1), + u256(42), + AssemblyItem{AssignImmutable, 0x1234}, + Instruction::JUMP, + AssemblyItem(Tag, 2), + u256(42), + AssemblyItem{AssignImmutable, 0xABCD}, + Instruction::JUMP + }; + BlockDeduplicator dedup(input); + BOOST_CHECK(!dedup.deduplicate()); +} + BOOST_AUTO_TEST_CASE(block_deduplicator_loops) { AssemblyItems input{ diff --git a/test/libsolidity/semanticTests/immutable/external_function_pointer.sol b/test/libsolidity/semanticTests/immutable/external_function_pointer.sol new file mode 100644 index 000000000..f671065ac --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/external_function_pointer.sol @@ -0,0 +1,20 @@ +contract D { + function f() external view returns (uint256) { + return 42; + } +} +contract C { + D d; + function() external view returns(uint256) immutable z; + constructor() public { + d = new D(); + z = d.f; + } + function f() public view returns (uint256) { + assert(z.address == address(d)); + assert(z.selector == D.f.selector); + return z(); + } +} +// ---- +// f() -> 42 diff --git a/test/libsolidity/semanticTests/immutable/read_during_creation.sol b/test/libsolidity/semanticTests/immutable/read_during_creation.sol new file mode 100644 index 000000000..4a037cadc --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/read_during_creation.sol @@ -0,0 +1,13 @@ +contract C { + uint256 immutable x; + uint256 immutable y; + constructor() public { + x = 42; + y = x; + } + function f() public view returns (uint256, uint256) { + return (x+x,y); + } +} +// ---- +// f() -> 84, 42 diff --git a/test/libsolidity/semanticTests/immutable/stub.sol b/test/libsolidity/semanticTests/immutable/stub.sol new file mode 100644 index 000000000..ee9ed0678 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/stub.sol @@ -0,0 +1,13 @@ +contract C { + uint256 immutable x; + uint256 immutable y; + constructor() public { + x = 42; + y = 23; + } + function f() public view returns (uint256, uint256) { + return (x+x,y); + } +} +// ---- +// f() -> 84, 23 From e255c15227a295dba2657b3c4c2a8f51c48a66ab Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 12 Mar 2020 17:32:10 +0100 Subject: [PATCH 07/76] Tests. --- test/libevmasm/Assembler.cpp | 117 +++++++++++++++++- .../immutable/external_function_pointer.sol | 24 ++-- .../semanticTests/immutable/inheritance.sol | 30 +++++ .../immutable/internal_function_pointer.sol | 15 +++ .../immutable/multi_creation.sol | 31 +++++ .../immutable/read_during_creation.sol | 13 -- .../semanticTests/immutable/stub.sol | 18 +-- .../semanticTests/immutable/use_scratch.sol | 19 +++ 8 files changed, 229 insertions(+), 38 deletions(-) create mode 100644 test/libsolidity/semanticTests/immutable/inheritance.sol create mode 100644 test/libsolidity/semanticTests/immutable/internal_function_pointer.sol create mode 100644 test/libsolidity/semanticTests/immutable/multi_creation.sol delete mode 100644 test/libsolidity/semanticTests/immutable/read_during_creation.sol create mode 100644 test/libsolidity/semanticTests/immutable/use_scratch.sol diff --git a/test/libevmasm/Assembler.cpp b/test/libevmasm/Assembler.cpp index 2d7057f7c..a402101c4 100644 --- a/test/libevmasm/Assembler.cpp +++ b/test/libevmasm/Assembler.cpp @@ -61,6 +61,8 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) Assembly _subAsm; auto sub_asm = make_shared("lorem ipsum", "sub.asm"); _subAsm.setSourceLocation({6, 8, sub_asm}); + // PushImmutable + _subAsm.appendImmutable("someImmutable"); _subAsm.append(Instruction::INVALID); shared_ptr _subAsmPtr = make_shared(_subAsm); @@ -86,6 +88,11 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) _assembly.pushSubroutineOffset(size_t(sub.data())); // PushDeployTimeAddress _assembly.append(PushDeployTimeAddress); + // AssignImmutable. + // Note that since there is no reference to "someOtherImmutable", this will compile to a simple POP in the hex output. + _assembly.appendImmutableAssignment("someOtherImmutable"); + _assembly.append(u256(2)); + _assembly.appendImmutableAssignment("someImmutable"); // Operation _assembly.append(Instruction::STOP); _assembly.appendAuxiliaryDataToEnd(bytes{0x42, 0x66}); @@ -95,8 +102,11 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) BOOST_CHECK_EQUAL( _assembly.assemble().toHex(), - "5b6001600220604673__$bf005014d9d0f534b8fcb268bd84c491a2$__" - "600056603e6001603d73000000000000000000000000000000000000000000fe" + "5b6001600220606f73__$bf005014d9d0f534b8fcb268bd84c491a2$__" + "60005660676022604573000000000000000000000000000000000000000050" + "60028060015250" + "00fe" + "7f0000000000000000000000000000000000000000000000000000000000000000" "fe010203044266eeaa" ); BOOST_CHECK_EQUAL( @@ -111,12 +121,16 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) " dataSize(sub_0)\n" " dataOffset(sub_0)\n" " deployTimeAddress()\n" + " assignImmutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n" + " 0x02\n" + " assignImmutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" " stop\n" "stop\n" "data_a6885b3731702da62e8e4a8f584ac46a7f6822f4e2ba50fba902f67b1588d23b 01020304\n" "\n" "sub_0: assembly {\n" " /* \"sub.asm\":6:8 */\n" + " immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" " invalid\n" "}\n" "\n" @@ -138,9 +152,104 @@ BOOST_AUTO_TEST_CASE(all_assembly_items) "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," "{\"begin\":1,\"end\":3,\"name\":\"PUSHDEPLOYADDRESS\",\"source\":0}," + "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someOtherImmutable\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2\"}," + "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someImmutable\"}," "{\"begin\":1,\"end\":3,\"name\":\"STOP\",\"source\":0}" - "],\".data\":{\"0\":{\".code\":[{\"begin\":6,\"end\":8,\"name\":\"INVALID\",\"source\":1}]}," - "\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}" + "],\".data\":{\"0\":{\".code\":[" + "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}," + "{\"begin\":6,\"end\":8,\"name\":\"INVALID\",\"source\":1}" + "]},\"A6885B3731702DA62E8E4A8F584AC46A7F6822F4E2BA50FBA902F67B1588D23B\":\"01020304\"}}" + ); +} + +BOOST_AUTO_TEST_CASE(immutable) +{ + map indices = { + { "root.asm", 0 }, + { "sub.asm", 1 } + }; + Assembly _assembly; + auto root_asm = make_shared("lorem ipsum", "root.asm"); + _assembly.setSourceLocation({1, 3, root_asm}); + + Assembly _subAsm; + auto sub_asm = make_shared("lorem ipsum", "sub.asm"); + _subAsm.setSourceLocation({6, 8, sub_asm}); + _subAsm.appendImmutable("someImmutable"); + _subAsm.appendImmutable("someOtherImmutable"); + _subAsm.appendImmutable("someImmutable"); + shared_ptr _subAsmPtr = make_shared(_subAsm); + + _assembly.append(u256(42)); + _assembly.appendImmutableAssignment("someImmutable"); + _assembly.append(u256(23)); + _assembly.appendImmutableAssignment("someOtherImmutable"); + + auto sub = _assembly.appendSubroutine(_subAsmPtr); + _assembly.pushSubroutineOffset(size_t(sub.data())); + + checkCompilation(_assembly); + + BOOST_CHECK_EQUAL( + _assembly.assemble().toHex(), + // root.asm + // assign "someImmutable" + "602a" // PUSH1 42 - value for someImmutable + "80" // DUP1 + "6001" // PUSH1 1 - offset of first someImmutable in sub_0 + "52" // MSTORE + "80" // DUP1 + "6043" // PUSH1 67 - offset of second someImmutable in sub_0 + "52" // MSTORE + "50" // POP + // assign "someOtherImmutable" + "6017" // PUSH1 23 - value for someOtherImmutable + "80" // DUP1 + "6022" // PUSH1 34 - offset of someOtherImmutable in sub_0 + "52" // MSTORE + "50" // POP + "6063" // PUSH1 0x63 - dataSize(sub_0) + "6017" // PUSH1 0x17 - dataOffset(sub_0) + "fe" // INVALID + // end of root.asm + // sub.asm + "7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someImmutable - data at offset 1 + "7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someOtherImmutable - data at offset 34 + "7f0000000000000000000000000000000000000000000000000000000000000000" // PUSHIMMUTABLE someImmutable - data at offset 67 + ); + BOOST_CHECK_EQUAL( + _assembly.assemblyString(), + " /* \"root.asm\":1:3 */\n" + " 0x2a\n" + " assignImmutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" + " 0x17\n" + " assignImmutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n" + " dataSize(sub_0)\n" + " dataOffset(sub_0)\n" + "stop\n" + "\n" + "sub_0: assembly {\n" + " /* \"sub.asm\":6:8 */\n" + " immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" + " immutable(\"0xc3978657661c4d8e32e3d5f42597c009f0d3859e9f9d0d94325268f9799e2bfb\")\n" + " immutable(\"0x26f2c0195e9d408feff3abd77d83f2971f3c9a18d1e8a9437c7835ae4211fc9f\")\n" + "}\n" + ); + BOOST_CHECK_EQUAL( + util::jsonCompactPrint(_assembly.assemblyJSON(indices)), + "{\".code\":[" + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"2A\"}," + "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someImmutable\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH\",\"source\":0,\"value\":\"17\"}," + "{\"begin\":1,\"end\":3,\"name\":\"ASSIGNIMMUTABLE\",\"source\":0,\"value\":\"someOtherImmutable\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH #[$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}," + "{\"begin\":1,\"end\":3,\"name\":\"PUSH [$]\",\"source\":0,\"value\":\"0000000000000000000000000000000000000000000000000000000000000000\"}" + "],\".data\":{\"0\":{\".code\":[" + "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}," + "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someOtherImmutable\"}," + "{\"begin\":6,\"end\":8,\"name\":\"PUSHIMMUTABLE\",\"source\":1,\"value\":\"someImmutable\"}" + "]}}}" ); } diff --git a/test/libsolidity/semanticTests/immutable/external_function_pointer.sol b/test/libsolidity/semanticTests/immutable/external_function_pointer.sol index f671065ac..c815a18c3 100644 --- a/test/libsolidity/semanticTests/immutable/external_function_pointer.sol +++ b/test/libsolidity/semanticTests/immutable/external_function_pointer.sol @@ -1,20 +1,20 @@ contract D { - function f() external view returns (uint256) { - return 42; - } + function f() external view returns (uint256) { + return 42; + } } contract C { - D d; - function() external view returns(uint256) immutable z; - constructor() public { - d = new D(); - z = d.f; - } - function f() public view returns (uint256) { + D d; + function() external view returns(uint256) immutable z; + constructor() public { + d = new D(); + z = d.f; + } + function f() public view returns (uint256) { assert(z.address == address(d)); assert(z.selector == D.f.selector); - return z(); - } + return z(); + } } // ---- // f() -> 42 diff --git a/test/libsolidity/semanticTests/immutable/inheritance.sol b/test/libsolidity/semanticTests/immutable/inheritance.sol new file mode 100644 index 000000000..f61009f60 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/inheritance.sol @@ -0,0 +1,30 @@ +contract A { + uint8 immutable a; + constructor() public { + a = 4; + } +} +contract B is A { + uint8 immutable b; + constructor() public { + b = 3; + } +} +contract C is A { + uint8 immutable c; + constructor() public { + c = 2; + } +} +contract D is B, C { + uint8 immutable d; + + constructor() public { + d = 1; + } + function f() public view returns (uint256, uint256, uint, uint) { + return (a, b, c, d); + } +} +// ---- +// f() -> 4, 3, 2, 1 diff --git a/test/libsolidity/semanticTests/immutable/internal_function_pointer.sol b/test/libsolidity/semanticTests/immutable/internal_function_pointer.sol new file mode 100644 index 000000000..0673aafb5 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/internal_function_pointer.sol @@ -0,0 +1,15 @@ +contract C { + function() internal view returns(uint256) immutable z; + constructor() public { + z = f; + } + function f() public view returns (uint256) { + return 7; + } + function callZ() public view returns (uint) { + return z(); + } +} +// ---- +// f() -> 7 +// callZ() -> 7 diff --git a/test/libsolidity/semanticTests/immutable/multi_creation.sol b/test/libsolidity/semanticTests/immutable/multi_creation.sol new file mode 100644 index 000000000..b9e362dbd --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/multi_creation.sol @@ -0,0 +1,31 @@ +contract A { + uint immutable a; + constructor() public { + a = 7; + } + function f() public view returns (uint) { return a; } +} +contract B { + uint immutable a; + constructor() public { + a = 5; + } + function f() public view returns (uint) { return a; } +} +contract C { + uint immutable a; + uint public x; + uint public y; + constructor() public { + a = 3; + x = (new A()).f(); + y = (new B()).f(); + } + function f() public returns (uint256, uint, uint) { + return (a, (new A()).f(), (new B()).f()); + } +} +// ---- +// f() -> 3, 7, 5 +// x() -> 7 +// y() -> 5 diff --git a/test/libsolidity/semanticTests/immutable/read_during_creation.sol b/test/libsolidity/semanticTests/immutable/read_during_creation.sol deleted file mode 100644 index 4a037cadc..000000000 --- a/test/libsolidity/semanticTests/immutable/read_during_creation.sol +++ /dev/null @@ -1,13 +0,0 @@ -contract C { - uint256 immutable x; - uint256 immutable y; - constructor() public { - x = 42; - y = x; - } - function f() public view returns (uint256, uint256) { - return (x+x,y); - } -} -// ---- -// f() -> 84, 42 diff --git a/test/libsolidity/semanticTests/immutable/stub.sol b/test/libsolidity/semanticTests/immutable/stub.sol index ee9ed0678..387541066 100644 --- a/test/libsolidity/semanticTests/immutable/stub.sol +++ b/test/libsolidity/semanticTests/immutable/stub.sol @@ -1,13 +1,13 @@ contract C { - uint256 immutable x; - uint256 immutable y; - constructor() public { - x = 42; - y = 23; - } - function f() public view returns (uint256, uint256) { - return (x+x,y); - } + uint256 immutable x; + uint256 immutable y; + constructor() public { + x = 42; + y = 23; + } + function f() public view returns (uint256, uint256) { + return (x+x,y); + } } // ---- // f() -> 84, 23 diff --git a/test/libsolidity/semanticTests/immutable/use_scratch.sol b/test/libsolidity/semanticTests/immutable/use_scratch.sol new file mode 100644 index 000000000..d83da476d --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/use_scratch.sol @@ -0,0 +1,19 @@ +contract C { + uint256 immutable x; + uint256 immutable y; + mapping(uint => uint) public m; + constructor(uint _a) public { + x = 42; + y = 23; + m[_a] = 7; + new uint[](4); + + } + function f() public view returns (uint256, uint256) { + return (x+x,y); + } +} +// ---- +// constructor(): 3 -> +// f() -> 84, 23 +// m(uint256): 3 -> 7 From 8b443627e29d1e06ae7b3892be4645de648deb2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 26 Feb 2020 19:28:58 +0100 Subject: [PATCH 08/76] [yul-phaser] Add ProgramCache class --- test/CMakeLists.txt | 2 + test/yulPhaser/ProgramCache.cpp | 207 +++++++++++++++++++++++++++++++ tools/CMakeLists.txt | 2 + tools/yulPhaser/ProgramCache.cpp | 94 ++++++++++++++ tools/yulPhaser/ProgramCache.h | 91 ++++++++++++++ 5 files changed, 396 insertions(+) create mode 100644 test/yulPhaser/ProgramCache.cpp create mode 100644 tools/yulPhaser/ProgramCache.cpp create mode 100644 tools/yulPhaser/ProgramCache.h diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 181a0fbcf..d63d9aec5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -154,6 +154,7 @@ set(yul_phaser_sources yulPhaser/Phaser.cpp yulPhaser/Population.cpp yulPhaser/Program.cpp + yulPhaser/ProgramCache.cpp yulPhaser/Selections.cpp yulPhaser/SimulationRNG.cpp @@ -170,6 +171,7 @@ set(yul_phaser_sources ../tools/yulPhaser/Phaser.cpp ../tools/yulPhaser/Population.cpp ../tools/yulPhaser/Program.cpp + ../tools/yulPhaser/ProgramCache.cpp ../tools/yulPhaser/Selections.cpp ../tools/yulPhaser/SimulationRNG.cpp ) diff --git a/test/yulPhaser/ProgramCache.cpp b/test/yulPhaser/ProgramCache.cpp new file mode 100644 index 000000000..598e7a16f --- /dev/null +++ b/test/yulPhaser/ProgramCache.cpp @@ -0,0 +1,207 @@ +/* + 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 . +*/ + +#include +#include + +#include + +#include + +#include + +#include +#include + +using namespace std; +using namespace solidity::util; +using namespace solidity::langutil; +using namespace solidity::yul; + +namespace solidity::phaser::test +{ + +class ProgramCacheFixture +{ +protected: + static constexpr char SampleSourceCode[] = + "{\n" + " for { let i := 0 } not(eq(i, 15)) { i := add(i, 1) }\n" + " {\n" + " let x := 1\n" + " mstore(i, 2)\n" + " }\n" + "}\n"; + + Program optimisedProgram(Program _program, string _abbreviatedOptimisationSteps) const + { + Program result = move(_program); + result.optimise(Chromosome(_abbreviatedOptimisationSteps).optimisationSteps()); + return result; + } + + static set cachedKeys(ProgramCache const& _programCache) + { + set keys; + for (auto pair = _programCache.entries().begin(); pair != _programCache.entries().end(); ++pair) + keys.insert(pair->first); + + return keys; + } + + CharStream m_sourceStream = CharStream(SampleSourceCode, "program-cache-test"); + Program m_program = get(Program::load(m_sourceStream)); + ProgramCache m_programCache{m_program}; +}; + +BOOST_AUTO_TEST_SUITE(Phaser) +BOOST_AUTO_TEST_SUITE(ProgramCacheTest) + +BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_apply_optimisation_steps_to_program, ProgramCacheFixture) +{ + Program expectedProgram = optimisedProgram(m_program, "IuO"); + assert(toString(expectedProgram) != toString(m_program)); + + Program cachedProgram = m_programCache.optimiseProgram("IuO"); + + BOOST_TEST(toString(cachedProgram) == toString(expectedProgram)); +} + +BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_store_programs_for_all_prefixes, ProgramCacheFixture) +{ + Program programI = optimisedProgram(m_program, "I"); + Program programIu = optimisedProgram(programI, "u"); + Program programIuO = optimisedProgram(programIu, "O"); + assert(toString(m_program) != toString(programI)); + assert(toString(m_program) != toString(programIu)); + assert(toString(m_program) != toString(programIuO)); + assert(toString(programI) != toString(programIu)); + assert(toString(programI) != toString(programIuO)); + assert(toString(programIu) != toString(programIuO)); + + BOOST_REQUIRE(m_programCache.size() == 0); + + Program cachedProgram = m_programCache.optimiseProgram("IuO"); + + BOOST_TEST(toString(cachedProgram) == toString(programIuO)); + + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "IuO"})); + BOOST_TEST(toString(*m_programCache.find("I")) == toString(programI)); + BOOST_TEST(toString(*m_programCache.find("Iu")) == toString(programIu)); + BOOST_TEST(toString(*m_programCache.find("IuO")) == toString(programIuO)); +} + +BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_repeat_the_chromosome_requested_number_of_times, ProgramCacheFixture) +{ + string steps = "IuOIuO"; + + Program cachedProgram = m_programCache.optimiseProgram("IuO", 2); + + ProgramCache cacheNoRepetitions(m_program); + Program cachedProgramNoRepetitions = cacheNoRepetitions.optimiseProgram("IuOIuO"); + + BOOST_TEST(toString(cachedProgram) == toString(cachedProgramNoRepetitions)); + + for (size_t size = 1; size <= 6; ++size) + { + BOOST_REQUIRE(m_programCache.contains(steps.substr(0, size))); + BOOST_REQUIRE(cacheNoRepetitions.contains(steps.substr(0, size))); + BOOST_TEST( + toString(*cacheNoRepetitions.find(steps.substr(0, size))) == + toString(*m_programCache.find(steps.substr(0, size))) + ); + } +} + +BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_reuse_the_longest_prefix_and_move_it_to_the_next_round, ProgramCacheFixture) +{ + BOOST_TEST(m_programCache.currentRound() == 0); + + m_programCache.optimiseProgram("Iu"); + m_programCache.optimiseProgram("Ia"); + m_programCache.startRound(1); + + BOOST_TEST(m_programCache.currentRound() == 1); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "Ia"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Ia")->second.roundNumber == 0); + + m_programCache.optimiseProgram("IuOI"); + + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "Ia", "IuO", "IuOI"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("Ia")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("IuO")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("IuOI")->second.roundNumber == 1); +} + +BOOST_FIXTURE_TEST_CASE(startRound_should_remove_entries_older_than_two_rounds, ProgramCacheFixture) +{ + BOOST_TEST(m_programCache.currentRound() == 0); + BOOST_TEST(m_programCache.size() == 0); + + m_programCache.optimiseProgram("Iu"); + + BOOST_TEST(m_programCache.currentRound() == 0); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + + m_programCache.optimiseProgram("a"); + + BOOST_TEST(m_programCache.currentRound() == 0); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "a"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 0); + + m_programCache.startRound(1); + + BOOST_TEST(m_programCache.currentRound() == 1); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "a"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 0); + + m_programCache.optimiseProgram("af"); + + BOOST_TEST(m_programCache.currentRound() == 1); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "a", "af"})); + BOOST_TEST(m_programCache.entries().find("I")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("Iu")->second.roundNumber == 0); + BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("af")->second.roundNumber == 1); + + m_programCache.startRound(2); + + BOOST_TEST(m_programCache.currentRound() == 2); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"a", "af"})); + BOOST_TEST(m_programCache.entries().find("a")->second.roundNumber == 1); + BOOST_TEST(m_programCache.entries().find("af")->second.roundNumber == 1); + + m_programCache.startRound(3); + + BOOST_TEST(m_programCache.currentRound() == 3); + BOOST_TEST(m_programCache.size() == 0); +} + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() + +} diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index fd5f92bbe..241a80f46 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -35,6 +35,8 @@ add_executable(yul-phaser yulPhaser/PairSelections.cpp yulPhaser/Selections.h yulPhaser/Selections.cpp + yulPhaser/ProgramCache.h + yulPhaser/ProgramCache.cpp yulPhaser/Program.h yulPhaser/Program.cpp yulPhaser/SimulationRNG.h diff --git a/tools/yulPhaser/ProgramCache.cpp b/tools/yulPhaser/ProgramCache.cpp new file mode 100644 index 000000000..bd3b05114 --- /dev/null +++ b/tools/yulPhaser/ProgramCache.cpp @@ -0,0 +1,94 @@ +/* + 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 . +*/ + +#include + +#include + +using namespace std; +using namespace solidity::yul; +using namespace solidity::phaser; + +Program ProgramCache::optimiseProgram( + string const& _abbreviatedOptimisationSteps, + size_t _repetitionCount +) +{ + string targetOptimisations = _abbreviatedOptimisationSteps; + for (size_t i = 1; i < _repetitionCount; ++i) + targetOptimisations += _abbreviatedOptimisationSteps; + + size_t prefixSize = 0; + for (size_t i = 1; i <= targetOptimisations.size(); ++i) + { + auto const& pair = m_entries.find(targetOptimisations.substr(0, i)); + if (pair != m_entries.end()) + { + pair->second.roundNumber = m_currentRound; + ++prefixSize; + } + else + break; + } + + Program intermediateProgram = ( + prefixSize == 0 ? + m_program : + m_entries.at(targetOptimisations.substr(0, prefixSize)).program + ); + + for (size_t i = prefixSize + 1; i <= targetOptimisations.size(); ++i) + { + string stepName = OptimiserSuite::stepAbbreviationToNameMap().at(targetOptimisations[i - 1]); + intermediateProgram.optimise({stepName}); + + m_entries.insert({targetOptimisations.substr(0, i), {intermediateProgram, m_currentRound}}); + } + + return intermediateProgram; +} + +void ProgramCache::startRound(size_t _roundNumber) +{ + assert(_roundNumber > m_currentRound); + m_currentRound = _roundNumber; + + for (auto pair = m_entries.begin(); pair != m_entries.end();) + { + assert(pair->second.roundNumber < m_currentRound); + + if (pair->second.roundNumber < m_currentRound - 1) + m_entries.erase(pair++); + else + ++pair; + } +} + +void ProgramCache::clear() +{ + m_entries.clear(); + m_currentRound = 0; +} + +Program const* ProgramCache::find(string const& _abbreviatedOptimisationSteps) const +{ + auto const& pair = m_entries.find(_abbreviatedOptimisationSteps); + if (pair == m_entries.end()) + return nullptr; + + return &(pair->second.program); +} diff --git a/tools/yulPhaser/ProgramCache.h b/tools/yulPhaser/ProgramCache.h new file mode 100644 index 000000000..433ba99c5 --- /dev/null +++ b/tools/yulPhaser/ProgramCache.h @@ -0,0 +1,91 @@ +/* + 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 . +*/ + +#pragma once + +#include + +#include +#include + +namespace solidity::phaser +{ + +/** + * Structure used by @a ProgramCache to store intermediate programs and metadata associated + * with them. + */ +struct CacheEntry +{ + Program program; + size_t roundNumber; + + CacheEntry(Program _program, size_t _roundNumber): + program(std::move(_program)), + roundNumber(_roundNumber) {} +}; + +/** + * Class that optimises programs one step at a time which allows it to store and later reuse the + * results of the intermediate steps. + * + * The cache keeps track of the current round number and associates newly created entries with it. + * @a startRound() must be called at the beginning of a round so that entries that are too old + * can be purged. The current strategy is to store programs corresponding to all possible prefixes + * encountered in the current and the previous rounds. Entries older than that get removed to + * conserve memory. + * + * The current strategy does speed things up (about 4:1 hit:miss ratio observed in my limited + * experiments) but there's room for improvement. We could fit more useful programs in + * the cache by being more picky about which ones we choose. + * + * There is currently no way to purge entries without starting a new round. Since the programs + * take a lot of memory, this may lead to the cache eating up all the available RAM if sequences are + * long and programs large. A limiter based on entry count or total program size would be useful. + */ +class ProgramCache +{ +public: + explicit ProgramCache(Program _program): + m_program(std::move(_program)) {} + + Program optimiseProgram( + std::string const& _abbreviatedOptimisationSteps, + size_t _repetitionCount = 1 + ); + void startRound(size_t _nextRoundNumber); + void clear(); + + size_t size() const { return m_entries.size(); } + Program const* find(std::string const& _abbreviatedOptimisationSteps) const; + bool contains(std::string const& _abbreviatedOptimisationSteps) const { return find(_abbreviatedOptimisationSteps) != nullptr; } + + std::map const& entries() const { return m_entries; }; + Program const& program() const { return m_program; } + size_t currentRound() const { return m_currentRound; } + +private: + // The best matching data structure here would be a trie of chromosome prefixes but since + // the programs are orders of magnitude larger than the prefixes, it does not really matter. + // A map should be good enough. + std::map m_entries; + + Program m_program; + size_t m_currentRound = 0; +}; + +} From 259f738f172b9637c2a6f4558604328fe8fa58eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 26 Feb 2020 20:55:13 +0100 Subject: [PATCH 09/76] [yul-phaser] ProgramBasedMetric: Add the ability to use ProgramCache --- test/yulPhaser/FitnessMetrics.cpp | 78 +++++++++++++++++++++++------- tools/yulPhaser/FitnessMetrics.cpp | 26 +++++++++- tools/yulPhaser/FitnessMetrics.h | 27 ++++++++--- tools/yulPhaser/Phaser.cpp | 2 + 4 files changed, 105 insertions(+), 28 deletions(-) diff --git a/test/yulPhaser/FitnessMetrics.cpp b/test/yulPhaser/FitnessMetrics.cpp index 8b62b03b7..524ef6357 100644 --- a/test/yulPhaser/FitnessMetrics.cpp +++ b/test/yulPhaser/FitnessMetrics.cpp @@ -76,15 +76,16 @@ protected: Chromosome m_chromosome{vector{UnusedPruner::name, EquivalentFunctionCombiner::name}}; Program m_program = get(Program::load(m_sourceStream)); Program m_optimisedProgram = optimisedProgram(m_program); + shared_ptr m_programCache = make_shared(m_program); }; class FitnessMetricCombinationFixture: public ProgramBasedMetricFixture { protected: vector> m_simpleMetrics = { - make_shared(m_program, 1), - make_shared(m_program, 2), - make_shared(m_program, 3), + make_shared(m_program, nullptr, 1), + make_shared(m_program, nullptr, 2), + make_shared(m_program, nullptr, 3), }; vector m_fitness = { m_simpleMetrics[0]->evaluate(m_chromosome), @@ -97,31 +98,66 @@ BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(FitnessMetricsTest) BOOST_AUTO_TEST_SUITE(ProgramBasedMetricTest) -BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_return_optimised_program, ProgramBasedMetricFixture) +BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture) { - string code = toString(DummyProgramBasedMetric(m_program).optimisedProgram(m_chromosome)); + string code = toString(DummyProgramBasedMetric(m_program, nullptr).optimisedProgram(m_chromosome)); BOOST_TEST(code != toString(m_program)); BOOST_TEST(code == toString(m_optimisedProgram)); } +BOOST_FIXTURE_TEST_CASE(optimisedProgram_should_use_cache_if_available, ProgramBasedMetricFixture) +{ + string code = toString(DummyProgramBasedMetric(nullopt, m_programCache).optimisedProgram(m_chromosome)); + + BOOST_TEST(code != toString(m_program)); + BOOST_TEST(code == toString(m_optimisedProgram)); + BOOST_TEST(m_programCache->size() == m_chromosome.length()); +} + +BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_return_optimised_program_even_if_cache_not_available, ProgramBasedMetricFixture) +{ + string code = toString(DummyProgramBasedMetric(m_program, nullptr).optimisedProgramNoCache(m_chromosome)); + + BOOST_TEST(code != toString(m_program)); + BOOST_TEST(code == toString(m_optimisedProgram)); +} + +BOOST_FIXTURE_TEST_CASE(optimisedProgramNoCache_should_not_use_cache_even_if_available, ProgramBasedMetricFixture) +{ + string code = toString(DummyProgramBasedMetric(nullopt, m_programCache).optimisedProgramNoCache(m_chromosome)); + + BOOST_TEST(code != toString(m_program)); + BOOST_TEST(code == toString(m_optimisedProgram)); + BOOST_TEST(m_programCache->size() == 0); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(ProgramSizeTest) BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_size_of_the_optimised_program, ProgramBasedMetricFixture) { - size_t fitness = ProgramSize(m_program).evaluate(m_chromosome); + size_t fitness = ProgramSize(m_program, nullptr).evaluate(m_chromosome); BOOST_TEST(fitness != m_program.codeSize()); BOOST_TEST(fitness == m_optimisedProgram.codeSize()); } +BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture) +{ + size_t fitness = ProgramSize(nullopt, m_programCache).evaluate(m_chromosome); + + BOOST_TEST(fitness != m_program.codeSize()); + BOOST_TEST(fitness == m_optimisedProgram.codeSize()); + BOOST_TEST(m_programCache->size() == m_chromosome.length()); +} + BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture) { Program const& programOptimisedOnce = m_optimisedProgram; Program programOptimisedTwice = optimisedProgram(programOptimisedOnce); - ProgramSize metric(m_program, 2); + ProgramSize metric(m_program, nullptr, 2); size_t fitness = metric.evaluate(m_chromosome); BOOST_TEST(fitness != m_program.codeSize()); @@ -131,7 +167,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number BOOST_FIXTURE_TEST_CASE(evaluate_should_not_optimise_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture) { - ProgramSize metric(m_program, 0); + ProgramSize metric(m_program, nullptr, 0); size_t fitness = metric.evaluate(m_chromosome); BOOST_TEST(fitness == m_program.codeSize()); @@ -143,7 +179,13 @@ BOOST_AUTO_TEST_SUITE(RelativeProgramSizeTest) BOOST_FIXTURE_TEST_CASE(evaluate_should_compute_the_size_ratio_between_optimised_program_and_original_program, ProgramBasedMetricFixture) { - BOOST_TEST(RelativeProgramSize(m_program, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize())); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize())); +} + +BOOST_FIXTURE_TEST_CASE(evaluate_should_be_able_to_use_program_cache_if_available, ProgramBasedMetricFixture) +{ + BOOST_TEST(RelativeProgramSize(nullopt, m_programCache, 3).evaluate(m_chromosome) == round(1000.0 * m_optimisedProgram.codeSize() / m_program.codeSize())); + BOOST_TEST(m_programCache->size() == m_chromosome.length()); } BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number_of_times, ProgramBasedMetricFixture) @@ -151,17 +193,17 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_repeat_the_optimisation_specified_number Program const& programOptimisedOnce = m_optimisedProgram; Program programOptimisedTwice = optimisedProgram(programOptimisedOnce); - RelativeProgramSize metric(m_program, 3, 2); + RelativeProgramSize metric(m_program, nullptr, 3, 2); size_t fitness = metric.evaluate(m_chromosome); BOOST_TEST(fitness != 1000); - BOOST_TEST(fitness != RelativeProgramSize(programOptimisedTwice, 3, 1).evaluate(m_chromosome)); + BOOST_TEST(fitness != RelativeProgramSize(programOptimisedTwice, nullptr, 3, 1).evaluate(m_chromosome)); BOOST_TEST(fitness == round(1000.0 * programOptimisedTwice.codeSize() / m_program.codeSize())); } BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_number_of_repetitions_is_zero, ProgramBasedMetricFixture) { - RelativeProgramSize metric(m_program, 3, 0); + RelativeProgramSize metric(m_program, nullptr, 3, 0); BOOST_TEST(metric.evaluate(m_chromosome) == 1000); } @@ -171,7 +213,7 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_the_original_program_size_ CharStream sourceStream = CharStream("{}", ""); Program program = get(Program::load(sourceStream)); - RelativeProgramSize metric(program, 3); + RelativeProgramSize metric(program, nullptr, 3); BOOST_TEST(metric.evaluate(m_chromosome) == 1000); BOOST_TEST(metric.evaluate(Chromosome("")) == 1000); @@ -181,11 +223,11 @@ BOOST_FIXTURE_TEST_CASE(evaluate_should_return_one_if_the_original_program_size_ BOOST_FIXTURE_TEST_CASE(evaluate_should_multiply_the_result_by_scaling_factor, ProgramBasedMetricFixture) { double sizeRatio = static_cast(m_optimisedProgram.codeSize()) / m_program.codeSize(); - BOOST_TEST(RelativeProgramSize(m_program, 0).evaluate(m_chromosome) == round(1.0 * sizeRatio)); - BOOST_TEST(RelativeProgramSize(m_program, 1).evaluate(m_chromosome) == round(10.0 * sizeRatio)); - BOOST_TEST(RelativeProgramSize(m_program, 2).evaluate(m_chromosome) == round(100.0 * sizeRatio)); - BOOST_TEST(RelativeProgramSize(m_program, 3).evaluate(m_chromosome) == round(1000.0 * sizeRatio)); - BOOST_TEST(RelativeProgramSize(m_program, 4).evaluate(m_chromosome) == round(10000.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 0).evaluate(m_chromosome) == round(1.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 1).evaluate(m_chromosome) == round(10.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 2).evaluate(m_chromosome) == round(100.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 3).evaluate(m_chromosome) == round(1000.0 * sizeRatio)); + BOOST_TEST(RelativeProgramSize(m_program, nullptr, 4).evaluate(m_chromosome) == round(10000.0 * sizeRatio)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/tools/yulPhaser/FitnessMetrics.cpp b/tools/yulPhaser/FitnessMetrics.cpp index 49482d0af..5279cea72 100644 --- a/tools/yulPhaser/FitnessMetrics.cpp +++ b/tools/yulPhaser/FitnessMetrics.cpp @@ -17,14 +17,36 @@ #include +#include + #include using namespace std; +using namespace solidity::util; using namespace solidity::phaser; -Program ProgramBasedMetric::optimisedProgram(Chromosome const& _chromosome) const +Program const& ProgramBasedMetric::program() const { - Program programCopy = m_program; + if (m_programCache == nullptr) + return m_program.value(); + else + return m_programCache->program(); +} + +Program ProgramBasedMetric::optimisedProgram(Chromosome const& _chromosome) +{ + if (m_programCache == nullptr) + return optimisedProgramNoCache(_chromosome); + + return m_programCache->optimiseProgram( + toString(_chromosome), + m_repetitionCount + ); +} + +Program ProgramBasedMetric::optimisedProgramNoCache(Chromosome const& _chromosome) const +{ + Program programCopy = program(); for (size_t i = 0; i < m_repetitionCount; ++i) programCopy.optimise(_chromosome.optimisationSteps()); diff --git a/tools/yulPhaser/FitnessMetrics.h b/tools/yulPhaser/FitnessMetrics.h index e79d99ff4..72e811152 100644 --- a/tools/yulPhaser/FitnessMetrics.h +++ b/tools/yulPhaser/FitnessMetrics.h @@ -22,8 +22,10 @@ #include #include +#include #include +#include namespace solidity::phaser { @@ -50,7 +52,7 @@ public: * Abstract base class for fitness metrics that return values based on program size. * * The class provides utilities for optimising programs according to the information stored in - * chromosomes. + * chromosomes. Allows using @a ProgramCache. * * It can also store weights for the @a CodeSize metric. It does not do anything with * them because it does not actually compute the code size but they are readily available for use @@ -60,19 +62,27 @@ class ProgramBasedMetric: public FitnessMetric { public: explicit ProgramBasedMetric( - Program _program, + std::optional _program, + std::shared_ptr _programCache, size_t _repetitionCount = 1 ): m_program(std::move(_program)), - m_repetitionCount(_repetitionCount) {} + m_programCache(std::move(_programCache)), + m_repetitionCount(_repetitionCount) + { + assert(m_program.has_value() == (m_programCache == nullptr)); + } - Program const& program() const { return m_program; } + Program const& program() const; + ProgramCache const* programCache() const { return m_programCache.get(); } size_t repetitionCount() const { return m_repetitionCount; } - Program optimisedProgram(Chromosome const& _chromosome) const; + Program optimisedProgram(Chromosome const& _chromosome); + Program optimisedProgramNoCache(Chromosome const& _chromosome) const; private: - Program m_program; + std::optional m_program; + std::shared_ptr m_programCache; size_t m_repetitionCount; }; @@ -98,11 +108,12 @@ class RelativeProgramSize: public ProgramBasedMetric { public: explicit RelativeProgramSize( - Program _program, + std::optional _program, + std::shared_ptr _programCache, size_t _fixedPointPrecision, size_t _repetitionCount = 1 ): - ProgramBasedMetric(std::move(_program), _repetitionCount), + ProgramBasedMetric(std::move(_program), std::move(_programCache), _repetitionCount), m_fixedPointPrecision(_fixedPointPrecision) {} size_t fixedPointPrecision() const { return m_fixedPointPrecision; } diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index b29938f22..cfe5cbd1f 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -171,6 +171,7 @@ unique_ptr FitnessMetricFactory::build( for (Program& program: _programs) metrics.push_back(make_unique( move(program), + nullptr, _options.chromosomeRepetitions )); @@ -181,6 +182,7 @@ unique_ptr FitnessMetricFactory::build( for (Program& program: _programs) metrics.push_back(make_unique( move(program), + nullptr, _options.relativeMetricScale, _options.chromosomeRepetitions )); From e2ff9698d39e766588d9d8d654d149f0e27bb112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 27 Feb 2020 00:55:24 +0100 Subject: [PATCH 10/76] [yul-phaser] AlgorithmRunner: Add support for ProgramCache --- test/yulPhaser/AlgorithmRunner.cpp | 45 ++++++++++++++++++++++++----- tools/yulPhaser/AlgorithmRunner.cpp | 17 +++++++++++ tools/yulPhaser/AlgorithmRunner.h | 7 +++++ tools/yulPhaser/Phaser.cpp | 2 +- 4 files changed, 63 insertions(+), 8 deletions(-) diff --git a/test/yulPhaser/AlgorithmRunner.cpp b/test/yulPhaser/AlgorithmRunner.cpp index 0b54ad38f..0382cc90d 100644 --- a/test/yulPhaser/AlgorithmRunner.cpp +++ b/test/yulPhaser/AlgorithmRunner.cpp @@ -20,6 +20,8 @@ #include #include +#include + #include #include @@ -29,6 +31,7 @@ using namespace std; using namespace boost::unit_test::framework; using namespace boost::test_tools; +using namespace solidity::langutil; using namespace solidity::util; namespace fs = boost::filesystem; @@ -92,7 +95,7 @@ BOOST_AUTO_TEST_SUITE(AlgorithmRunnerTest) BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, AlgorithmRunnerFixture) { m_options.maxRounds = 5; - AlgorithmRunner runner(Population(m_fitnessMetric), m_options, m_output); + AlgorithmRunner runner(Population(m_fitnessMetric), {}, m_options, m_output); CountingAlgorithm algorithm; @@ -112,6 +115,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_the_top_chromosome, AlgorithmRunnerFixt // NOTE: Chromosomes chosen so that they're not substrings of each other and are not // words likely to appear in the output in normal circumstances. Population(m_fitnessMetric, {Chromosome("fcCUnDve"), Chromosome("jsxIOo"), Chromosome("ighTLM")}), + {}, m_options, m_output ); @@ -131,7 +135,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_save_initial_population_to_file_if_autosave_f { m_options.maxRounds = 0; m_options.populationAutosaveFile = m_autosavePath; - AlgorithmRunner runner(m_population, m_options, m_output); + AlgorithmRunner runner(m_population, {}, m_options, m_output); assert(!fs::exists(m_autosavePath)); runner.run(m_algorithm); @@ -145,7 +149,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_save_population_to_file_if_autosave_file_spec { m_options.maxRounds = 1; m_options.populationAutosaveFile = m_autosavePath; - AlgorithmRunner runner(m_population, m_options, m_output); + AlgorithmRunner runner(m_population, {}, m_options, m_output); assert(!fs::exists(m_autosavePath)); runner.run(m_algorithm); @@ -159,7 +163,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_overwrite_existing_file_if_autosave_file_spec { m_options.maxRounds = 5; m_options.populationAutosaveFile = m_autosavePath; - AlgorithmRunner runner(m_population, m_options, m_output); + AlgorithmRunner runner(m_population, {}, m_options, m_output); assert(!fs::exists(m_autosavePath)); vector originalContent = {"Original content"}; @@ -180,7 +184,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_save_population_to_file_if_autosave_file_ { m_options.maxRounds = 5; m_options.populationAutosaveFile = nullopt; - AlgorithmRunner runner(m_population, m_options, m_output); + AlgorithmRunner runner(m_population, {}, m_options, m_output); assert(!fs::exists(m_autosavePath)); runner.run(m_algorithm); @@ -198,7 +202,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_randomise_duplicate_chromosomes_if_requested, m_options.randomiseDuplicates = true; m_options.minChromosomeLength = 50; m_options.maxChromosomeLength = 50; - AlgorithmRunner runner(population, m_options, m_output); + AlgorithmRunner runner(population, {}, m_options, m_output); runner.run(algorithm); @@ -227,7 +231,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_randomise_duplicate_chromosomes_if_not_re m_options.maxRounds = 1; m_options.randomiseDuplicates = false; - AlgorithmRunner runner(population, m_options, m_output); + AlgorithmRunner runner(population, {}, m_options, m_output); runner.run(algorithm); @@ -237,6 +241,33 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_randomise_duplicate_chromosomes_if_not_re BOOST_TEST(runner.population().individuals()[2].chromosome == duplicate); } +BOOST_FIXTURE_TEST_CASE(run_should_clear_cache_at_the_beginning_and_update_it_before_each_round, AlgorithmRunnerFixture) +{ + CharStream sourceStream = CharStream("{}", current_test_case().p_name); + vector> caches = { + make_shared(get(Program::load(sourceStream))), + make_shared(get(Program::load(sourceStream))), + }; + + m_options.maxRounds = 10; + AlgorithmRunner runner(Population(m_fitnessMetric), caches, m_options, m_output); + CountingAlgorithm algorithm; + + BOOST_TEST(algorithm.m_currentRound == 0); + BOOST_TEST(caches[0]->currentRound() == 0); + BOOST_TEST(caches[1]->currentRound() == 0); + + runner.run(algorithm); + BOOST_TEST(algorithm.m_currentRound == 10); + BOOST_TEST(caches[0]->currentRound() == 10); + BOOST_TEST(caches[1]->currentRound() == 10); + + runner.run(algorithm); + BOOST_TEST(algorithm.m_currentRound == 20); + BOOST_TEST(caches[0]->currentRound() == 10); + BOOST_TEST(caches[1]->currentRound() == 10); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/tools/yulPhaser/AlgorithmRunner.cpp b/tools/yulPhaser/AlgorithmRunner.cpp index b9f22171b..e7ea5a072 100644 --- a/tools/yulPhaser/AlgorithmRunner.cpp +++ b/tools/yulPhaser/AlgorithmRunner.cpp @@ -31,9 +31,12 @@ using namespace solidity::phaser; void AlgorithmRunner::run(GeneticAlgorithm& _algorithm) { populationAutosave(); + cacheClear(); for (size_t round = 0; !m_options.maxRounds.has_value() || round < m_options.maxRounds.value(); ++round) { + cacheStartRound(round + 1); + m_population = _algorithm.runNextRound(m_population); randomiseDuplicates(); @@ -66,6 +69,20 @@ void AlgorithmRunner::populationAutosave() const ); } +void AlgorithmRunner::cacheClear() +{ + for (auto& cache: m_programCaches) + if (cache != nullptr) + cache->clear(); +} + +void AlgorithmRunner::cacheStartRound(size_t _roundNumber) +{ + for (auto& cache: m_programCaches) + if (cache != nullptr) + cache->startRound(_roundNumber); +} + void AlgorithmRunner::randomiseDuplicates() { if (m_options.randomiseDuplicates) diff --git a/tools/yulPhaser/AlgorithmRunner.h b/tools/yulPhaser/AlgorithmRunner.h index ff0d0e3c3..8ee97ac48 100644 --- a/tools/yulPhaser/AlgorithmRunner.h +++ b/tools/yulPhaser/AlgorithmRunner.h @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -50,10 +51,12 @@ public: AlgorithmRunner( Population _initialPopulation, + std::vector> _programCaches, Options _options, std::ostream& _outputStream ): m_population(std::move(_initialPopulation)), + m_programCaches(std::move(_programCaches)), m_options(std::move(_options)), m_outputStream(_outputStream) {} @@ -65,6 +68,9 @@ public: private: void populationAutosave() const; void randomiseDuplicates(); + void cacheClear(); + void cacheStartRound(size_t _roundNumber); + static Population randomiseDuplicates( Population _population, size_t _minChromosomeLength, @@ -72,6 +78,7 @@ private: ); Population m_population; + std::vector> m_programCaches; Options m_options; std::ostream& m_outputStream; }; diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index cfe5cbd1f..80aed0226 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -577,6 +577,6 @@ void Phaser::runAlgorithm(po::variables_map const& _arguments) population.individuals().size() ); - AlgorithmRunner algorithmRunner(population, buildAlgorithmRunnerOptions(_arguments), cout); + AlgorithmRunner algorithmRunner(population, vector>(programs.size(), nullptr), buildAlgorithmRunnerOptions(_arguments), cout); algorithmRunner.run(*geneticAlgorithm); } From 3b49fbb8a56b9fec6d78af9f44131c103d62eba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Fri, 28 Feb 2020 22:29:08 +0100 Subject: [PATCH 11/76] [yul-phaser] Add ProgramCacheFactory class --- test/yulPhaser/Phaser.cpp | 36 +++++++++++++++++++++++++++++++++++- tools/yulPhaser/Phaser.cpp | 19 +++++++++++++++++++ tools/yulPhaser/Phaser.h | 20 ++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/test/yulPhaser/Phaser.cpp b/test/yulPhaser/Phaser.cpp index 04dfbe3dc..ca45c0771 100644 --- a/test/yulPhaser/Phaser.cpp +++ b/test/yulPhaser/Phaser.cpp @@ -55,7 +55,7 @@ protected: }; }; -class FitnessMetricFactoryFixture +class FixtureWithPrograms { protected: vector m_sourceStreams = { @@ -68,6 +68,11 @@ protected: get(Program::load(m_sourceStreams[1])), get(Program::load(m_sourceStreams[2])), }; +}; + +class FitnessMetricFactoryFixture: public FixtureWithPrograms +{ +protected: FitnessMetricFactory::Options m_options = { /* metric = */ MetricChoice::CodeSize, /* metricAggregator = */ MetricAggregatorChoice::Average, @@ -317,6 +322,35 @@ BOOST_FIXTURE_TEST_CASE(build_should_combine_populations_from_all_sources, Poula BOOST_TEST(count(begin, end, Individual(Chromosome("fcL"), *m_fitnessMetric)) >= 2); } + +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE(ProgramCacheFactoryTest) + +BOOST_FIXTURE_TEST_CASE(build_should_create_cache_for_each_input_program_if_cache_enabled, FixtureWithPrograms) +{ + ProgramCacheFactory::Options options{/* programCacheEnabled = */ true}; + vector> caches = ProgramCacheFactory::build(options, m_programs); + assert(m_programs.size() >= 2 && "There must be at least 2 programs for this test to be meaningful"); + + BOOST_TEST(caches.size() == m_programs.size()); + for (size_t i = 0; i < m_programs.size(); ++i) + { + BOOST_REQUIRE(caches[i] != nullptr); + BOOST_TEST(toString(caches[i]->program()) == toString(m_programs[i])); + } +} + +BOOST_FIXTURE_TEST_CASE(build_should_return_nullptr_for_each_input_program_if_cache_disabled, FixtureWithPrograms) +{ + ProgramCacheFactory::Options options{/* programCacheEnabled = */ false}; + vector> caches = ProgramCacheFactory::build(options, m_programs); + assert(m_programs.size() >= 2 && "There must be at least 2 programs for this test to be meaningful"); + + BOOST_TEST(caches.size() == m_programs.size()); + for (size_t i = 0; i < m_programs.size(); ++i) + BOOST_TEST(caches[i] == nullptr); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(ProgramFactoryTest) diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index 80aed0226..054b6e99e 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -283,6 +283,25 @@ Population PopulationFactory::buildFromFile( return buildFromStrings(readLinesFromFile(_filePath), move(_fitnessMetric)); } +ProgramCacheFactory::Options ProgramCacheFactory::Options::fromCommandLine(po::variables_map const& _arguments) +{ + return { + _arguments["program-cache"].as(), + }; +} + +vector> ProgramCacheFactory::build( + Options const& _options, + vector _programs +) +{ + vector> programCaches; + for (Program& program: _programs) + programCaches.push_back(_options.programCacheEnabled ? make_shared(move(program)) : nullptr); + + return programCaches; +} + ProgramFactory::Options ProgramFactory::Options::fromCommandLine(po::variables_map const& _arguments) { return { diff --git a/tools/yulPhaser/Phaser.h b/tools/yulPhaser/Phaser.h index 9814c9111..256d9a288 100644 --- a/tools/yulPhaser/Phaser.h +++ b/tools/yulPhaser/Phaser.h @@ -45,6 +45,7 @@ class FitnessMetric; class GeneticAlgorithm; class Population; class Program; +class ProgramCache; enum class Algorithm { @@ -160,6 +161,25 @@ public: ); }; +/** + * Builds and validates instances of @a ProgramCache. + */ +class ProgramCacheFactory +{ +public: + struct Options + { + bool programCacheEnabled; + + static Options fromCommandLine(boost::program_options::variables_map const& _arguments); + }; + + static std::vector> build( + Options const& _options, + std::vector _programs + ); +}; + /** * Builds and validates instances of @a Program. */ From 98db50ccac95ba94be3352df8f7b2b57536031ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Wed, 26 Feb 2020 22:00:18 +0100 Subject: [PATCH 12/76] [yul-phaser] Add --program-cache option --- test/yulPhaser/Phaser.cpp | 37 +++++++++++++++++++++++++++++++++---- tools/yulPhaser/Phaser.cpp | 36 +++++++++++++++++++++++++++--------- tools/yulPhaser/Phaser.h | 3 ++- 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/test/yulPhaser/Phaser.cpp b/test/yulPhaser/Phaser.cpp index ca45c0771..633462865 100644 --- a/test/yulPhaser/Phaser.cpp +++ b/test/yulPhaser/Phaser.cpp @@ -159,7 +159,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_create_metric_of_the_right_type, FitnessMet { m_options.metric = MetricChoice::RelativeCodeSize; m_options.metricAggregator = MetricAggregatorChoice::Sum; - unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}); + unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}); BOOST_REQUIRE(metric != nullptr); auto sumMetric = dynamic_cast(metric.get()); @@ -177,7 +177,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_respect_chromosome_repetitions_option, Fitn m_options.metric = MetricChoice::CodeSize; m_options.metricAggregator = MetricAggregatorChoice::Average; m_options.chromosomeRepetitions = 5; - unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}); + unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}); BOOST_REQUIRE(metric != nullptr); auto averageMetric = dynamic_cast(metric.get()); @@ -195,7 +195,7 @@ BOOST_FIXTURE_TEST_CASE(build_should_set_relative_metric_scale, FitnessMetricFac m_options.metric = MetricChoice::RelativeCodeSize; m_options.metricAggregator = MetricAggregatorChoice::Average; m_options.relativeMetricScale = 10; - unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}); + unique_ptr metric = FitnessMetricFactory::build(m_options, {m_programs[0]}, {nullptr}); BOOST_REQUIRE(metric != nullptr); auto averageMetric = dynamic_cast(metric.get()); @@ -210,7 +210,11 @@ BOOST_FIXTURE_TEST_CASE(build_should_set_relative_metric_scale, FitnessMetricFac BOOST_FIXTURE_TEST_CASE(build_should_create_metric_for_each_input_program, FitnessMetricFactoryFixture) { - unique_ptr metric = FitnessMetricFactory::build(m_options, m_programs); + unique_ptr metric = FitnessMetricFactory::build( + m_options, + m_programs, + vector>(m_programs.size(), nullptr) + ); BOOST_REQUIRE(metric != nullptr); auto combinedMetric = dynamic_cast(metric.get()); @@ -218,6 +222,31 @@ BOOST_FIXTURE_TEST_CASE(build_should_create_metric_for_each_input_program, Fitne BOOST_REQUIRE(combinedMetric->metrics().size() == m_programs.size()); } +BOOST_FIXTURE_TEST_CASE(build_should_pass_program_caches_to_metrics, FitnessMetricFactoryFixture) +{ + assert(m_programs.size() == 3); + vector> caches = { + make_shared(m_programs[0]), + make_shared(m_programs[1]), + make_shared(m_programs[2]), + }; + + m_options.metric = MetricChoice::RelativeCodeSize; + unique_ptr metric = FitnessMetricFactory::build(m_options, m_programs, caches); + BOOST_REQUIRE(metric != nullptr); + + auto combinedMetric = dynamic_cast(metric.get()); + BOOST_REQUIRE(combinedMetric != nullptr); + BOOST_REQUIRE(combinedMetric->metrics().size() == caches.size()); + + for (size_t i = 0; i < caches.size(); ++i) + { + auto programBasedMetric = dynamic_cast(combinedMetric->metrics()[i].get()); + BOOST_REQUIRE(programBasedMetric != nullptr); + BOOST_TEST(programBasedMetric->programCache() == caches[i].get()); + } +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(PopulationFactoryTest) diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index 054b6e99e..218b358a0 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -158,9 +158,11 @@ FitnessMetricFactory::Options FitnessMetricFactory::Options::fromCommandLine(po: unique_ptr FitnessMetricFactory::build( Options const& _options, - vector _programs + vector _programs, + vector> _programCaches ) { + assert(_programCaches.size() == _programs.size()); assert(_programs.size() > 0 && "Validations should prevent this from being executed with zero files."); vector> metrics; @@ -168,10 +170,10 @@ unique_ptr FitnessMetricFactory::build( { case MetricChoice::CodeSize: { - for (Program& program: _programs) + for (size_t i = 0; i < _programs.size(); ++i) metrics.push_back(make_unique( - move(program), - nullptr, + _programCaches[i] != nullptr ? optional{} : move(_programs[i]), + move(_programCaches[i]), _options.chromosomeRepetitions )); @@ -179,10 +181,10 @@ unique_ptr FitnessMetricFactory::build( } case MetricChoice::RelativeCodeSize: { - for (Program& program: _programs) + for (size_t i = 0; i < _programs.size(); ++i) metrics.push_back(make_unique( - move(program), - nullptr, + _programCaches[i] != nullptr ? optional{} : move(_programs[i]), + move(_programCaches[i]), _options.relativeMetricScale, _options.chromosomeRepetitions )); @@ -528,6 +530,19 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription() ; keywordDescription.add(metricsDescription); + po::options_description cacheDescription("CACHE", lineLength, minDescriptionLength); + cacheDescription.add_options() + ( + "program-cache", + po::bool_switch(), + "Enables caching of intermediate programs corresponding to chromosome prefixes.\n" + "This speeds up fitness evaluation by a lot but eats tons of memory if the chromosomes are long. " + "Disabled by default since there's currently no way to set an upper limit on memory usage but " + "highly recommended if your computer has enough RAM." + ) + ; + keywordDescription.add(cacheDescription); + po::positional_options_description positionalDescription; positionalDescription.add("input-files", -1); @@ -583,12 +598,15 @@ AlgorithmRunner::Options Phaser::buildAlgorithmRunnerOptions(po::variables_map c void Phaser::runAlgorithm(po::variables_map const& _arguments) { auto programOptions = ProgramFactory::Options::fromCommandLine(_arguments); + auto cacheOptions = ProgramCacheFactory::Options::fromCommandLine(_arguments); auto metricOptions = FitnessMetricFactory::Options::fromCommandLine(_arguments); auto populationOptions = PopulationFactory::Options::fromCommandLine(_arguments); auto algorithmOptions = GeneticAlgorithmFactory::Options::fromCommandLine(_arguments); vector programs = ProgramFactory::build(programOptions); - unique_ptr fitnessMetric = FitnessMetricFactory::build(metricOptions, move(programs)); + vector> programCaches = ProgramCacheFactory::build(cacheOptions, programs); + + unique_ptr fitnessMetric = FitnessMetricFactory::build(metricOptions, move(programs), programCaches); Population population = PopulationFactory::build(populationOptions, move(fitnessMetric)); unique_ptr geneticAlgorithm = GeneticAlgorithmFactory::build( @@ -596,6 +614,6 @@ void Phaser::runAlgorithm(po::variables_map const& _arguments) population.individuals().size() ); - AlgorithmRunner algorithmRunner(population, vector>(programs.size(), nullptr), buildAlgorithmRunnerOptions(_arguments), cout); + AlgorithmRunner algorithmRunner(population, move(programCaches), buildAlgorithmRunnerOptions(_arguments), cout); algorithmRunner.run(*geneticAlgorithm); } diff --git a/tools/yulPhaser/Phaser.h b/tools/yulPhaser/Phaser.h index 256d9a288..ba816044c 100644 --- a/tools/yulPhaser/Phaser.h +++ b/tools/yulPhaser/Phaser.h @@ -120,7 +120,8 @@ public: static std::unique_ptr build( Options const& _options, - std::vector _programs + std::vector _programs, + std::vector> _programCaches ); }; From 339f3ca32c75aecdffb5e86534e50f23fc562355 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Tue, 24 Mar 2020 03:43:07 +0100 Subject: [PATCH 13/76] Fix #8427: Promoted typeError to fatalTypeError in ReferencesResolver::endVisit(UserDefinedTypeName). --- Changelog.md | 3 ++- libsolidity/analysis/ReferencesResolver.cpp | 2 +- .../syntaxTests/structs/member_type_eq_name.sol | 7 +++++++ test/libsolidity/syntaxTests/structs/member_type_func.sol | 8 ++++++++ 4 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/syntaxTests/structs/member_type_eq_name.sol create mode 100644 test/libsolidity/syntaxTests/structs/member_type_func.sol diff --git a/Changelog.md b/Changelog.md index 2472d9467..96f3a1444 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,7 +8,8 @@ Compiler Features: Bugfixes: - * Inline Assembly: Fix internal error when accessing incorrect constant variables. + * Inline Assembly: Fix internal error when accessing invalid constant variables. + * Reference Resolver: Fix internal error when accessing invalid struct members. * Inheritance: Allow public state variables to override functions with dynamic memory types in their return values. * JSON AST: Always add pointer suffix for memory reference types. diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 8ea07f182..94a16d6f1 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -204,7 +204,7 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName) else { _typeName.annotation().type = TypeProvider::emptyTuple(); - typeError(_typeName.location(), "Name has to refer to a struct, enum or contract."); + fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract."); } } diff --git a/test/libsolidity/syntaxTests/structs/member_type_eq_name.sol b/test/libsolidity/syntaxTests/structs/member_type_eq_name.sol new file mode 100644 index 000000000..50f0eb365 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/member_type_eq_name.sol @@ -0,0 +1,7 @@ +contract C { + struct S {t t;} + function f(function(S memory) external) public {} +} +// ---- +// TypeError: (25-26): Name has to refer to a struct, enum or contract. +// TypeError: (53-61): Internal type cannot be used for external function type. diff --git a/test/libsolidity/syntaxTests/structs/member_type_func.sol b/test/libsolidity/syntaxTests/structs/member_type_func.sol new file mode 100644 index 000000000..709a40e26 --- /dev/null +++ b/test/libsolidity/syntaxTests/structs/member_type_func.sol @@ -0,0 +1,8 @@ +contract C { + function f() public {} + struct S {f x;} + function g(function(S memory) external) public {} +} +// ---- +// TypeError: (50-51): Name has to refer to a struct, enum or contract. +// TypeError: (78-86): Internal type cannot be used for external function type. From a97aeb0e6ee946ebf864534a29205bfd7fed15e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 8 Mar 2020 16:55:24 +0100 Subject: [PATCH 14/76] [yul-phaser] AlgorithmRunner: A stronger test for run() output --- test/yulPhaser/AlgorithmRunner.cpp | 55 ++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/test/yulPhaser/AlgorithmRunner.cpp b/test/yulPhaser/AlgorithmRunner.cpp index 0382cc90d..ce6f43bd2 100644 --- a/test/yulPhaser/AlgorithmRunner.cpp +++ b/test/yulPhaser/AlgorithmRunner.cpp @@ -28,6 +28,9 @@ #include #include +#include +#include + using namespace std; using namespace boost::unit_test::framework; using namespace boost::test_tools; @@ -65,8 +68,29 @@ public: class AlgorithmRunnerFixture { protected: + // NOTE: Regexes here should not contain spaces because we strip them before matching + regex RoundSummaryRegex{R"(-+ROUND\d+-+)"}; + + string individualPattern(Individual const& individual) const + { + ostringstream output; + output << "Fitness:" << individual.fitness << ","; + output << "optimisations:" << individual.chromosome; + return output.str(); + return output.str(); + } + + bool nextLineMatches(stringstream& stream, regex const& pattern) const + { + string line; + if (getline(stream, line).fail()) + return false; + + return regex_match(stripWhitespace(line), pattern); + } + shared_ptr m_fitnessMetric = make_shared(); - output_test_stream m_output; + stringstream m_output; AlgorithmRunner::Options m_options; }; @@ -106,29 +130,24 @@ BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, AlgorithmRu BOOST_TEST(algorithm.m_currentRound == 10); } -BOOST_FIXTURE_TEST_CASE(run_should_print_the_top_chromosome, AlgorithmRunnerFixture) +BOOST_FIXTURE_TEST_CASE(run_should_print_round_summary_after_each_round, AlgorithmRunnerFixture) { - // run() is allowed to print more but should at least print the first one + Population population(m_fitnessMetric, {Chromosome("fcCUnDve"), Chromosome("jsxIOo"), Chromosome("ighTLM")}); m_options.maxRounds = 1; - AlgorithmRunner runner( - // NOTE: Chromosomes chosen so that they're not substrings of each other and are not - // words likely to appear in the output in normal circumstances. - Population(m_fitnessMetric, {Chromosome("fcCUnDve"), Chromosome("jsxIOo"), Chromosome("ighTLM")}), - {}, - m_options, - m_output - ); + AlgorithmRunner runner(population, {}, m_options, m_output); + RandomisingAlgorithm algorithm; - CountingAlgorithm algorithm; + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, RoundSummaryRegex)); + for (auto const& individual: runner.population().individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); - BOOST_TEST(m_output.is_empty()); runner.run(algorithm); - BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(runner.population().individuals()[0].chromosome)) == 1); - runner.run(algorithm); - runner.run(algorithm); - runner.run(algorithm); - BOOST_TEST(countSubstringOccurrences(m_output.str(), toString(runner.population().individuals()[0].chromosome)) == 4); + BOOST_TEST(nextLineMatches(m_output, RoundSummaryRegex)); + for (auto const& individual: runner.population().individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(m_output.peek() == EOF); } BOOST_FIXTURE_TEST_CASE(run_should_save_initial_population_to_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture) From d6b96063f8ed816d220fa28537ccd277cff2363a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sun, 8 Mar 2020 16:59:14 +0100 Subject: [PATCH 15/76] [yul-phaser] AlgorithmRunner: Make all tests use population from AlgorithmRunnerFixture --- test/yulPhaser/AlgorithmRunner.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/test/yulPhaser/AlgorithmRunner.cpp b/test/yulPhaser/AlgorithmRunner.cpp index ce6f43bd2..80a431fdc 100644 --- a/test/yulPhaser/AlgorithmRunner.cpp +++ b/test/yulPhaser/AlgorithmRunner.cpp @@ -90,6 +90,7 @@ protected: } shared_ptr m_fitnessMetric = make_shared(); + Population const m_population = Population::makeRandom(m_fitnessMetric, 5, 0, 20); stringstream m_output; AlgorithmRunner::Options m_options; }; @@ -109,7 +110,6 @@ public: protected: TemporaryDirectory m_tempDir; string const m_autosavePath = m_tempDir.memberPath("population-autosave.txt"); - Population const m_population = Population::makeRandom(m_fitnessMetric, 5, 0, 20); RandomisingAlgorithm m_algorithm; }; @@ -119,7 +119,7 @@ BOOST_AUTO_TEST_SUITE(AlgorithmRunnerTest) BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, AlgorithmRunnerFixture) { m_options.maxRounds = 5; - AlgorithmRunner runner(Population(m_fitnessMetric), {}, m_options, m_output); + AlgorithmRunner runner(m_population, {}, m_options, m_output); CountingAlgorithm algorithm; @@ -132,10 +132,8 @@ BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, AlgorithmRu BOOST_FIXTURE_TEST_CASE(run_should_print_round_summary_after_each_round, AlgorithmRunnerFixture) { - Population population(m_fitnessMetric, {Chromosome("fcCUnDve"), Chromosome("jsxIOo"), Chromosome("ighTLM")}); - m_options.maxRounds = 1; - AlgorithmRunner runner(population, {}, m_options, m_output); + AlgorithmRunner runner(m_population, {}, m_options, m_output); RandomisingAlgorithm algorithm; runner.run(algorithm); @@ -269,7 +267,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_clear_cache_at_the_beginning_and_update_it_be }; m_options.maxRounds = 10; - AlgorithmRunner runner(Population(m_fitnessMetric), caches, m_options, m_output); + AlgorithmRunner runner(m_population, caches, m_options, m_output); CountingAlgorithm algorithm; BOOST_TEST(algorithm.m_currentRound == 0); From ec10a3d378f9bf07a1c9e4d99a17c627929911a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 27 Feb 2020 02:04:09 +0100 Subject: [PATCH 16/76] [yul-phaser] Add --show-initial-population option --- test/yulPhaser/AlgorithmRunner.cpp | 29 +++++++++++++++++++++++++++++ tools/yulPhaser/AlgorithmRunner.cpp | 10 ++++++++++ tools/yulPhaser/AlgorithmRunner.h | 2 ++ tools/yulPhaser/Phaser.cpp | 11 +++++++++++ 4 files changed, 52 insertions(+) diff --git a/test/yulPhaser/AlgorithmRunner.cpp b/test/yulPhaser/AlgorithmRunner.cpp index 80a431fdc..8cd4ec803 100644 --- a/test/yulPhaser/AlgorithmRunner.cpp +++ b/test/yulPhaser/AlgorithmRunner.cpp @@ -70,6 +70,7 @@ class AlgorithmRunnerFixture protected: // NOTE: Regexes here should not contain spaces because we strip them before matching regex RoundSummaryRegex{R"(-+ROUND\d+-+)"}; + regex InitialPopulationHeaderRegex{"-+INITIALPOPULATION-+"}; string individualPattern(Individual const& individual) const { @@ -133,6 +134,7 @@ BOOST_FIXTURE_TEST_CASE(run_should_call_runNextRound_once_per_round, AlgorithmRu BOOST_FIXTURE_TEST_CASE(run_should_print_round_summary_after_each_round, AlgorithmRunnerFixture) { m_options.maxRounds = 1; + m_options.showInitialPopulation = false; AlgorithmRunner runner(m_population, {}, m_options, m_output); RandomisingAlgorithm algorithm; @@ -148,6 +150,33 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_round_summary_after_each_round, Algorit BOOST_TEST(m_output.peek() == EOF); } +BOOST_FIXTURE_TEST_CASE(run_should_print_initial_population_if_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 0; + m_options.showInitialPopulation = true; + RandomisingAlgorithm algorithm; + + AlgorithmRunner runner(m_population, {}, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(nextLineMatches(m_output, InitialPopulationHeaderRegex)); + for (auto const& individual: m_population.individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_print_initial_population_if_not_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 0; + m_options.showInitialPopulation = false; + RandomisingAlgorithm algorithm; + + AlgorithmRunner runner(m_population, {}, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(m_output.peek() == EOF); +} + BOOST_FIXTURE_TEST_CASE(run_should_save_initial_population_to_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture) { m_options.maxRounds = 0; diff --git a/tools/yulPhaser/AlgorithmRunner.cpp b/tools/yulPhaser/AlgorithmRunner.cpp index e7ea5a072..dd1275daf 100644 --- a/tools/yulPhaser/AlgorithmRunner.cpp +++ b/tools/yulPhaser/AlgorithmRunner.cpp @@ -31,6 +31,7 @@ using namespace solidity::phaser; void AlgorithmRunner::run(GeneticAlgorithm& _algorithm) { populationAutosave(); + printInitialPopulation(); cacheClear(); for (size_t round = 0; !m_options.maxRounds.has_value() || round < m_options.maxRounds.value(); ++round) @@ -47,6 +48,15 @@ void AlgorithmRunner::run(GeneticAlgorithm& _algorithm) } } +void AlgorithmRunner::printInitialPopulation() const +{ + if (!m_options.showInitialPopulation) + return; + + m_outputStream << "---------- INITIAL POPULATION ----------" << endl; + m_outputStream << m_population; +} + void AlgorithmRunner::populationAutosave() const { if (!m_options.populationAutosaveFile.has_value()) diff --git a/tools/yulPhaser/AlgorithmRunner.h b/tools/yulPhaser/AlgorithmRunner.h index 8ee97ac48..978c6a7cb 100644 --- a/tools/yulPhaser/AlgorithmRunner.h +++ b/tools/yulPhaser/AlgorithmRunner.h @@ -47,6 +47,7 @@ public: bool randomiseDuplicates = false; std::optional minChromosomeLength = std::nullopt; std::optional maxChromosomeLength = std::nullopt; + bool showInitialPopulation = false; }; AlgorithmRunner( @@ -66,6 +67,7 @@ public: Population const& population() const { return m_population; } private: + void printInitialPopulation() const; void populationAutosave() const; void randomiseDuplicates(); void cacheClear(); diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index 218b358a0..50cad1659 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -543,6 +543,16 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription() ; keywordDescription.add(cacheDescription); + po::options_description outputDescription("OUTPUT", lineLength, minDescriptionLength); + outputDescription.add_options() + ( + "show-initial-population", + po::bool_switch(), + "Print the state of the population before the algorithm starts." + ) + ; + keywordDescription.add(outputDescription); + po::positional_options_description positionalDescription; positionalDescription.add("input-files", -1); @@ -592,6 +602,7 @@ AlgorithmRunner::Options Phaser::buildAlgorithmRunnerOptions(po::variables_map c !_arguments["no-randomise-duplicates"].as(), _arguments["min-chromosome-length"].as(), _arguments["max-chromosome-length"].as(), + _arguments["show-initial-population"].as(), }; } From c875b3d94468ef9199a5ceb6428cd5b540099e53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 27 Feb 2020 02:07:48 +0100 Subject: [PATCH 17/76] [yul-phaser] Add --show-only-top-chromosome and --hide-round options --- test/yulPhaser/AlgorithmRunner.cpp | 100 ++++++++++++++++++++++++++++ tools/yulPhaser/AlgorithmRunner.cpp | 29 +++++++- tools/yulPhaser/AlgorithmRunner.h | 6 ++ tools/yulPhaser/Phaser.cpp | 12 ++++ 4 files changed, 144 insertions(+), 3 deletions(-) diff --git a/test/yulPhaser/AlgorithmRunner.cpp b/test/yulPhaser/AlgorithmRunner.cpp index 8cd4ec803..b3379d661 100644 --- a/test/yulPhaser/AlgorithmRunner.cpp +++ b/test/yulPhaser/AlgorithmRunner.cpp @@ -78,6 +78,12 @@ protected: output << "Fitness:" << individual.fitness << ","; output << "optimisations:" << individual.chromosome; return output.str(); + } + + string topChromosomePattern(size_t roundNumber, Individual const& individual) const + { + ostringstream output; + output << roundNumber << "\\|" << individualPattern(individual); return output.str(); } @@ -135,6 +141,8 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_round_summary_after_each_round, Algorit { m_options.maxRounds = 1; m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = false; + m_options.showRoundInfo = true; AlgorithmRunner runner(m_population, {}, m_options, m_output); RandomisingAlgorithm algorithm; @@ -150,10 +158,83 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_round_summary_after_each_round, Algorit BOOST_TEST(m_output.peek() == EOF); } +BOOST_FIXTURE_TEST_CASE(run_should_not_print_round_summary_if_not_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = false; + m_options.showRoundInfo = false; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, regex(""))); + for (auto const& individual: runner.population().individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_print_population_if_its_empty, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = false; + m_options.showRoundInfo = true; + AlgorithmRunner runner(Population(m_fitnessMetric), {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, RoundSummaryRegex)); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_only_top_chromosome_if_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = true; + m_options.showRoundInfo = true; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, regex(topChromosomePattern(1, runner.population().individuals()[0])))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_print_round_number_for_top_chromosome_if_round_info_not_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = true; + m_options.showRoundInfo = false; + AlgorithmRunner runner(m_population, {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(runner.population().individuals()[0])))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_not_print_population_if_its_empty_and_only_top_chromosome_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 3; + m_options.showRoundInfo = true; + m_options.showInitialPopulation = false; + m_options.showOnlyTopChromosome = true; + AlgorithmRunner runner(Population(m_fitnessMetric), {}, m_options, m_output); + RandomisingAlgorithm algorithm; + + runner.run(algorithm); + BOOST_TEST(m_output.peek() == EOF); +} + BOOST_FIXTURE_TEST_CASE(run_should_print_initial_population_if_requested, AlgorithmRunnerFixture) { m_options.maxRounds = 0; m_options.showInitialPopulation = true; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = false; RandomisingAlgorithm algorithm; AlgorithmRunner runner(m_population, {}, m_options, m_output); @@ -169,6 +250,8 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_print_initial_population_if_not_requested { m_options.maxRounds = 0; m_options.showInitialPopulation = false; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = false; RandomisingAlgorithm algorithm; AlgorithmRunner runner(m_population, {}, m_options, m_output); @@ -177,6 +260,23 @@ BOOST_FIXTURE_TEST_CASE(run_should_not_print_initial_population_if_not_requested BOOST_TEST(m_output.peek() == EOF); } +BOOST_FIXTURE_TEST_CASE(run_should_print_whole_initial_population_even_if_only_top_chromosome_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 0; + m_options.showInitialPopulation = true; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = true; + RandomisingAlgorithm algorithm; + + AlgorithmRunner runner(m_population, {}, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(nextLineMatches(m_output, InitialPopulationHeaderRegex)); + for (auto const& individual: m_population.individuals()) + BOOST_TEST(nextLineMatches(m_output, regex(individualPattern(individual)))); + BOOST_TEST(m_output.peek() == EOF); +} + BOOST_FIXTURE_TEST_CASE(run_should_save_initial_population_to_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture) { m_options.maxRounds = 0; diff --git a/tools/yulPhaser/AlgorithmRunner.cpp b/tools/yulPhaser/AlgorithmRunner.cpp index dd1275daf..90440e120 100644 --- a/tools/yulPhaser/AlgorithmRunner.cpp +++ b/tools/yulPhaser/AlgorithmRunner.cpp @@ -41,13 +41,36 @@ void AlgorithmRunner::run(GeneticAlgorithm& _algorithm) m_population = _algorithm.runNextRound(m_population); randomiseDuplicates(); - m_outputStream << "---------- ROUND " << round + 1 << " ----------" << endl; - m_outputStream << m_population; - + printRoundSummary(round); populationAutosave(); } } +void AlgorithmRunner::printRoundSummary( + size_t _round +) const +{ + if (!m_options.showOnlyTopChromosome) + { + if (m_options.showRoundInfo) + { + m_outputStream << "---------- ROUND " << _round + 1; + m_outputStream << " ----------" << endl; + } + else if (m_population.individuals().size() > 0) + m_outputStream << endl; + + m_outputStream << m_population; + } + else if (m_population.individuals().size() > 0) + { + if (m_options.showRoundInfo) + m_outputStream << setw(5) << _round + 1 << " | "; + + m_outputStream << m_population.individuals()[0] << endl; + } +} + void AlgorithmRunner::printInitialPopulation() const { if (!m_options.showInitialPopulation) diff --git a/tools/yulPhaser/AlgorithmRunner.h b/tools/yulPhaser/AlgorithmRunner.h index 978c6a7cb..abef48869 100644 --- a/tools/yulPhaser/AlgorithmRunner.h +++ b/tools/yulPhaser/AlgorithmRunner.h @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -48,6 +49,8 @@ public: std::optional minChromosomeLength = std::nullopt; std::optional maxChromosomeLength = std::nullopt; bool showInitialPopulation = false; + bool showOnlyTopChromosome = false; + bool showRoundInfo = true; }; AlgorithmRunner( @@ -67,6 +70,9 @@ public: Population const& population() const { return m_population; } private: + void printRoundSummary( + size_t _round + ) const; void printInitialPopulation() const; void populationAutosave() const; void randomiseDuplicates(); diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index 50cad1659..344b93208 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -550,6 +550,16 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription() po::bool_switch(), "Print the state of the population before the algorithm starts." ) + ( + "show-only-top-chromosome", + po::bool_switch(), + "Print only the best chromosome found in each round rather than the whole population." + ) + ( + "hide-round", + po::bool_switch(), + "Hide information about the current round (round number and elapsed time)." + ) ; keywordDescription.add(outputDescription); @@ -603,6 +613,8 @@ AlgorithmRunner::Options Phaser::buildAlgorithmRunnerOptions(po::variables_map c _arguments["min-chromosome-length"].as(), _arguments["max-chromosome-length"].as(), _arguments["show-initial-population"].as(), + _arguments["show-only-top-chromosome"].as(), + !_arguments["hide-round"].as(), }; } From 47c3b558f2fc399f27b9e9c478b413e1f073645b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Thu, 27 Feb 2020 02:08:40 +0100 Subject: [PATCH 18/76] [yul-phaser] AlgorithmRunner: Print elapsed time after each round --- test/yulPhaser/AlgorithmRunner.cpp | 2 +- tools/yulPhaser/AlgorithmRunner.cpp | 14 ++++++++++++-- tools/yulPhaser/AlgorithmRunner.h | 4 +++- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/test/yulPhaser/AlgorithmRunner.cpp b/test/yulPhaser/AlgorithmRunner.cpp index b3379d661..831cb360a 100644 --- a/test/yulPhaser/AlgorithmRunner.cpp +++ b/test/yulPhaser/AlgorithmRunner.cpp @@ -69,7 +69,7 @@ class AlgorithmRunnerFixture { protected: // NOTE: Regexes here should not contain spaces because we strip them before matching - regex RoundSummaryRegex{R"(-+ROUND\d+-+)"}; + regex RoundSummaryRegex{R"(-+ROUND\d+\[round:[0-9.]+s,total:[0-9.]+s\]-+)"}; regex InitialPopulationHeaderRegex{"-+INITIALPOPULATION-+"}; string individualPattern(Individual const& individual) const diff --git a/tools/yulPhaser/AlgorithmRunner.cpp b/tools/yulPhaser/AlgorithmRunner.cpp index 90440e120..9ddce41bc 100644 --- a/tools/yulPhaser/AlgorithmRunner.cpp +++ b/tools/yulPhaser/AlgorithmRunner.cpp @@ -34,27 +34,37 @@ void AlgorithmRunner::run(GeneticAlgorithm& _algorithm) printInitialPopulation(); cacheClear(); + chrono::steady_clock::time_point totalTimeStart = std::chrono::steady_clock::now(); for (size_t round = 0; !m_options.maxRounds.has_value() || round < m_options.maxRounds.value(); ++round) { + chrono::steady_clock::time_point roundTimeStart = std::chrono::steady_clock::now(); cacheStartRound(round + 1); m_population = _algorithm.runNextRound(m_population); randomiseDuplicates(); - printRoundSummary(round); + printRoundSummary(round, roundTimeStart, totalTimeStart); populationAutosave(); } } void AlgorithmRunner::printRoundSummary( - size_t _round + size_t _round, + chrono::steady_clock::time_point _roundTimeStart, + chrono::steady_clock::time_point _totalTimeStart ) const { if (!m_options.showOnlyTopChromosome) { if (m_options.showRoundInfo) { + chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + auto roundTime = chrono::duration_cast(now - _roundTimeStart).count(); + auto totalTime = chrono::duration_cast(now - _totalTimeStart).count(); + m_outputStream << "---------- ROUND " << _round + 1; + m_outputStream << " [round: " << fixed << setprecision(1) << roundTime / 1000.0 << " s,"; + m_outputStream << " total: " << fixed << setprecision(1) << totalTime / 1000.0 << " s]"; m_outputStream << " ----------" << endl; } else if (m_population.individuals().size() > 0) diff --git a/tools/yulPhaser/AlgorithmRunner.h b/tools/yulPhaser/AlgorithmRunner.h index abef48869..7c6946391 100644 --- a/tools/yulPhaser/AlgorithmRunner.h +++ b/tools/yulPhaser/AlgorithmRunner.h @@ -71,7 +71,9 @@ public: private: void printRoundSummary( - size_t _round + size_t _round, + std::chrono::steady_clock::time_point _roundTimeStart, + std::chrono::steady_clock::time_point _totalTimeStart ) const; void printInitialPopulation() const; void populationAutosave() const; From 1272a9335cf0b746d0c93e90071bdd7d572944c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 29 Feb 2020 00:56:47 +0100 Subject: [PATCH 19/76] [yul-phaser] Add --mode option --- tools/yulPhaser/Phaser.cpp | 35 ++++++++++++++++++++++++++++++----- tools/yulPhaser/Phaser.h | 14 +++++++++++++- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index 344b93208..2a4ee4b86 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -46,6 +46,12 @@ namespace po = boost::program_options; namespace { +map const PhaserModeToStringMap = +{ + {PhaserMode::RunAlgorithm, "run-algorithm"}, +}; +map const StringToPhaserModeMap = invertMap(PhaserModeToStringMap); + map const AlgorithmToStringMap = { {Algorithm::Random, "random"}, @@ -71,6 +77,8 @@ map const StringToMetricAggregatorChoiceMap = in } +istream& phaser::operator>>(istream& _inputStream, PhaserMode& _phaserMode) { return deserializeChoice(_inputStream, _phaserMode, StringToPhaserModeMap); } +ostream& phaser::operator<<(ostream& _outputStream, PhaserMode _phaserMode) { return serializeChoice(_outputStream, _phaserMode, PhaserModeToStringMap); } istream& phaser::operator>>(istream& _inputStream, Algorithm& _algorithm) { return deserializeChoice(_inputStream, _algorithm, StringToAlgorithmMap); } ostream& phaser::operator<<(ostream& _outputStream, Algorithm _algorithm) { return serializeChoice(_outputStream, _algorithm, AlgorithmToStringMap); } istream& phaser::operator>>(istream& _inputStream, MetricChoice& _metric) { return deserializeChoice(_inputStream, _metric, StringToMetricChoiceMap); } @@ -348,7 +356,7 @@ void Phaser::main(int _argc, char** _argv) initialiseRNG(arguments.value()); - runAlgorithm(arguments.value()); + runPhaser(arguments.value()); } Phaser::CommandLineDescription Phaser::buildCommandLineDescription() @@ -392,6 +400,12 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription() po::value()->value_name(""), "The number of rounds after which the algorithm should stop. (default=no limit)." ) + ( + "mode", + po::value()->value_name("")->default_value(PhaserMode::RunAlgorithm), + "Mode of operation. The default is to run the algorithm but you can also tell phaser " + "to do something else with its parameters, e.g. just print the optimised programs and exit." + ) ; keywordDescription.add(generalDescription); @@ -618,13 +632,12 @@ AlgorithmRunner::Options Phaser::buildAlgorithmRunnerOptions(po::variables_map c }; } -void Phaser::runAlgorithm(po::variables_map const& _arguments) +void Phaser::runPhaser(po::variables_map const& _arguments) { auto programOptions = ProgramFactory::Options::fromCommandLine(_arguments); auto cacheOptions = ProgramCacheFactory::Options::fromCommandLine(_arguments); auto metricOptions = FitnessMetricFactory::Options::fromCommandLine(_arguments); auto populationOptions = PopulationFactory::Options::fromCommandLine(_arguments); - auto algorithmOptions = GeneticAlgorithmFactory::Options::fromCommandLine(_arguments); vector programs = ProgramFactory::build(programOptions); vector> programCaches = ProgramCacheFactory::build(cacheOptions, programs); @@ -632,11 +645,23 @@ void Phaser::runAlgorithm(po::variables_map const& _arguments) unique_ptr fitnessMetric = FitnessMetricFactory::build(metricOptions, move(programs), programCaches); Population population = PopulationFactory::build(populationOptions, move(fitnessMetric)); + if (_arguments["mode"].as() == PhaserMode::RunAlgorithm) + runAlgorithm(_arguments, move(population), move(programCaches)); +} + +void Phaser::runAlgorithm( + po::variables_map const& _arguments, + Population _population, + vector> _programCaches +) +{ + auto algorithmOptions = GeneticAlgorithmFactory::Options::fromCommandLine(_arguments); + unique_ptr geneticAlgorithm = GeneticAlgorithmFactory::build( algorithmOptions, - population.individuals().size() + _population.individuals().size() ); - AlgorithmRunner algorithmRunner(population, move(programCaches), buildAlgorithmRunnerOptions(_arguments), cout); + AlgorithmRunner algorithmRunner(move(_population), move(_programCaches), buildAlgorithmRunnerOptions(_arguments), cout); algorithmRunner.run(*geneticAlgorithm); } diff --git a/tools/yulPhaser/Phaser.h b/tools/yulPhaser/Phaser.h index ba816044c..ef7c00f41 100644 --- a/tools/yulPhaser/Phaser.h +++ b/tools/yulPhaser/Phaser.h @@ -47,6 +47,11 @@ class Population; class Program; class ProgramCache; +enum class PhaserMode +{ + RunAlgorithm, +}; + enum class Algorithm { Random, @@ -67,6 +72,8 @@ enum class MetricAggregatorChoice Minimum, }; +std::istream& operator>>(std::istream& _inputStream, solidity::phaser::PhaserMode& _phaserMode); +std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::PhaserMode _phaserMode); std::istream& operator>>(std::istream& _inputStream, solidity::phaser::Algorithm& _algorithm); std::ostream& operator<<(std::ostream& _outputStream, solidity::phaser::Algorithm _algorithm); std::istream& operator>>(std::istream& _inputStream, solidity::phaser::MetricChoice& _metric); @@ -223,7 +230,12 @@ private: static void initialiseRNG(boost::program_options::variables_map const& _arguments); static AlgorithmRunner::Options buildAlgorithmRunnerOptions(boost::program_options::variables_map const& _arguments); - static void runAlgorithm(boost::program_options::variables_map const& _arguments); + static void runPhaser(boost::program_options::variables_map const& _arguments); + static void runAlgorithm( + boost::program_options::variables_map const& _arguments, + Population _population, + std::vector> _programCaches + ); }; } From d33ba54a38ebefe8861d947a620188fcb4fc9db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 29 Feb 2020 00:57:24 +0100 Subject: [PATCH 20/76] [yul-phaser] Add print-optimised-programs and print-optimised-asts modes --- tools/yulPhaser/Phaser.cpp | 41 +++++++++++++++++++++++++++++++++++++- tools/yulPhaser/Phaser.h | 8 ++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index 2a4ee4b86..0bfd6a433 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -49,6 +49,8 @@ namespace map const PhaserModeToStringMap = { {PhaserMode::RunAlgorithm, "run-algorithm"}, + {PhaserMode::PrintOptimisedPrograms, "print-optimised-programs"}, + {PhaserMode::PrintOptimisedASTs, "print-optimised-asts"}, }; map const StringToPhaserModeMap = invertMap(PhaserModeToStringMap); @@ -642,11 +644,13 @@ void Phaser::runPhaser(po::variables_map const& _arguments) vector programs = ProgramFactory::build(programOptions); vector> programCaches = ProgramCacheFactory::build(cacheOptions, programs); - unique_ptr fitnessMetric = FitnessMetricFactory::build(metricOptions, move(programs), programCaches); + unique_ptr fitnessMetric = FitnessMetricFactory::build(metricOptions, programs, programCaches); Population population = PopulationFactory::build(populationOptions, move(fitnessMetric)); if (_arguments["mode"].as() == PhaserMode::RunAlgorithm) runAlgorithm(_arguments, move(population), move(programCaches)); + else + printOptimisedProgramsOrASTs(_arguments, population, move(programs), _arguments["mode"].as()); } void Phaser::runAlgorithm( @@ -665,3 +669,38 @@ void Phaser::runAlgorithm( AlgorithmRunner algorithmRunner(move(_population), move(_programCaches), buildAlgorithmRunnerOptions(_arguments), cout); algorithmRunner.run(*geneticAlgorithm); } + +void Phaser::printOptimisedProgramsOrASTs( + po::variables_map const& _arguments, + Population const& _population, + vector _programs, + PhaserMode phaserMode +) +{ + assert(phaserMode == PhaserMode::PrintOptimisedPrograms || phaserMode == PhaserMode::PrintOptimisedASTs); + assert(_programs.size() == _arguments["input-files"].as>().size()); + + if (_population.individuals().size() == 0) + { + cout << "" << endl; + return; + } + + vector const& paths = _arguments["input-files"].as>(); + for (auto& individual: _population.individuals()) + { + cout << "Chromosome: " << individual.chromosome << endl; + + for (size_t i = 0; i < _programs.size(); ++i) + { + for (size_t j = 0; j < _arguments["chromosome-repetitions"].as(); ++j) + _programs[i].optimise(individual.chromosome.optimisationSteps()); + + cout << "Program: " << paths[i] << endl; + if (phaserMode == PhaserMode::PrintOptimisedPrograms) + cout << _programs[i] << endl; + else + cout << _programs[i].toJson() << endl; + } + } +} diff --git a/tools/yulPhaser/Phaser.h b/tools/yulPhaser/Phaser.h index ef7c00f41..77e9e48c8 100644 --- a/tools/yulPhaser/Phaser.h +++ b/tools/yulPhaser/Phaser.h @@ -50,6 +50,8 @@ class ProgramCache; enum class PhaserMode { RunAlgorithm, + PrintOptimisedPrograms, + PrintOptimisedASTs, }; enum class Algorithm @@ -236,6 +238,12 @@ private: Population _population, std::vector> _programCaches ); + static void printOptimisedProgramsOrASTs( + boost::program_options::variables_map const& _arguments, + Population const& _population, + std::vector _programs, + PhaserMode phaserMode + ); }; } From 3e35decf2bade5d52018cfea98f611ad28a649b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 29 Feb 2020 01:53:11 +0100 Subject: [PATCH 21/76] [yul-phaser] ProgramCache: Add ability to gather cache stats --- test/yulPhaser/ProgramCache.cpp | 48 +++++++++++++++++++++++++++ tools/yulPhaser/ProgramCache.cpp | 57 ++++++++++++++++++++++++++++++++ tools/yulPhaser/ProgramCache.h | 26 +++++++++++++++ 3 files changed, 131 insertions(+) diff --git a/test/yulPhaser/ProgramCache.cpp b/test/yulPhaser/ProgramCache.cpp index 598e7a16f..1ed6024bc 100644 --- a/test/yulPhaser/ProgramCache.cpp +++ b/test/yulPhaser/ProgramCache.cpp @@ -71,6 +71,15 @@ protected: BOOST_AUTO_TEST_SUITE(Phaser) BOOST_AUTO_TEST_SUITE(ProgramCacheTest) +BOOST_AUTO_TEST_CASE(CacheStats_operator_plus_should_add_stats_together) +{ + CacheStats statsA{11, 12, 13, {{1, 14}, {2, 15}}}; + CacheStats statsB{21, 22, 23, {{2, 24}, {3, 25}}}; + CacheStats statsC{32, 34, 36, {{1, 14}, {2, 39}, {3, 25}}}; + + BOOST_CHECK(statsA + statsB == statsC); +} + BOOST_FIXTURE_TEST_CASE(optimiseProgram_should_apply_optimisation_steps_to_program, ProgramCacheFixture) { Program expectedProgram = optimisedProgram(m_program, "IuO"); @@ -201,6 +210,45 @@ BOOST_FIXTURE_TEST_CASE(startRound_should_remove_entries_older_than_two_rounds, BOOST_TEST(m_programCache.size() == 0); } +BOOST_FIXTURE_TEST_CASE(gatherStats_should_return_cache_statistics, ProgramCacheFixture) +{ + size_t sizeI = optimisedProgram(m_program, "I").codeSize(); + size_t sizeIu = optimisedProgram(m_program, "Iu").codeSize(); + size_t sizeIuO = optimisedProgram(m_program, "IuO").codeSize(); + size_t sizeL = optimisedProgram(m_program, "L").codeSize(); + size_t sizeLT = optimisedProgram(m_program, "LT").codeSize(); + + m_programCache.optimiseProgram("L"); + m_programCache.optimiseProgram("Iu"); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu"})); + CacheStats expectedStats1{0, 3, sizeL + sizeI + sizeIu, {{0, 3}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats1); + + m_programCache.optimiseProgram("IuO"); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu", "IuO"})); + CacheStats expectedStats2{2, 4, sizeL + sizeI + sizeIu + sizeIuO, {{0, 4}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats2); + + m_programCache.startRound(1); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu", "IuO"})); + BOOST_CHECK(m_programCache.gatherStats() == expectedStats2); + + m_programCache.optimiseProgram("IuO"); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "I", "Iu", "IuO"})); + CacheStats expectedStats3{5, 4, sizeL + sizeI + sizeIu + sizeIuO, {{0, 1}, {1, 3}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats3); + + m_programCache.startRound(2); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"I", "Iu", "IuO"})); + CacheStats expectedStats4{5, 4, sizeI + sizeIu + sizeIuO, {{1, 3}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats4); + + m_programCache.optimiseProgram("LT"); + BOOST_REQUIRE((cachedKeys(m_programCache) == set{"L", "LT", "I", "Iu", "IuO"})); + CacheStats expectedStats5{5, 6, sizeL + sizeLT + sizeI + sizeIu + sizeIuO, {{1, 3}, {2, 2}}}; + BOOST_CHECK(m_programCache.gatherStats() == expectedStats5); +} + BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/tools/yulPhaser/ProgramCache.cpp b/tools/yulPhaser/ProgramCache.cpp index bd3b05114..7acec16bd 100644 --- a/tools/yulPhaser/ProgramCache.cpp +++ b/tools/yulPhaser/ProgramCache.cpp @@ -23,6 +23,30 @@ using namespace std; using namespace solidity::yul; using namespace solidity::phaser; +CacheStats& CacheStats::operator+=(CacheStats const& _other) +{ + hits += _other.hits; + misses += _other.misses; + totalCodeSize += _other.totalCodeSize; + + for (auto& [round, count]: _other.roundEntryCounts) + if (roundEntryCounts.find(round) != roundEntryCounts.end()) + roundEntryCounts.at(round) += count; + else + roundEntryCounts.insert({round, count}); + + return *this; +} + +bool CacheStats::operator==(CacheStats const& _other) const +{ + return + hits == _other.hits && + misses == _other.misses && + totalCodeSize == _other.totalCodeSize && + roundEntryCounts == _other.roundEntryCounts; +} + Program ProgramCache::optimiseProgram( string const& _abbreviatedOptimisationSteps, size_t _repetitionCount @@ -40,6 +64,7 @@ Program ProgramCache::optimiseProgram( { pair->second.roundNumber = m_currentRound; ++prefixSize; + ++m_hits; } else break; @@ -57,6 +82,7 @@ Program ProgramCache::optimiseProgram( intermediateProgram.optimise({stepName}); m_entries.insert({targetOptimisations.substr(0, i), {intermediateProgram, m_currentRound}}); + ++m_misses; } return intermediateProgram; @@ -92,3 +118,34 @@ Program const* ProgramCache::find(string const& _abbreviatedOptimisationSteps) c return &(pair->second.program); } + +CacheStats ProgramCache::gatherStats() const +{ + return { + /* hits = */ m_hits, + /* misses = */ m_misses, + /* totalCodeSize = */ calculateTotalCachedCodeSize(), + /* roundEntryCounts = */ countRoundEntries(), + }; +} + +size_t ProgramCache::calculateTotalCachedCodeSize() const +{ + size_t size = 0; + for (auto const& pair: m_entries) + size += pair.second.program.codeSize(); + + return size; +} + +map ProgramCache::countRoundEntries() const +{ + map counts; + for (auto& pair: m_entries) + if (counts.find(pair.second.roundNumber) != counts.end()) + ++counts.at(pair.second.roundNumber); + else + counts.insert({pair.second.roundNumber, 1}); + + return counts; +} diff --git a/tools/yulPhaser/ProgramCache.h b/tools/yulPhaser/ProgramCache.h index 433ba99c5..8cc830098 100644 --- a/tools/yulPhaser/ProgramCache.h +++ b/tools/yulPhaser/ProgramCache.h @@ -39,6 +39,23 @@ struct CacheEntry roundNumber(_roundNumber) {} }; +/** + * Stores statistics about current cache usage. + */ +struct CacheStats +{ + size_t hits; + size_t misses; + size_t totalCodeSize; + std::map roundEntryCounts; + + CacheStats& operator+=(CacheStats const& _other); + CacheStats operator+(CacheStats const& _other) const { return CacheStats(*this) += _other; } + + bool operator==(CacheStats const& _other) const; + bool operator!=(CacheStats const& _other) const { return !(*this == _other); } +}; + /** * Class that optimises programs one step at a time which allows it to store and later reuse the * results of the intermediate steps. @@ -49,6 +66,8 @@ struct CacheEntry * encountered in the current and the previous rounds. Entries older than that get removed to * conserve memory. * + * @a gatherStats() allows getting statistics useful for determining cache effectiveness. + * * The current strategy does speed things up (about 4:1 hit:miss ratio observed in my limited * experiments) but there's room for improvement. We could fit more useful programs in * the cache by being more picky about which ones we choose. @@ -74,11 +93,16 @@ public: Program const* find(std::string const& _abbreviatedOptimisationSteps) const; bool contains(std::string const& _abbreviatedOptimisationSteps) const { return find(_abbreviatedOptimisationSteps) != nullptr; } + CacheStats gatherStats() const; + std::map const& entries() const { return m_entries; }; Program const& program() const { return m_program; } size_t currentRound() const { return m_currentRound; } private: + size_t calculateTotalCachedCodeSize() const; + std::map countRoundEntries() const; + // The best matching data structure here would be a trie of chromosome prefixes but since // the programs are orders of magnitude larger than the prefixes, it does not really matter. // A map should be good enough. @@ -86,6 +110,8 @@ private: Program m_program; size_t m_currentRound = 0; + size_t m_hits = 0; + size_t m_misses = 0; }; } From cd16a6e1780f9298170b96209cdb3429f72d15f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Sat, 29 Feb 2020 02:14:51 +0100 Subject: [PATCH 22/76] [yul-phaser] Add --show-cache-stats option --- test/yulPhaser/AlgorithmRunner.cpp | 106 ++++++++++++++++++++++++++++ tools/yulPhaser/AlgorithmRunner.cpp | 34 +++++++++ tools/yulPhaser/AlgorithmRunner.h | 2 + tools/yulPhaser/Phaser.cpp | 6 ++ 4 files changed, 148 insertions(+) diff --git a/test/yulPhaser/AlgorithmRunner.cpp b/test/yulPhaser/AlgorithmRunner.cpp index 831cb360a..fa87579dd 100644 --- a/test/yulPhaser/AlgorithmRunner.cpp +++ b/test/yulPhaser/AlgorithmRunner.cpp @@ -19,6 +19,7 @@ #include #include +#include #include @@ -277,6 +278,111 @@ BOOST_FIXTURE_TEST_CASE(run_should_print_whole_initial_population_even_if_only_t BOOST_TEST(m_output.peek() == EOF); } +BOOST_FIXTURE_TEST_CASE(run_should_print_cache_stats_if_requested, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 4; + m_options.showInitialPopulation = false; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = true; + m_options.showCacheStats = true; + RandomisingAlgorithm algorithm; + + vector sourceStreams = { + CharStream("{mstore(10, 20)}", ""), + CharStream("{mstore(10, 20)\nsstore(10, 20)}", ""), + }; + vector programs = { + get(Program::load(sourceStreams[0])), + get(Program::load(sourceStreams[1])), + }; + vector> caches = { + make_shared(programs[0]), + make_shared(programs[1]), + }; + shared_ptr fitnessMetric = make_shared(vector>{ + make_shared(nullopt, caches[0]), + make_shared(nullopt, caches[1]), + }); + Population population = Population::makeRandom(fitnessMetric, 2, 0, 5); + + AlgorithmRunner runner(population, caches, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(caches[0]->currentRound() == m_options.maxRounds.value()); + BOOST_TEST(caches[1]->currentRound() == m_options.maxRounds.value()); + + CacheStats stats = caches[0]->gatherStats() + caches[1]->gatherStats(); + + for (size_t i = 0; i < m_options.maxRounds.value() - 1; ++i) + { + BOOST_TEST(nextLineMatches(m_output, regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); + if (i > 0) + BOOST_TEST(nextLineMatches(m_output, regex(R"(Round\d+:\d+entries)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Round\d+:\d+entries)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalhits:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalmisses:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Sizeofcachedcode:\d+)"))); + } + + BOOST_REQUIRE(stats.roundEntryCounts.size() == 2); + BOOST_REQUIRE(stats.roundEntryCounts.count(m_options.maxRounds.value() - 1) == 1); + BOOST_REQUIRE(stats.roundEntryCounts.count(m_options.maxRounds.value()) == 1); + + size_t round = m_options.maxRounds.value(); + BOOST_TEST(nextLineMatches(m_output, regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); + BOOST_TEST(nextLineMatches(m_output, regex("Round" + toString(round - 1) + ":" + toString(stats.roundEntryCounts[round - 1]) + "entries"))); + BOOST_TEST(nextLineMatches(m_output, regex("Round" + toString(round) + ":" + toString(stats.roundEntryCounts[round]) + "entries"))); + BOOST_TEST(nextLineMatches(m_output, regex("Totalhits:" + toString(stats.hits)))); + BOOST_TEST(nextLineMatches(m_output, regex("Totalmisses:" + toString(stats.misses)))); + BOOST_TEST(nextLineMatches(m_output, regex("Sizeofcachedcode:" + toString(stats.totalCodeSize)))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_message_if_cache_stats_requested_but_cache_disabled, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = true; + m_options.showCacheStats = true; + RandomisingAlgorithm algorithm; + + AlgorithmRunner runner(m_population, {nullptr}, m_options, m_output); + runner.run(algorithm); + + BOOST_TEST(nextLineMatches(m_output, regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); + BOOST_TEST(nextLineMatches(m_output, regex(stripWhitespace("Program cache disabled")))); + BOOST_TEST(m_output.peek() == EOF); +} + +BOOST_FIXTURE_TEST_CASE(run_should_print_partial_stats_and_message_if_some_caches_disabled, AlgorithmRunnerFixture) +{ + m_options.maxRounds = 1; + m_options.showInitialPopulation = false; + m_options.showRoundInfo = false; + m_options.showOnlyTopChromosome = true; + m_options.showCacheStats = true; + RandomisingAlgorithm algorithm; + + CharStream sourceStream = CharStream("{}", ""); + shared_ptr cache = make_shared(get(Program::load(sourceStream))); + + AlgorithmRunner runner(m_population, {cache, nullptr}, m_options, m_output); + BOOST_REQUIRE(cache->gatherStats().roundEntryCounts.size() == 0); + + runner.run(algorithm); + BOOST_TEST(nextLineMatches(m_output, regex(".*"))); + BOOST_TEST(nextLineMatches(m_output, regex("-+CACHESTATS-+"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalhits:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Totalmisses:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(R"(Sizeofcachedcode:\d+)"))); + BOOST_TEST(nextLineMatches(m_output, regex(stripWhitespace("Program cache disabled for 1 out of 2 programs")))); + BOOST_TEST(m_output.peek() == EOF); +} + BOOST_FIXTURE_TEST_CASE(run_should_save_initial_population_to_file_if_autosave_file_specified, AlgorithmRunnerAutosaveFixture) { m_options.maxRounds = 0; diff --git a/tools/yulPhaser/AlgorithmRunner.cpp b/tools/yulPhaser/AlgorithmRunner.cpp index 9ddce41bc..5e03e2820 100644 --- a/tools/yulPhaser/AlgorithmRunner.cpp +++ b/tools/yulPhaser/AlgorithmRunner.cpp @@ -44,6 +44,7 @@ void AlgorithmRunner::run(GeneticAlgorithm& _algorithm) randomiseDuplicates(); printRoundSummary(round, roundTimeStart, totalTimeStart); + printCacheStats(); populationAutosave(); } } @@ -90,6 +91,39 @@ void AlgorithmRunner::printInitialPopulation() const m_outputStream << m_population; } +void AlgorithmRunner::printCacheStats() const +{ + if (!m_options.showCacheStats) + return; + + CacheStats totalStats{}; + size_t disabledCacheCount = 0; + for (size_t i = 0; i < m_programCaches.size(); ++i) + if (m_programCaches[i] != nullptr) + totalStats += m_programCaches[i]->gatherStats(); + else + ++disabledCacheCount; + + m_outputStream << "---------- CACHE STATS ----------" << endl; + + if (disabledCacheCount < m_programCaches.size()) + { + for (auto& [round, count]: totalStats.roundEntryCounts) + m_outputStream << "Round " << round << ": " << count << " entries" << endl; + m_outputStream << "Total hits: " << totalStats.hits << endl; + m_outputStream << "Total misses: " << totalStats.misses << endl; + m_outputStream << "Size of cached code: " << totalStats.totalCodeSize << endl; + } + + if (disabledCacheCount == m_programCaches.size()) + m_outputStream << "Program cache disabled" << endl; + else if (disabledCacheCount > 0) + { + m_outputStream << "Program cache disabled for " << disabledCacheCount << " out of "; + m_outputStream << m_programCaches.size() << " programs" << endl; + } +} + void AlgorithmRunner::populationAutosave() const { if (!m_options.populationAutosaveFile.has_value()) diff --git a/tools/yulPhaser/AlgorithmRunner.h b/tools/yulPhaser/AlgorithmRunner.h index 7c6946391..d1cffb30a 100644 --- a/tools/yulPhaser/AlgorithmRunner.h +++ b/tools/yulPhaser/AlgorithmRunner.h @@ -51,6 +51,7 @@ public: bool showInitialPopulation = false; bool showOnlyTopChromosome = false; bool showRoundInfo = true; + bool showCacheStats = false; }; AlgorithmRunner( @@ -76,6 +77,7 @@ private: std::chrono::steady_clock::time_point _totalTimeStart ) const; void printInitialPopulation() const; + void printCacheStats() const; void populationAutosave() const; void randomiseDuplicates(); void cacheClear(); diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index 0bfd6a433..6e6ec7a3f 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -576,6 +576,11 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription() po::bool_switch(), "Hide information about the current round (round number and elapsed time)." ) + ( + "show-cache-stats", + po::bool_switch(), + "Print information about cache size and effectiveness after each round." + ) ; keywordDescription.add(outputDescription); @@ -631,6 +636,7 @@ AlgorithmRunner::Options Phaser::buildAlgorithmRunnerOptions(po::variables_map c _arguments["show-initial-population"].as(), _arguments["show-only-top-chromosome"].as(), !_arguments["hide-round"].as(), + _arguments["show-cache-stats"].as(), }; } From 58e3fca3de9245d170800964b26e61e050188657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 9 Mar 2020 19:19:42 +0100 Subject: [PATCH 23/76] [yul-phaser] AlgorithmRunner: Measure CPU time rather than wall-clock time --- tools/yulPhaser/AlgorithmRunner.cpp | 18 +++++++++--------- tools/yulPhaser/AlgorithmRunner.h | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/yulPhaser/AlgorithmRunner.cpp b/tools/yulPhaser/AlgorithmRunner.cpp index 5e03e2820..d3bda6960 100644 --- a/tools/yulPhaser/AlgorithmRunner.cpp +++ b/tools/yulPhaser/AlgorithmRunner.cpp @@ -34,10 +34,10 @@ void AlgorithmRunner::run(GeneticAlgorithm& _algorithm) printInitialPopulation(); cacheClear(); - chrono::steady_clock::time_point totalTimeStart = std::chrono::steady_clock::now(); + clock_t totalTimeStart = clock(); for (size_t round = 0; !m_options.maxRounds.has_value() || round < m_options.maxRounds.value(); ++round) { - chrono::steady_clock::time_point roundTimeStart = std::chrono::steady_clock::now(); + clock_t roundTimeStart = clock(); cacheStartRound(round + 1); m_population = _algorithm.runNextRound(m_population); @@ -51,21 +51,21 @@ void AlgorithmRunner::run(GeneticAlgorithm& _algorithm) void AlgorithmRunner::printRoundSummary( size_t _round, - chrono::steady_clock::time_point _roundTimeStart, - chrono::steady_clock::time_point _totalTimeStart + clock_t _roundTimeStart, + clock_t _totalTimeStart ) const { if (!m_options.showOnlyTopChromosome) { if (m_options.showRoundInfo) { - chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); - auto roundTime = chrono::duration_cast(now - _roundTimeStart).count(); - auto totalTime = chrono::duration_cast(now - _totalTimeStart).count(); + clock_t now = clock(); + double roundTime = static_cast(now - _roundTimeStart) / CLOCKS_PER_SEC; + double totalTime = static_cast(now - _totalTimeStart) / CLOCKS_PER_SEC; m_outputStream << "---------- ROUND " << _round + 1; - m_outputStream << " [round: " << fixed << setprecision(1) << roundTime / 1000.0 << " s,"; - m_outputStream << " total: " << fixed << setprecision(1) << totalTime / 1000.0 << " s]"; + m_outputStream << " [round: " << fixed << setprecision(1) << roundTime << " s,"; + m_outputStream << " total: " << fixed << setprecision(1) << totalTime << " s]"; m_outputStream << " ----------" << endl; } else if (m_population.individuals().size() > 0) diff --git a/tools/yulPhaser/AlgorithmRunner.h b/tools/yulPhaser/AlgorithmRunner.h index d1cffb30a..4326ce227 100644 --- a/tools/yulPhaser/AlgorithmRunner.h +++ b/tools/yulPhaser/AlgorithmRunner.h @@ -24,7 +24,7 @@ #include #include -#include +#include #include #include @@ -73,8 +73,8 @@ public: private: void printRoundSummary( size_t _round, - std::chrono::steady_clock::time_point _roundTimeStart, - std::chrono::steady_clock::time_point _totalTimeStart + clock_t _roundTimeStart, + clock_t _totalTimeStart ) const; void printInitialPopulation() const; void printCacheStats() const; From 10e8d3616c0d24428f97b6e4d1ce62f03b51d642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 9 Mar 2020 19:23:25 +0100 Subject: [PATCH 24/76] [yul-phaser] AlgorithmRunner: Print total time when showing only the top chromosome --- test/yulPhaser/AlgorithmRunner.cpp | 2 +- tools/yulPhaser/AlgorithmRunner.cpp | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/test/yulPhaser/AlgorithmRunner.cpp b/test/yulPhaser/AlgorithmRunner.cpp index fa87579dd..9d062d0ac 100644 --- a/test/yulPhaser/AlgorithmRunner.cpp +++ b/test/yulPhaser/AlgorithmRunner.cpp @@ -84,7 +84,7 @@ protected: string topChromosomePattern(size_t roundNumber, Individual const& individual) const { ostringstream output; - output << roundNumber << "\\|" << individualPattern(individual); + output << roundNumber << R"(\|[0-9.]+\|)" << individualPattern(individual); return output.str(); } diff --git a/tools/yulPhaser/AlgorithmRunner.cpp b/tools/yulPhaser/AlgorithmRunner.cpp index d3bda6960..c402e5d84 100644 --- a/tools/yulPhaser/AlgorithmRunner.cpp +++ b/tools/yulPhaser/AlgorithmRunner.cpp @@ -55,14 +55,14 @@ void AlgorithmRunner::printRoundSummary( clock_t _totalTimeStart ) const { + clock_t now = clock(); + double roundTime = static_cast(now - _roundTimeStart) / CLOCKS_PER_SEC; + double totalTime = static_cast(now - _totalTimeStart) / CLOCKS_PER_SEC; + if (!m_options.showOnlyTopChromosome) { if (m_options.showRoundInfo) { - clock_t now = clock(); - double roundTime = static_cast(now - _roundTimeStart) / CLOCKS_PER_SEC; - double totalTime = static_cast(now - _totalTimeStart) / CLOCKS_PER_SEC; - m_outputStream << "---------- ROUND " << _round + 1; m_outputStream << " [round: " << fixed << setprecision(1) << roundTime << " s,"; m_outputStream << " total: " << fixed << setprecision(1) << totalTime << " s]"; @@ -76,7 +76,10 @@ void AlgorithmRunner::printRoundSummary( else if (m_population.individuals().size() > 0) { if (m_options.showRoundInfo) + { m_outputStream << setw(5) << _round + 1 << " | "; + m_outputStream << setw(5) << fixed << setprecision(1) << totalTime << " | "; + } m_outputStream << m_population.individuals()[0] << endl; } From 3f524ccfe5d1c0bb7c5f18e814d4fd3a196a8b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 9 Mar 2020 19:24:37 +0100 Subject: [PATCH 25/76] [yul-phaser] Population: Print individuals in a more compact way --- test/yulPhaser/AlgorithmRunner.cpp | 3 +-- tools/yulPhaser/Population.cpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/yulPhaser/AlgorithmRunner.cpp b/test/yulPhaser/AlgorithmRunner.cpp index 9d062d0ac..8dd6e7564 100644 --- a/test/yulPhaser/AlgorithmRunner.cpp +++ b/test/yulPhaser/AlgorithmRunner.cpp @@ -76,8 +76,7 @@ protected: string individualPattern(Individual const& individual) const { ostringstream output; - output << "Fitness:" << individual.fitness << ","; - output << "optimisations:" << individual.chromosome; + output << individual.fitness << individual.chromosome; return output.str(); } diff --git a/tools/yulPhaser/Population.cpp b/tools/yulPhaser/Population.cpp index 87df4e59b..169cbfe30 100644 --- a/tools/yulPhaser/Population.cpp +++ b/tools/yulPhaser/Population.cpp @@ -43,8 +43,7 @@ ostream& operator<<(ostream& _stream, Population const& _population); ostream& phaser::operator<<(ostream& _stream, Individual const& _individual) { - _stream << "Fitness: " << _individual.fitness; - _stream << ", optimisations: " << _individual.chromosome; + _stream << _individual.fitness << " " << _individual.chromosome; return _stream; } From e41ea6d25e6ff3269248e4110c99376ceacdc098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 9 Mar 2020 19:26:40 +0100 Subject: [PATCH 26/76] [yul-phaser] Add --show-seed option and don't print seed by default --- tools/yulPhaser/Phaser.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/yulPhaser/Phaser.cpp b/tools/yulPhaser/Phaser.cpp index 6e6ec7a3f..2e38edbf0 100644 --- a/tools/yulPhaser/Phaser.cpp +++ b/tools/yulPhaser/Phaser.cpp @@ -581,6 +581,11 @@ Phaser::CommandLineDescription Phaser::buildCommandLineDescription() po::bool_switch(), "Print information about cache size and effectiveness after each round." ) + ( + "show-seed", + po::bool_switch(), + "Print the selected random seed." + ) ; keywordDescription.add(outputDescription); @@ -622,7 +627,8 @@ void Phaser::initialiseRNG(po::variables_map const& _arguments) seed = SimulationRNG::generateSeed(); SimulationRNG::reset(seed); - cout << "Random seed: " << seed << endl; + if (_arguments["show-seed"].as()) + cout << "Random seed: " << seed << endl; } AlgorithmRunner::Options Phaser::buildAlgorithmRunnerOptions(po::variables_map const& _arguments) From e16c0c4133d4dcd624c6c1962db69449cb3da568 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Wed, 25 Mar 2020 23:46:26 -0400 Subject: [PATCH 27/76] Convert operator+(Population, Population) into a hidden friend --- tools/yulPhaser/Population.cpp | 6 ++++++ tools/yulPhaser/Population.h | 7 ++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/tools/yulPhaser/Population.cpp b/tools/yulPhaser/Population.cpp index 87df4e59b..71b32da70 100644 --- a/tools/yulPhaser/Population.cpp +++ b/tools/yulPhaser/Population.cpp @@ -118,15 +118,21 @@ Population Population::crossover(PairSelection const& _selection, function _mutation) const; Population crossover(PairSelection const& _selection, std::function _crossover) const; - friend Population (::operator+)(Population _a, Population _b); + + friend Population operator+(Population _a, Population _b); std::shared_ptr fitnessMetric() { return m_fitnessMetric; } std::vector const& individuals() const { return m_individuals; } From 5b4ea1eb895d5edc9a24ee5c6f96d8580eceec08 Mon Sep 17 00:00:00 2001 From: Martin Lundfall Date: Tue, 24 Mar 2020 13:22:25 +0100 Subject: [PATCH 28/76] CommandLineInterface: add storage-layout option to --combined-json --- Changelog.md | 2 +- solc/CommandLineInterface.cpp | 27 ++++++++++++++++++++++++--- solc/CommandLineInterface.h | 1 + 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 2472d9467..031fe5a67 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,7 +5,7 @@ Language Features: Compiler Features: * Metadata: Added support for IPFS hashes of large files that need to be split in multiple chunks. - + * Commandline Interface: Enable output of storage layout with `--storage-layout`. Bugfixes: * Inline Assembly: Fix internal error when accessing incorrect constant variables. diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp index e91f6446c..90c2931c5 100644 --- a/solc/CommandLineInterface.cpp +++ b/solc/CommandLineInterface.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -147,6 +148,7 @@ static string const g_strOptimizeYul = "optimize-yul"; static string const g_strOutputDir = "output-dir"; static string const g_strOverwrite = "overwrite"; static string const g_strRevertStrings = "revert-strings"; +static string const g_strStorageLayout = "storage-layout"; /// Possible arguments to for --revert-strings static set const g_revertStringsArgs @@ -207,6 +209,7 @@ static string const g_argOptimizeRuns = g_strOptimizeRuns; static string const g_argOutputDir = g_strOutputDir; static string const g_argSignatureHashes = g_strSignatureHashes; static string const g_argStandardJSON = g_strStandardJSON; +static string const g_argStorageLayout = g_strStorageLayout; static string const g_argStrictAssembly = g_strStrictAssembly; static string const g_argVersion = g_strVersion; static string const g_stdinFileName = g_stdinFileNameStr; @@ -231,7 +234,8 @@ static set const g_combinedJsonArgs g_strOpcodes, g_strSignatureHashes, g_strSrcMap, - g_strSrcMapRuntime + g_strSrcMapRuntime, + g_strStorageLayout }; /// Possible arguments to for --machine @@ -293,7 +297,8 @@ static bool needsHumanTargetedStdout(po::variables_map const& _args) g_argNatspecUser, g_argNatspecDev, g_argOpcodes, - g_argSignatureHashes + g_argSignatureHashes, + g_argStorageLayout }) if (_args.count(arg)) return true; @@ -433,6 +438,18 @@ void CommandLineInterface::handleABI(string const& _contract) sout() << "Contract JSON ABI" << endl << data << endl; } +void CommandLineInterface::handleStorageLayout(string const& _contract) +{ + if (!m_args.count(g_argStorageLayout)) + return; + + string data = jsonCompactPrint(m_compiler->storageLayout(_contract)); + if (m_args.count(g_argOutputDir)) + createFile(m_compiler->filesystemFriendlyName(_contract) + "_storage.json", data); + else + sout() << "Contract Storage Layout:" << endl << data << endl; +} + void CommandLineInterface::handleNatspec(bool _natspecDev, string const& _contract) { std::string argName; @@ -833,7 +850,8 @@ Allowed options)", (g_argSignatureHashes.c_str(), "Function signature hashes of the contracts.") (g_argNatspecUser.c_str(), "Natspec user documentation of all contracts.") (g_argNatspecDev.c_str(), "Natspec developer documentation of all contracts.") - (g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain."); + (g_argMetadata.c_str(), "Combined Metadata JSON whose Swarm hash is stored on-chain.") + (g_argStorageLayout.c_str(), "Slots, offsets and types of the contract's state variables."); desc.add(outputComponents); po::options_description allOptions = desc; @@ -1276,6 +1294,8 @@ void CommandLineInterface::handleCombinedJSON() contractData[g_strOpcodes] = evmasm::disassemble(m_compiler->object(contractName).bytecode); if (requests.count(g_strAsm) && m_compiler->compilationSuccessful()) contractData[g_strAsm] = m_compiler->assemblyJSON(contractName); + if (requests.count(g_strStorageLayout) && m_compiler->compilationSuccessful()) + contractData[g_strStorageLayout] = jsonCompactPrint(m_compiler->storageLayout(contractName)); if (requests.count(g_strSrcMap) && m_compiler->compilationSuccessful()) { auto map = m_compiler->sourceMapping(contractName); @@ -1653,6 +1673,7 @@ void CommandLineInterface::outputCompilationResults() handleSignatureHashes(contract); handleMetadata(contract); handleABI(contract); + handleStorageLayout(contract); handleNatspec(true, contract); handleNatspec(false, contract); } // end of contracts iteration diff --git a/solc/CommandLineInterface.h b/solc/CommandLineInterface.h index 417501907..f6972cf04 100644 --- a/solc/CommandLineInterface.h +++ b/solc/CommandLineInterface.h @@ -74,6 +74,7 @@ private: void handleNatspec(bool _natspecDev, std::string const& _contract); void handleGasEstimation(std::string const& _contract); void handleFormal(); + void handleStorageLayout(std::string const& _contract); /// Fills @a m_sourceCodes initially and @a m_redirects. bool readInputFilesAndConfigureRemappings(); From 6474a358623ec831c21904e4249739aef7d88d79 Mon Sep 17 00:00:00 2001 From: Martin Lundfall Date: Tue, 24 Mar 2020 17:19:00 +0100 Subject: [PATCH 29/76] CMakeLists: ensure OSX deployment target supports std::visit --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a674c99e..dba182345 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,8 @@ eth_policy() # project name and version should be set after cmake_policy CMP0048 set(PROJECT_VERSION "0.6.5") +# OSX target needed in order to support std::visit +set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) include(TestBigEndian) From 37fb53bebdb602a4e64c51a15ecf6fb624e0cea7 Mon Sep 17 00:00:00 2001 From: Bhargava Shastry Date: Wed, 26 Feb 2020 15:01:05 +0100 Subject: [PATCH 30/76] Add a security policy and link it in README. Co-Authored-By: chriseth --- README.md | 5 +++++ SECURITY.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 SECURITY.md diff --git a/README.md b/README.md index c0a98aefa..c3636ff06 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Solidity is a statically typed, contract-oriented, high-level language for imple - [Development](#development) - [Maintainers](#maintainers) - [License](#license) +- [Security](#security) ## Background @@ -75,3 +76,7 @@ releases [in the projects section](https://github.com/ethereum/solidity/projects Solidity is licensed under [GNU General Public License v3.0](LICENSE.txt). Some third-party code has its [own licensing terms](cmake/templates/license.h.in). + +## Security + +The security policy may be [found here](SECURITY.md). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..dd39453e8 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,52 @@ +# Security Policy + +The Solidity team and community take all security bugs in Solidity seriously. +We appreciate your efforts and responsible disclosure and will make every effort to acknowledge your contributions. + +## Scope + +Bugs in the Solidity repository are in scope. +Bugs in third-party dependencies e.g., jsoncpp, boost etc. are not in scope unless they result in a Solidity specific bug. + +Only bugs that have a demonstrable security impact on smart contracts are in scope. +For example, a Solidity program whose optimization is incorrect (e.g., leads to an incorrect output) qualifies as a security bug. +Please note that the [rules][2] of the [Ethereum bounty program][1] have precedence over this security policy. + +## Supported Versions + +As a general rule, only the latest release gets security updates. +Exceptions may be made when the current breaking release is relatively new, e.g. less than three months old. +If you are reporting a bug, please state clearly the Solidity version(s) it affects. + +Example 1: Assuming the current release is `0.6.3` and a security bug has been found in it that affects both `0.5.x` and `0.6.x` trees, we may not only patch `0.6.3` (the bug-fix release numbered `0.6.4`) but `0.5.x` as well (the bug-fix release numbered `0.5.(x+1)`). + +Example 2: Assuming the current release is `0.6.25` and a security bug has been found in it, we may only patch `0.6.25` (in the bug-fix release numbered `0.6.26`) even if the bug affects a previous tree such as `0.5.x`. + +## Reporting a Vulnerability + +To report a vulnerability, please follow the instructions stated in the [Ethereum bounty program][1]. + +In the bug report, please include all details necessary to reproduce the vulnerability such as: + +- Input program that triggers the bug +- Compiler version affected +- Target EVM version +- Framework/IDE if applicable +- EVM execution environment/client if applicable +- Operating system + +Please include steps to reproduce the bug you have found in as much detail as possible. + +Once we have received your bug report, we will try to reproduce it and provide a more detailed response. +Once the reported bug has been successfully reproduced, the Solidity team will work on a fix. + +The Solidity team maintains the following JSON-formatted lists of patched security vulnerabilities: + +- [Summary of known security vulnerabilities][3] +- [List of security vulnerabilities affecting a specific version of the compiler][4]. + + +[1]: https://bounty.ethereum.org/ +[2]: https://bounty.ethereum.org/#rules +[3]: https://solidity.readthedocs.io/en/develop/bugs.html +[4]: https://github.com/ethereum/solidity/blob/develop/docs/bugs_by_version.json From d2f65ea8b11f3be7281347390d739b9fec3c61ce Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 24 Mar 2020 20:51:52 +0100 Subject: [PATCH 31/76] [SMTChecker] Add SortProvider --- libsolidity/CMakeLists.txt | 2 + libsolidity/formal/CHC.cpp | 35 +++---- libsolidity/formal/EncodingContext.cpp | 4 +- libsolidity/formal/SolverInterface.h | 90 +----------------- libsolidity/formal/Sorts.cpp | 29 ++++++ libsolidity/formal/Sorts.h | 125 +++++++++++++++++++++++++ libsolidity/formal/SymbolicTypes.cpp | 17 ++-- 7 files changed, 179 insertions(+), 123 deletions(-) create mode 100644 libsolidity/formal/Sorts.cpp create mode 100644 libsolidity/formal/Sorts.h diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index bb54c836f..ba2651e94 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -100,6 +100,8 @@ set(sources formal/SMTPortfolio.cpp formal/SMTPortfolio.h formal/SolverInterface.h + formal/Sorts.cpp + formal/Sorts.h formal/SSAVariable.cpp formal/SSAVariable.h formal/SymbolicTypes.cpp diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index 1e851937c..e10c16d39 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -79,10 +79,9 @@ void CHC::analyze(SourceUnit const& _source) resetSourceAnalysis(); - auto boolSort = make_shared(smt::Kind::Bool); auto genesisSort = make_shared( vector(), - boolSort + smt::SortProvider::boolSort ); m_genesisPredicate = createSymbolicBlock(genesisSort, "genesis"); addRule(genesis(), "genesis"); @@ -131,12 +130,9 @@ bool CHC::visit(ContractDefinition const& _contract) clearIndices(&_contract); - - // TODO create static instances for Bool/Int sorts in SolverInterface. - auto boolSort = make_shared(smt::Kind::Bool); auto errorFunctionSort = make_shared( vector(), - boolSort + smt::SortProvider::boolSort ); string suffix = _contract.name() + "_" + to_string(_contract.id()); @@ -664,29 +660,25 @@ vector CHC::stateSorts(ContractDefinition const& _contract) smt::SortPointer CHC::constructorSort() { - auto boolSort = make_shared(smt::Kind::Bool); - auto intSort = make_shared(smt::Kind::Int); return make_shared( - vector{intSort} + m_stateSorts, - boolSort + vector{smt::SortProvider::intSort} + m_stateSorts, + smt::SortProvider::boolSort ); } smt::SortPointer CHC::interfaceSort() { - auto boolSort = make_shared(smt::Kind::Bool); return make_shared( m_stateSorts, - boolSort + smt::SortProvider::boolSort ); } smt::SortPointer CHC::interfaceSort(ContractDefinition const& _contract) { - auto boolSort = make_shared(smt::Kind::Bool); return make_shared( stateSorts(_contract), - boolSort + smt::SortProvider::boolSort ); } @@ -703,8 +695,6 @@ smt::SortPointer CHC::interfaceSort(ContractDefinition const& _contract) /// - 1 set of output variables smt::SortPointer CHC::sort(FunctionDefinition const& _function) { - auto boolSort = make_shared(smt::Kind::Bool); - auto intSort = make_shared(smt::Kind::Int); vector inputSorts; for (auto const& var: _function.parameters()) inputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); @@ -712,8 +702,8 @@ smt::SortPointer CHC::sort(FunctionDefinition const& _function) for (auto const& var: _function.returnParameters()) outputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); return make_shared( - vector{intSort} + m_stateSorts + inputSorts + m_stateSorts + inputSorts + outputSorts, - boolSort + vector{smt::SortProvider::intSort} + m_stateSorts + inputSorts + m_stateSorts + inputSorts + outputSorts, + smt::SortProvider::boolSort ); } @@ -725,13 +715,12 @@ smt::SortPointer CHC::sort(ASTNode const* _node) auto fSort = dynamic_pointer_cast(sort(*m_currentFunction)); solAssert(fSort, ""); - auto boolSort = make_shared(smt::Kind::Bool); vector varSorts; for (auto const& var: m_currentFunction->localVariables()) varSorts.push_back(smt::smtSortAbstractFunction(*var->type())); return make_shared( fSort->domain + varSorts, - boolSort + smt::SortProvider::boolSort ); } @@ -740,16 +729,14 @@ smt::SortPointer CHC::summarySort(FunctionDefinition const& _function, ContractD auto stateVariables = stateVariablesIncludingInheritedAndPrivate(_contract); auto sorts = stateSorts(_contract); - auto boolSort = make_shared(smt::Kind::Bool); - auto intSort = make_shared(smt::Kind::Int); vector inputSorts, outputSorts; for (auto const& var: _function.parameters()) inputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); for (auto const& var: _function.returnParameters()) outputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); return make_shared( - vector{intSort} + sorts + inputSorts + sorts + outputSorts, - boolSort + vector{smt::SortProvider::intSort} + sorts + inputSorts + sorts + outputSorts, + smt::SortProvider::boolSort ); } diff --git a/libsolidity/formal/EncodingContext.cpp b/libsolidity/formal/EncodingContext.cpp index ff193d2e2..1f7b7a5e0 100644 --- a/libsolidity/formal/EncodingContext.cpp +++ b/libsolidity/formal/EncodingContext.cpp @@ -28,8 +28,8 @@ EncodingContext::EncodingContext(): m_thisAddress(make_unique("this", *this)) { auto sort = make_shared( - make_shared(Kind::Int), - make_shared(Kind::Int) + SortProvider::intSort, + SortProvider::intSort ); m_balances = make_unique(sort, "balances", *this); } diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h index 512be65bb..106ada820 100644 --- a/libsolidity/formal/SolverInterface.h +++ b/libsolidity/formal/SolverInterface.h @@ -17,6 +17,8 @@ #pragma once +#include + #include #include #include @@ -52,94 +54,6 @@ enum class CheckResult SATISFIABLE, UNSATISFIABLE, UNKNOWN, CONFLICTING, ERROR }; -enum class Kind -{ - Int, - Bool, - Function, - Array, - Sort -}; - -struct Sort -{ - Sort(Kind _kind): - kind(_kind) {} - virtual ~Sort() = default; - virtual bool operator==(Sort const& _other) const { return kind == _other.kind; } - - Kind const kind; -}; -using SortPointer = std::shared_ptr; - -struct FunctionSort: public Sort -{ - FunctionSort(std::vector _domain, SortPointer _codomain): - Sort(Kind::Function), domain(std::move(_domain)), codomain(std::move(_codomain)) {} - bool operator==(Sort const& _other) const override - { - if (!Sort::operator==(_other)) - return false; - auto _otherFunction = dynamic_cast(&_other); - solAssert(_otherFunction, ""); - if (domain.size() != _otherFunction->domain.size()) - return false; - if (!std::equal( - domain.begin(), - domain.end(), - _otherFunction->domain.begin(), - [&](SortPointer _a, SortPointer _b) { return *_a == *_b; } - )) - return false; - solAssert(codomain, ""); - solAssert(_otherFunction->codomain, ""); - return *codomain == *_otherFunction->codomain; - } - - std::vector domain; - SortPointer codomain; -}; - -struct ArraySort: public Sort -{ - /// _domain is the sort of the indices - /// _range is the sort of the values - ArraySort(SortPointer _domain, SortPointer _range): - Sort(Kind::Array), domain(std::move(_domain)), range(std::move(_range)) {} - bool operator==(Sort const& _other) const override - { - if (!Sort::operator==(_other)) - return false; - auto _otherArray = dynamic_cast(&_other); - solAssert(_otherArray, ""); - solAssert(_otherArray->domain, ""); - solAssert(_otherArray->range, ""); - solAssert(domain, ""); - solAssert(range, ""); - return *domain == *_otherArray->domain && *range == *_otherArray->range; - } - - SortPointer domain; - SortPointer range; -}; - -struct SortSort: public Sort -{ - SortSort(SortPointer _inner): Sort(Kind::Sort), inner(std::move(_inner)) {} - bool operator==(Sort const& _other) const override - { - if (!Sort::operator==(_other)) - return false; - auto _otherSort = dynamic_cast(&_other); - solAssert(_otherSort, ""); - solAssert(_otherSort->inner, ""); - solAssert(inner, ""); - return *inner == *_otherSort->inner; - } - - SortPointer inner; -}; - // Forward declaration. SortPointer smtSort(Type const& _type); diff --git a/libsolidity/formal/Sorts.cpp b/libsolidity/formal/Sorts.cpp new file mode 100644 index 000000000..195048e56 --- /dev/null +++ b/libsolidity/formal/Sorts.cpp @@ -0,0 +1,29 @@ +/* + 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 . +*/ + + +#include + +using namespace std; + +namespace solidity::frontend::smt +{ + +shared_ptr const SortProvider::boolSort{make_shared(Kind::Bool)}; +shared_ptr const SortProvider::intSort{make_shared(Kind::Int)}; + +} diff --git a/libsolidity/formal/Sorts.h b/libsolidity/formal/Sorts.h new file mode 100644 index 000000000..d26644462 --- /dev/null +++ b/libsolidity/formal/Sorts.h @@ -0,0 +1,125 @@ +/* + 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 . +*/ + +#pragma once + +#include +#include +#include + +#include +#include + +namespace solidity::frontend::smt +{ + +enum class Kind +{ + Int, + Bool, + Function, + Array, + Sort +}; + +struct Sort +{ + Sort(Kind _kind): + kind(_kind) {} + virtual ~Sort() = default; + virtual bool operator==(Sort const& _other) const { return kind == _other.kind; } + + Kind const kind; +}; +using SortPointer = std::shared_ptr; + +struct FunctionSort: public Sort +{ + FunctionSort(std::vector _domain, SortPointer _codomain): + Sort(Kind::Function), domain(std::move(_domain)), codomain(std::move(_codomain)) {} + bool operator==(Sort const& _other) const override + { + if (!Sort::operator==(_other)) + return false; + auto _otherFunction = dynamic_cast(&_other); + solAssert(_otherFunction, ""); + if (domain.size() != _otherFunction->domain.size()) + return false; + if (!std::equal( + domain.begin(), + domain.end(), + _otherFunction->domain.begin(), + [&](SortPointer _a, SortPointer _b) { return *_a == *_b; } + )) + return false; + solAssert(codomain, ""); + solAssert(_otherFunction->codomain, ""); + return *codomain == *_otherFunction->codomain; + } + + std::vector domain; + SortPointer codomain; +}; + +struct ArraySort: public Sort +{ + /// _domain is the sort of the indices + /// _range is the sort of the values + ArraySort(SortPointer _domain, SortPointer _range): + Sort(Kind::Array), domain(std::move(_domain)), range(std::move(_range)) {} + bool operator==(Sort const& _other) const override + { + if (!Sort::operator==(_other)) + return false; + auto _otherArray = dynamic_cast(&_other); + solAssert(_otherArray, ""); + solAssert(_otherArray->domain, ""); + solAssert(_otherArray->range, ""); + solAssert(domain, ""); + solAssert(range, ""); + return *domain == *_otherArray->domain && *range == *_otherArray->range; + } + + SortPointer domain; + SortPointer range; +}; + +struct SortSort: public Sort +{ + SortSort(SortPointer _inner): Sort(Kind::Sort), inner(std::move(_inner)) {} + bool operator==(Sort const& _other) const override + { + if (!Sort::operator==(_other)) + return false; + auto _otherSort = dynamic_cast(&_other); + solAssert(_otherSort, ""); + solAssert(_otherSort->inner, ""); + solAssert(inner, ""); + return *inner == *_otherSort->inner; + } + + SortPointer inner; +}; + +/** Frequently used sorts.*/ +struct SortProvider +{ + static std::shared_ptr const boolSort; + static std::shared_ptr const intSort; +}; + +} diff --git a/libsolidity/formal/SymbolicTypes.cpp b/libsolidity/formal/SymbolicTypes.cpp index a2475b337..1d9655a9c 100644 --- a/libsolidity/formal/SymbolicTypes.cpp +++ b/libsolidity/formal/SymbolicTypes.cpp @@ -32,9 +32,9 @@ SortPointer smtSort(frontend::Type const& _type) switch (smtKind(_type.category())) { case Kind::Int: - return make_shared(Kind::Int); + return SortProvider::intSort; case Kind::Bool: - return make_shared(Kind::Bool); + return SortProvider::boolSort; case Kind::Function: { auto fType = dynamic_cast(&_type); @@ -45,10 +45,10 @@ SortPointer smtSort(frontend::Type const& _type) // TODO change this when we support tuples. if (returnTypes.size() == 0) // We cannot declare functions without a return sort, so we use the smallest. - returnSort = make_shared(Kind::Bool); + returnSort = SortProvider::boolSort; else if (returnTypes.size() > 1) // Abstract sort. - returnSort = make_shared(Kind::Int); + returnSort = SortProvider::intSort; else returnSort = smtSort(*returnTypes.front()); return make_shared(parameterSorts, returnSort); @@ -65,20 +65,19 @@ SortPointer smtSort(frontend::Type const& _type) { auto stringLitType = dynamic_cast(&_type); solAssert(stringLitType, ""); - auto intSort = make_shared(Kind::Int); - return make_shared(intSort, intSort); + return make_shared(SortProvider::intSort, SortProvider::intSort); } else { solAssert(isArray(_type.category()), ""); auto arrayType = dynamic_cast(&_type); solAssert(arrayType, ""); - return make_shared(make_shared(Kind::Int), smtSortAbstractFunction(*arrayType->baseType())); + return make_shared(SortProvider::intSort, smtSortAbstractFunction(*arrayType->baseType())); } } default: // Abstract case. - return make_shared(Kind::Int); + return SortProvider::intSort; } } @@ -93,7 +92,7 @@ vector smtSort(vector const& _types) SortPointer smtSortAbstractFunction(frontend::Type const& _type) { if (isFunction(_type.category())) - return make_shared(Kind::Int); + return SortProvider::intSort; return smtSort(_type); } From 67e9776418e155a663dd5076d41829249f5b64f2 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Fri, 20 Mar 2020 18:32:30 -0500 Subject: [PATCH 32/76] Extract 45 tests from SolidityEndToEndTest.cpp --- test/libsolidity/SolidityEndToEndTest.cpp | 1268 ----------------- .../extracted/abi_decode_calldata.sol | 11 + .../extracted/abi_decode_simple.sol | 7 + .../extracted/abi_decode_simple_storage.sol | 10 + .../extracted/abi_encode_empty_string.sol | 11 + .../arrays_complex_from_and_to_storage.sol | 18 + .../arrays_complex_memory_index_access.sol | 13 + ...signment_to_const_var_involving_keccak.sol | 9 + .../bitwise_shifting_constantinople.sol | 34 + ...twise_shifting_constantinople_combined.sol | 122 ++ ...wise_shifting_constants_constantinople.sol | 83 ++ .../extracted/bool_conversion.sol | 23 + .../extracted/bool_conversion_v2.sol | 24 + .../extracted/bytes_memory_index_access.sol | 13 + .../calldata_array_dynamic_bytes.sol | 74 + .../calldata_bytes_array_to_memory.sol | 18 + .../extracted/calldata_string_array.sol | 15 + .../extracted/calldata_struct_cleaning.sol | 22 + .../extracted/cleanup_address_types.sol | 17 + .../extracted/cleanup_address_types_v2.sol | 18 + .../extracted/cleanup_bytes_types.sol | 13 + .../extracted/cleanup_bytes_types_v2.sol | 14 + .../extracted/delegatecall_return_value.sol | 37 + ...elegatecall_return_value_pre_byzantium.sol | 39 + .../extracted/fixed_arrays_in_storage.sol | 45 + .../extracted/fixed_bytes_index_access.sol | 17 + .../extracted/function_types_sig.sol | 26 + .../iterated_keccak256_with_bytes.sol | 12 + .../keccak256_multiple_arguments.sol | 7 + ...ltiple_arguments_with_numeric_literals.sol | 7 + ...ultiple_arguments_with_string_literals.sol | 12 + ...mory_arrays_dynamic_index_access_write.sol | 19 + .../memory_arrays_index_access_write.sol | 14 + .../extracted/memory_structs_nested_load.sol | 69 + .../semanticTests/extracted/msg_sig.sol | 9 + .../msg_sig_after_internal_call_is_same.sol | 13 + .../extracted/multi_modifiers.sol | 23 + .../extracted/reusing_memory.sol | 26 + .../extracted/shift_right_garbled.sol | 14 + .../extracted/shift_right_garbled_signed.sol | 30 + .../shift_right_garbled_signed_v2.sol | 31 + .../extracted/shift_right_garbled_v2.sol | 15 + ...right_negative_lvalue_signextend_int16.sol | 13 + ...ht_negative_lvalue_signextend_int16_v2.sol | 14 + ...right_negative_lvalue_signextend_int32.sol | 13 + ...ht_negative_lvalue_signextend_int32_v2.sol | 14 + ..._right_negative_lvalue_signextend_int8.sol | 13 + ...ght_negative_lvalue_signextend_int8_v2.sol | 14 + .../staticcall_for_view_and_pure.sol | 39 + ...iccall_for_view_and_pure_pre_byzantium.sol | 39 + .../extracted/string_allocation_bug.sol | 20 + .../extracted/string_bytes_conversion.sol | 17 + .../extracted/strings_in_struct.sol | 35 + .../extracted/struct_constructor_nested.sol | 30 + 54 files changed, 1295 insertions(+), 1268 deletions(-) create mode 100644 test/libsolidity/semanticTests/extracted/abi_decode_calldata.sol create mode 100644 test/libsolidity/semanticTests/extracted/abi_decode_simple.sol create mode 100644 test/libsolidity/semanticTests/extracted/abi_decode_simple_storage.sol create mode 100644 test/libsolidity/semanticTests/extracted/abi_encode_empty_string.sol create mode 100644 test/libsolidity/semanticTests/extracted/arrays_complex_from_and_to_storage.sol create mode 100644 test/libsolidity/semanticTests/extracted/arrays_complex_memory_index_access.sol create mode 100644 test/libsolidity/semanticTests/extracted/assignment_to_const_var_involving_keccak.sol create mode 100644 test/libsolidity/semanticTests/extracted/bitwise_shifting_constantinople.sol create mode 100644 test/libsolidity/semanticTests/extracted/bitwise_shifting_constantinople_combined.sol create mode 100644 test/libsolidity/semanticTests/extracted/bitwise_shifting_constants_constantinople.sol create mode 100644 test/libsolidity/semanticTests/extracted/bool_conversion.sol create mode 100644 test/libsolidity/semanticTests/extracted/bool_conversion_v2.sol create mode 100644 test/libsolidity/semanticTests/extracted/bytes_memory_index_access.sol create mode 100644 test/libsolidity/semanticTests/extracted/calldata_array_dynamic_bytes.sol create mode 100644 test/libsolidity/semanticTests/extracted/calldata_bytes_array_to_memory.sol create mode 100644 test/libsolidity/semanticTests/extracted/calldata_string_array.sol create mode 100644 test/libsolidity/semanticTests/extracted/calldata_struct_cleaning.sol create mode 100644 test/libsolidity/semanticTests/extracted/cleanup_address_types.sol create mode 100644 test/libsolidity/semanticTests/extracted/cleanup_address_types_v2.sol create mode 100644 test/libsolidity/semanticTests/extracted/cleanup_bytes_types.sol create mode 100644 test/libsolidity/semanticTests/extracted/cleanup_bytes_types_v2.sol create mode 100644 test/libsolidity/semanticTests/extracted/delegatecall_return_value.sol create mode 100644 test/libsolidity/semanticTests/extracted/delegatecall_return_value_pre_byzantium.sol create mode 100644 test/libsolidity/semanticTests/extracted/fixed_arrays_in_storage.sol create mode 100644 test/libsolidity/semanticTests/extracted/fixed_bytes_index_access.sol create mode 100644 test/libsolidity/semanticTests/extracted/function_types_sig.sol create mode 100644 test/libsolidity/semanticTests/extracted/iterated_keccak256_with_bytes.sol create mode 100644 test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments.sol create mode 100644 test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments_with_numeric_literals.sol create mode 100644 test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments_with_string_literals.sol create mode 100644 test/libsolidity/semanticTests/extracted/memory_arrays_dynamic_index_access_write.sol create mode 100644 test/libsolidity/semanticTests/extracted/memory_arrays_index_access_write.sol create mode 100644 test/libsolidity/semanticTests/extracted/memory_structs_nested_load.sol create mode 100644 test/libsolidity/semanticTests/extracted/msg_sig.sol create mode 100644 test/libsolidity/semanticTests/extracted/msg_sig_after_internal_call_is_same.sol create mode 100644 test/libsolidity/semanticTests/extracted/multi_modifiers.sol create mode 100644 test/libsolidity/semanticTests/extracted/reusing_memory.sol create mode 100644 test/libsolidity/semanticTests/extracted/shift_right_garbled.sol create mode 100644 test/libsolidity/semanticTests/extracted/shift_right_garbled_signed.sol create mode 100644 test/libsolidity/semanticTests/extracted/shift_right_garbled_signed_v2.sol create mode 100644 test/libsolidity/semanticTests/extracted/shift_right_garbled_v2.sol create mode 100644 test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int16.sol create mode 100644 test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int16_v2.sol create mode 100644 test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int32.sol create mode 100644 test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int32_v2.sol create mode 100644 test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int8.sol create mode 100644 test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int8_v2.sol create mode 100644 test/libsolidity/semanticTests/extracted/staticcall_for_view_and_pure.sol create mode 100644 test/libsolidity/semanticTests/extracted/staticcall_for_view_and_pure_pre_byzantium.sol create mode 100644 test/libsolidity/semanticTests/extracted/string_allocation_bug.sol create mode 100644 test/libsolidity/semanticTests/extracted/string_bytes_conversion.sol create mode 100644 test/libsolidity/semanticTests/extracted/strings_in_struct.sol create mode 100644 test/libsolidity/semanticTests/extracted/struct_constructor_nested.sol diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index ae39bec0e..69e645494 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -1024,39 +1024,6 @@ BOOST_AUTO_TEST_CASE(blockchain) ABI_CHECK(callContractFunctionWithValue("someInfo()", 28), encodeArgs(28, u256("0x1212121212121212121212121212121212121212"), 7)); } -BOOST_AUTO_TEST_CASE(msg_sig) -{ - char const* sourceCode = R"( - contract test { - function foo(uint256 a) public returns (bytes4 value) { - return msg.sig; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("foo(uint256)", 0), encodeArgs(asString(FixedHash<4>(util::keccak256("foo(uint256)")).asBytes()))); - ) -} - -BOOST_AUTO_TEST_CASE(msg_sig_after_internal_call_is_same) -{ - char const* sourceCode = R"( - contract test { - function boo() public returns (bytes4 value) { - return msg.sig; - } - function foo(uint256 a) public returns (bytes4 value) { - return boo(); - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("foo(uint256)", 0), encodeArgs(asString(FixedHash<4>(util::keccak256("foo(uint256)")).asBytes()))); - ) -} - BOOST_AUTO_TEST_CASE(now) { char const* sourceCode = R"( @@ -2403,96 +2370,6 @@ BOOST_AUTO_TEST_CASE(empty_name_input_parameter_with_named_one) ) } -BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments) -{ - char const* sourceCode = R"( - contract c { - function foo(uint a, uint b, uint c) public returns (bytes32 d) - { - d = keccak256(abi.encodePacked(a, b, c)); - } - } - )"; - compileAndRun(sourceCode); - - ABI_CHECK(callContractFunction("foo(uint256,uint256,uint256)", 10, 12, 13), encodeArgs( - util::keccak256( - toBigEndian(u256(10)) + - toBigEndian(u256(12)) + - toBigEndian(u256(13)) - ) - )); -} - -BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments_with_numeric_literals) -{ - char const* sourceCode = R"( - contract c { - function foo(uint a, uint16 b) public returns (bytes32 d) - { - d = keccak256(abi.encodePacked(a, b, uint8(145))); - } - } - )"; - compileAndRun(sourceCode); - - ABI_CHECK(callContractFunction("foo(uint256,uint16)", 10, 12), encodeArgs( - util::keccak256( - toBigEndian(u256(10)) + - bytes{0x0, 0xc} + - bytes(1, 0x91) - ) - )); -} - -BOOST_AUTO_TEST_CASE(keccak256_multiple_arguments_with_string_literals) -{ - char const* sourceCode = R"( - contract c { - function foo() public returns (bytes32 d) - { - d = keccak256("foo"); - } - function bar(uint a, uint16 b) public returns (bytes32 d) - { - d = keccak256(abi.encodePacked(a, b, uint8(145), "foo")); - } - } - )"; - compileAndRun(sourceCode); - - ABI_CHECK(callContractFunction("foo()"), encodeArgs(util::keccak256("foo"))); - - ABI_CHECK(callContractFunction("bar(uint256,uint16)", 10, 12), encodeArgs( - util::keccak256( - toBigEndian(u256(10)) + - bytes{0x0, 0xc} + - bytes(1, 0x91) + - bytes{0x66, 0x6f, 0x6f} - ) - )); -} - -BOOST_AUTO_TEST_CASE(iterated_keccak256_with_bytes) -{ - char const* sourceCode = R"ABC( - contract c { - bytes data; - function foo() public returns (bytes32) - { - data.push("x"); - data.push("y"); - data.push("z"); - return keccak256(abi.encodePacked("b", keccak256(data), "a")); - } - } - )ABC"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("foo()"), encodeArgs( - u256(util::keccak256(bytes{'b'} + util::keccak256("xyz").asBytes() + bytes{'a'})) - )); -} - BOOST_AUTO_TEST_CASE(generic_call) { char const* sourceCode = R"**( @@ -3095,33 +2972,6 @@ BOOST_AUTO_TEST_CASE(bytes_in_arguments) ); } -BOOST_AUTO_TEST_CASE(fixed_arrays_in_storage) -{ - char const* sourceCode = R"( - contract c { - struct Data { uint x; uint y; } - Data[2**10] data; - uint[2**10 + 3] ids; - function setIDStatic(uint id) public { ids[2] = id; } - function setID(uint index, uint id) public { ids[index] = id; } - function setData(uint index, uint x, uint y) public { data[index].x = x; data[index].y = y; } - function getID(uint index) public returns (uint) { return ids[index]; } - function getData(uint index) public returns (uint x, uint y) { x = data[index].x; y = data[index].y; } - function getLengths() public returns (uint l1, uint l2) { l1 = data.length; l2 = ids.length; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("setIDStatic(uint256)", 11), bytes()); - ABI_CHECK(callContractFunction("getID(uint256)", 2), encodeArgs(11)); - ABI_CHECK(callContractFunction("setID(uint256,uint256)", 7, 8), bytes()); - ABI_CHECK(callContractFunction("getID(uint256)", 7), encodeArgs(8)); - ABI_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 7, 8, 9), bytes()); - ABI_CHECK(callContractFunction("setData(uint256,uint256,uint256)", 8, 10, 11), bytes()); - ABI_CHECK(callContractFunction("getData(uint256)", 7), encodeArgs(8, 9)); - ABI_CHECK(callContractFunction("getData(uint256)", 8), encodeArgs(10, 11)); - ABI_CHECK(callContractFunction("getLengths()"), encodeArgs(u256(1) << 10, (u256(1) << 10) + 3)); -} - BOOST_AUTO_TEST_CASE(fixed_array_cleanup) { char const* sourceCode = R"( @@ -3662,19 +3512,6 @@ BOOST_AUTO_TEST_CASE(array_copy_including_mapping) BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(assignment_to_const_var_involving_keccak) -{ - char const* sourceCode = R"( - contract C { - bytes32 constant x = keccak256("abc"); - function f() public returns (bytes32) { return x; } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f()"), encodeArgs(util::keccak256("abc"))); -} - -// Disabled until https://github.com/ethereum/solidity/issues/715 is implemented //BOOST_AUTO_TEST_CASE(assignment_to_const_array_vars) //{ // char const* sourceCode = R"( @@ -3733,35 +3570,6 @@ BOOST_AUTO_TEST_CASE(packed_storage_structs_delete) BOOST_CHECK(storageEmpty(m_contractAddress)); } -BOOST_AUTO_TEST_CASE(bool_conversion) -{ - char const* sourceCode = R"( - contract C { - function f(bool _b) public returns(uint) { - if (_b) - return 1; - else - return 0; - } - function g(bool _in) public returns (bool _out) { - _out = _in; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(bool)", 0), encodeArgs(0)); - ABI_CHECK(callContractFunction("f(bool)", 1), encodeArgs(1)); - ABI_CHECK(callContractFunction("f(bool)", 2), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("f(bool)", 3), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("f(bool)", 255), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("g(bool)", 0), encodeArgs(0)); - ABI_CHECK(callContractFunction("g(bool)", 1), encodeArgs(1)); - ABI_CHECK(callContractFunction("g(bool)", 2), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("g(bool)", 3), v2 ? encodeArgs() : encodeArgs(1)); - ABI_CHECK(callContractFunction("g(bool)", 255), v2 ? encodeArgs() : encodeArgs(1)); -} - BOOST_AUTO_TEST_CASE(invalid_enum_logged) { char const* sourceCode = R"( @@ -3837,32 +3645,6 @@ BOOST_AUTO_TEST_CASE(failing_send) BOOST_REQUIRE(callContractFunction("callHelper(address)", c_helperAddress) == encodeArgs(true, 20)); } -BOOST_AUTO_TEST_CASE(reusing_memory) -{ - // Invoke some features that use memory and test that they do not interfere with each other. - char const* sourceCode = R"( - contract Helper { - uint public flag; - constructor(uint x) public { - flag = x; - } - } - contract Main { - mapping(uint => uint) map; - function f(uint x) public returns (uint) { - map[x] = x; - return (new Helper(uint(keccak256(abi.encodePacked(this.g(map[x])))))).flag(); - } - function g(uint a) public returns (uint) - { - return map[a]; - } - } - )"; - compileAndRun(sourceCode, 0, "Main"); - BOOST_REQUIRE(callContractFunction("f(uint256)", 0x34) == encodeArgs(util::keccak256(util::toBigEndian(u256(0x34))))); -} - BOOST_AUTO_TEST_CASE(return_string) { char const* sourceCode = R"( @@ -4195,83 +3977,6 @@ BOOST_AUTO_TEST_CASE(arrays_from_and_to_storage) ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0x20), u256(data.size()), data)); } -BOOST_AUTO_TEST_CASE(arrays_complex_from_and_to_storage) -{ - char const* sourceCode = R"( - contract Test { - uint24[3][] public data; - function set(uint24[3][] memory _data) public returns (uint) { - data = _data; - return data.length; - } - function get() public returns (uint24[3][] memory) { - return data; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; - BOOST_REQUIRE( - callContractFunction("set(uint24[3][])", u256(0x20), u256(data.size() / 3), data) == - encodeArgs(u256(data.size() / 3)) - ); - ABI_CHECK(callContractFunction("data(uint256,uint256)", u256(2), u256(2)), encodeArgs(u256(9))); - ABI_CHECK(callContractFunction("data(uint256,uint256)", u256(5), u256(1)), encodeArgs(u256(17))); - ABI_CHECK(callContractFunction("data(uint256,uint256)", u256(6), u256(0)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0x20), u256(data.size() / 3), data)); -} - -BOOST_AUTO_TEST_CASE(arrays_complex_memory_index_access) -{ - char const* sourceCode = R"( - contract Test { - function set(uint24[3][] memory _data, uint a, uint b) public returns (uint l, uint e) { - l = _data.length; - e = _data[a][b]; - } - } - )"; - vector data{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}; - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Test"); - - BOOST_REQUIRE(callContractFunction( - "set(uint24[3][],uint256,uint256)", - u256(0x60), - u256(3), - u256(2), - u256(data.size() / 3), - data - ) == encodeArgs(u256(data.size() / 3), u256(data[3 * 3 + 2]))); - ); -} - -BOOST_AUTO_TEST_CASE(bytes_memory_index_access) -{ - char const* sourceCode = R"( - contract Test { - function set(bytes memory _data, uint i) public returns (uint l, byte c) { - l = _data.length; - c = _data[i]; - } - } - )"; - string data("abcdefgh"); - - ALSO_VIA_YUL( - compileAndRun(sourceCode, 0, "Test"); - - BOOST_REQUIRE(callContractFunction( - "set(bytes,uint256)", - u256(0x40), - u256(3), - u256(data.size()), - data - ) == encodeArgs(u256(data.size()), string("d"))); - ); -} - BOOST_AUTO_TEST_CASE(memory_types_initialisation) { char const* sourceCode = R"( @@ -4323,128 +4028,6 @@ BOOST_AUTO_TEST_CASE(memory_arrays_delete) ABI_CHECK(callContractFunction("del()"), encodeArgs(data)); } -BOOST_AUTO_TEST_CASE(memory_arrays_index_access_write) -{ - char const* sourceCode = R"( - contract Test { - function set(uint24[3][4] memory x) public { - x[2][2] = 1; - x[3][2] = 7; - } - function f() public returns (uint24[3][4] memory){ - uint24[3][4] memory data; - set(data); - return data; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data(3 * 4); - data[3 * 2 + 2] = 1; - data[3 * 3 + 2] = 7; - ABI_CHECK(callContractFunction("f()"), encodeArgs(data)); -} - -BOOST_AUTO_TEST_CASE(memory_arrays_dynamic_index_access_write) -{ - char const* sourceCode = R"( - contract Test { - uint24[3][][4] data; - function set(uint24[3][][4] memory x) internal returns (uint24[3][][4] memory) { - x[1][2][2] = 1; - x[1][3][2] = 7; - return x; - } - function f() public returns (uint24[3][] memory) { - while (data[1].length < 4) - data[1].push(); - return set(data)[1]; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - vector data(3 * 4); - data[3 * 2 + 2] = 1; - data[3 * 3 + 2] = 7; - ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(0x20), u256(4), data)); -} - -BOOST_AUTO_TEST_CASE(memory_structs_nested_load) -{ - char const* sourceCode = R"( - contract Test { - struct S { uint8 x; uint16 y; uint z; } - struct X { uint8 x; S s; uint8[2] a; } - X m_x; - function load() public returns (uint a, uint x, uint y, uint z, uint a1, uint a2) { - m_x.x = 1; - m_x.s.x = 2; - m_x.s.y = 3; - m_x.s.z = 4; - m_x.a[0] = 5; - m_x.a[1] = 6; - X memory d = m_x; - a = d.x; - x = d.s.x; - y = d.s.y; - z = d.s.z; - a1 = d.a[0]; - a2 = d.a[1]; - } - function store() public returns (uint a, uint x, uint y, uint z, uint a1, uint a2) { - X memory d; - d.x = 1; - d.s.x = 2; - d.s.y = 3; - d.s.z = 4; - d.a[0] = 5; - d.a[1] = 6; - m_x = d; - a = m_x.x; - x = m_x.s.x; - y = m_x.s.y; - z = m_x.s.z; - a1 = m_x.a[0]; - a2 = m_x.a[1]; - } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - - auto out = encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5), u256(6)); - ABI_CHECK(callContractFunction("load()"), out); - ABI_CHECK(callContractFunction("store()"), out); -} - -BOOST_AUTO_TEST_CASE(struct_constructor_nested) -{ - char const* sourceCode = R"( - contract C { - struct X { uint x1; uint x2; } - struct S { uint s1; uint[3] s2; X s3; } - S s; - constructor() public { - uint[3] memory s2; - s2[1] = 9; - s = S(1, s2, X(4, 5)); - } - function get() public returns (uint s1, uint[3] memory s2, uint x1, uint x2) - { - s1 = s.s1; - s2 = s.s2; - x1 = s.s3.x1; - x2 = s.s3.x2; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - auto out = encodeArgs(u256(1), u256(0), u256(9), u256(0), u256(4), u256(5)); - ABI_CHECK(callContractFunction("get()"), out); -} - BOOST_AUTO_TEST_CASE(calldata_struct_short) { char const* sourceCode = R"( @@ -4468,31 +4051,6 @@ BOOST_AUTO_TEST_CASE(calldata_struct_short) ABI_CHECK(callContractFunctionNoEncoding("f((uint256,uint256))", bytes()), encodeArgs()); } -BOOST_AUTO_TEST_CASE(calldata_struct_cleaning) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - struct S { uint8 a; bytes1 b; } - function f(S calldata s) external pure returns (uint256 a, bytes32 b) { - uint8 tmp1 = s.a; - bytes1 tmp2 = s.b; - assembly { - a := tmp1 - b := tmp2 - } - - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - // double check that the valid case goes through - ABI_CHECK(callContractFunction("f((uint8,bytes1))", u256(0x12), bytes{0x34} + bytes(31,0)), encodeArgs(0x12, bytes{0x34} + bytes(31,0))); - ABI_CHECK(callContractFunction("f((uint8,bytes1))", u256(0x1234), bytes{0x56, 0x78} + bytes(30,0)), encodeArgs()); - ABI_CHECK(callContractFunction("f((uint8,bytes1))", u256(-1), u256(-1)), encodeArgs()); -} - BOOST_AUTO_TEST_CASE(calldata_struct_function_type) { char const* sourceCode = R"( @@ -4518,92 +4076,6 @@ BOOST_AUTO_TEST_CASE(calldata_struct_function_type) ABI_CHECK(callContractFunctionNoEncoding("f((function))", fn_C_h), encodeArgs(23)); } -BOOST_AUTO_TEST_CASE(calldata_array_dynamic_bytes) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f1(bytes[1] calldata a) external returns (uint256, uint256, uint256, uint256) { - return (a[0].length, uint8(a[0][0]), uint8(a[0][1]), uint8(a[0][2])); - } - function f2(bytes[1] calldata a, bytes[1] calldata b) external returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256) { - return (a[0].length, uint8(a[0][0]), uint8(a[0][1]), uint8(a[0][2]), b[0].length, uint8(b[0][0]), uint8(b[0][1])); - } - function g1(bytes[2] calldata a) external returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256) { - return (a[0].length, uint8(a[0][0]), uint8(a[0][1]), uint8(a[0][2]), a[1].length, uint8(a[1][0]), uint8(a[1][1]), uint8(a[1][2])); - } - function g2(bytes[] calldata a) external returns (uint256[8] memory) { - return [a.length, a[0].length, uint8(a[0][0]), uint8(a[0][1]), a[1].length, uint8(a[1][0]), uint8(a[1][1]), uint8(a[1][2])]; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - bytes bytes010203 = bytes{1,2,3}+bytes(29,0); - bytes bytes040506 = bytes{4,5,6}+bytes(29,0); - bytes bytes0102 = bytes{1,2}+bytes(30,0); - ABI_CHECK( - callContractFunction("f1(bytes[1])", 0x20, 0x20, 3, bytes010203), - encodeArgs(3, 1, 2, 3) - ); - ABI_CHECK( - callContractFunction("f2(bytes[1],bytes[1])", 0x40, 0xA0, 0x20, 3, bytes010203, 0x20, 2, bytes0102), - encodeArgs(3, 1, 2, 3, 2, 1, 2) - ); - ABI_CHECK( - callContractFunction("g1(bytes[2])", 0x20, 0x40, 0x80, 3, bytes010203, 3, bytes040506), - encodeArgs(3, 1, 2, 3, 3, 4, 5, 6) - ); - // same offset for both arrays - ABI_CHECK( - callContractFunction("g1(bytes[2])", 0x20, 0x40, 0x40, 3, bytes010203), - encodeArgs(3, 1, 2, 3, 3, 1, 2, 3) - ); - ABI_CHECK( - callContractFunction("g2(bytes[])", 0x20, 2, 0x40, 0x80, 2, bytes0102, 3, bytes040506), - encodeArgs(2, 2, 1, 2, 3, 4, 5, 6) - ); -} - -BOOST_AUTO_TEST_CASE(calldata_bytes_array_to_memory) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(bytes[] calldata a) external returns (uint, uint, bytes memory) { - bytes memory m = a[0]; - return (a.length, m.length, m); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs(1, 2, 0x60, 2, bytes{'a','b'} + bytes(30, 0)) - ); - - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 32, bytes(32, 'x')), - encodeArgs(1, 32, 0x60, 32, bytes(32, 'x')) - ); - bytes x_zero_a = bytes{'x'} + bytes(30, 0) + bytes{'a'}; - bytes a_zero_x = bytes{'a'} + bytes(30, 0) + bytes{'x'}; - bytes a_m_x = bytes{'a'} + bytes(30, 'm') + bytes{'x'}; - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 32, x_zero_a), - encodeArgs(1, 32, 0x60, 32, x_zero_a) - ); - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 32, a_zero_x), - encodeArgs(1, 32, 0x60, 32, a_zero_x) - ); - ABI_CHECK( - callContractFunction("f(bytes[])", 0x20, 1, 0x20, 32, a_m_x), - encodeArgs(1, 32, 0x60, 32, a_m_x) - ); -} - BOOST_AUTO_TEST_CASE(calldata_bytes_array_bounds) { char const* sourceCode = R"( @@ -4630,26 +4102,6 @@ BOOST_AUTO_TEST_CASE(calldata_bytes_array_bounds) ); } -BOOST_AUTO_TEST_CASE(calldata_string_array) -{ - char const* sourceCode = R"( - pragma experimental ABIEncoderV2; - contract C { - function f(string[] calldata a) external returns (uint, uint, uint, string memory) { - string memory s1 = a[0]; - bytes memory m1 = bytes(s1); - return (a.length, m1.length, uint8(m1[0]), s1); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - ABI_CHECK( - callContractFunction("f(string[])", 0x20, 1, 0x20, 2, bytes{'a', 'b'} + bytes(30, 0)), - encodeArgs(1, 2, 'a', 0x80, 2, bytes{'a', 'b'} + bytes(30, 0)) - ); -} - BOOST_AUTO_TEST_CASE(calldata_array_two_dimensional) { vector> data { @@ -4835,25 +4287,6 @@ BOOST_AUTO_TEST_CASE(initialise_string_constant) ABI_CHECK(callContractFunction("short()"), encodeDyn(shortStr)); } -BOOST_AUTO_TEST_CASE(string_bytes_conversion) -{ - char const* sourceCode = R"( - contract Test { - string s; - bytes b; - function f(string memory _s, uint n) public returns (byte) { - b = bytes(_s); - s = string(b); - return bytes(s)[n]; - } - function l() public returns (uint) { return bytes(s).length; } - } - )"; - compileAndRun(sourceCode, 0, "Test"); - ABI_CHECK(callContractFunction("f(string,uint256)", u256(0x40), u256(2), u256(6), string("abcdef")), encodeArgs("c")); - ABI_CHECK(callContractFunction("l()"), encodeArgs(u256(6))); -} - BOOST_AUTO_TEST_CASE(string_as_mapping_key) { char const* sourceCode = R"( @@ -5108,48 +4541,6 @@ BOOST_AUTO_TEST_CASE(library_stray_values) ABI_CHECK(callContractFunction("f(uint256)", u256(33)), encodeArgs(u256(42))); } -BOOST_AUTO_TEST_CASE(strings_in_struct) -{ - char const* sourceCode = R"( - contract buggystruct { - Buggy public bug; - - struct Buggy { - uint first; - uint second; - uint third; - string last; - } - - constructor() public { - bug = Buggy(10, 20, 30, "asdfghjkl"); - } - function getFirst() public returns (uint) - { - return bug.first; - } - function getSecond() public returns (uint) - { - return bug.second; - } - function getThird() public returns (uint) - { - return bug.third; - } - function getLast() public returns (string memory) - { - return bug.last; - } - } - )"; - compileAndRun(sourceCode); - string s = "asdfghjkl"; - ABI_CHECK(callContractFunction("getFirst()"), encodeArgs(u256(10))); - ABI_CHECK(callContractFunction("getSecond()"), encodeArgs(u256(20))); - ABI_CHECK(callContractFunction("getThird()"), encodeArgs(u256(30))); - ABI_CHECK(callContractFunction("getLast()"), encodeDyn(s)); -} - BOOST_AUTO_TEST_CASE(internal_types_in_library) { char const* sourceCode = R"( @@ -5641,27 +5032,6 @@ BOOST_AUTO_TEST_CASE(create_memory_array_allocation_size) } } -BOOST_AUTO_TEST_CASE(string_allocation_bug) -{ - char const* sourceCode = R"( - contract Sample - { - struct s { uint16 x; uint16 y; string a; string b;} - s[2] public p; - constructor() public { - s memory m; - m.x = 0xbbbb; - m.y = 0xcccc; - m.a = "hello"; - m.b = "world"; - p[0] = m; - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("p(uint256)", 0), encodeArgs(u256(0xbbbb), u256(0xcccc), u256(0x80), u256(0xc0), u256(5), string("hello"), u256(5), string("world"))); -} - BOOST_AUTO_TEST_CASE(using_for_function_on_int) { char const* sourceCode = R"( @@ -5815,27 +5185,6 @@ BOOST_AUTO_TEST_CASE(inline_long_string_return) ABI_CHECK(callContractFunction("f()"), encodeDyn(strLong)); } -BOOST_AUTO_TEST_CASE(fixed_bytes_index_access) -{ - char const* sourceCode = R"( - contract C { - bytes16[] public data; - function f(bytes32 x) public returns (byte) { - return x[2]; - } - function g(bytes32 x) public returns (uint) { - data = [x[0], x[1], x[2]]; - data[0] = "12345"; - return uint(uint8(data[0][4])); - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f(bytes32)", "789"), encodeArgs("9")); - ABI_CHECK(callContractFunction("g(bytes32)", "789"), encodeArgs(u256(int('5')))); - ABI_CHECK(callContractFunction("data(uint256)", u256(1)), encodeArgs("8")); -} - 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. @@ -5852,47 +5201,6 @@ BOOST_AUTO_TEST_CASE(index_access_with_type_conversion) BOOST_CHECK(callContractFunction("f(uint256)", u256(0x101)).size() == 256 * 32); } -BOOST_AUTO_TEST_CASE(cleanup_bytes_types) -{ - // Checks that bytesXX types are properly cleaned before they are compared. - char const* sourceCode = R"( - contract C { - function f(bytes2 a, uint16 x) public returns (uint) { - if (a != "ab") return 1; - if (x != 0x0102) return 2; - if (bytes3(uint24(x)) != 0x000102) return 3; - return 0; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - // We input longer data on purpose. - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(bytes2,uint16)", string("abc"), u256(0x040102)), v2 ? encodeArgs() : encodeArgs(0)); -} -BOOST_AUTO_TEST_CASE(cleanup_address_types) -{ - // Checks that address types are properly cleaned before they are compared. - char const* sourceCode = R"( - contract C { - function f(address a) public returns (uint) { - if (a != 0x1234567890123456789012345678901234567890) return 1; - return 0; - } - function g(address payable a) public returns (uint) { - if (a != 0x1234567890123456789012345678901234567890) return 1; - return 0; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - // We input longer data on purpose. - ABI_CHECK(callContractFunction("f(address)", u256("0xFFFF1234567890123456789012345678901234567890")), v2 ? encodeArgs() : encodeArgs(0)); - ABI_CHECK(callContractFunction("g(address)", u256("0xFFFF1234567890123456789012345678901234567890")), v2 ? encodeArgs() : encodeArgs(0)); -} - BOOST_AUTO_TEST_CASE(failed_create) { char const* sourceCode = R"( @@ -6172,113 +5480,6 @@ BOOST_AUTO_TEST_CASE(return_external_function_type) // TODO: store bound internal library functions -BOOST_AUTO_TEST_CASE(shift_right_garbled) -{ - char const* sourceCode = R"( - contract C { - function f(uint8 a, uint8 b) public returns (uint) { - assembly { - a := 0xffffffff - } - // Higher bits should be cleared before the shift - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x0), u256(4)), encodeArgs(u256(0xf))); - ABI_CHECK(callContractFunction("f(uint8,uint8)", u256(0x0), u256(0x1004)), v2 ? encodeArgs() : encodeArgs(u256(0xf))); -} - -BOOST_AUTO_TEST_CASE(shift_right_garbled_signed) -{ - char const* sourceCode = R"( - contract C { - function f(int8 a, uint8 b) public returns (int) { - assembly { - a := 0xfffffff0 - } - // Higher bits should be signextended before the shift - return a >> b; - } - function g(int8 a, uint8 b) public returns (int) { - assembly { - a := 0xf0 - } - // Higher bits should be signextended before the shift - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(3)), encodeArgs(u256(-2))); - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(4)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(0xFF)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(0x1003)), v2 ? encodeArgs() : encodeArgs(u256(-2))); - ABI_CHECK(callContractFunction("f(int8,uint8)", u256(0x0), u256(0x1004)), v2 ? encodeArgs() : encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(3)), encodeArgs(u256(-2))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(4)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(0xFF)), encodeArgs(u256(-1))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(0x1003)), v2 ? encodeArgs() : encodeArgs(u256(-2))); - ABI_CHECK(callContractFunction("g(int8,uint8)", u256(0x0), u256(0x1004)), v2 ? encodeArgs() : encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int8) -{ - char const* sourceCode = R"( - contract C { - function f(int8 a, int8 b) public returns (int8) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(0)), v2 ? encodeArgs() : encodeArgs(u256(-103))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(1)), v2 ? encodeArgs() : encodeArgs(u256(-52))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(2)), v2 ? encodeArgs() : encodeArgs(u256(-26))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(4)), v2 ? encodeArgs() : encodeArgs(u256(-7))); - ABI_CHECK(callContractFunction("f(int8,int8)", u256(0x99u), u256(8)), v2 ? encodeArgs() : encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int16) -{ - char const* sourceCode = R"( - contract C { - function f(int16 a, int16 b) public returns (int16) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(0)), v2 ? encodeArgs() : encodeArgs(u256(-103))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(1)), v2 ? encodeArgs() : encodeArgs(u256(-52))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(2)), v2 ? encodeArgs() : encodeArgs(u256(-26))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(4)), v2 ? encodeArgs() : encodeArgs(u256(-7))); - ABI_CHECK(callContractFunction("f(int16,int16)", u256(0xFF99u), u256(8)), v2 ? encodeArgs() : encodeArgs(u256(-1))); -} - -BOOST_AUTO_TEST_CASE(shift_right_negative_lvalue_signextend_int32) -{ - char const* sourceCode = R"( - contract C { - function f(int32 a, int32 b) public returns (int32) { - return a >> b; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - bool v2 = solidity::test::CommonOptions::get().useABIEncoderV2; - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(0)), v2 ? encodeArgs() : encodeArgs(u256(-103))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(1)), v2 ? encodeArgs() : encodeArgs(u256(-52))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(2)), v2 ? encodeArgs() : encodeArgs(u256(-26))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(4)), v2 ? encodeArgs() : encodeArgs(u256(-7))); - ABI_CHECK(callContractFunction("f(int32,int32)", u256(0xFFFFFF99u), u256(8)), v2 ? encodeArgs() : encodeArgs(u256(-1))); -} - BOOST_AUTO_TEST_CASE(shift_bytes) { char const* sourceCode = R"( @@ -6653,33 +5854,6 @@ BOOST_AUTO_TEST_CASE(interface_contract) ABI_CHECK(callContractFunction("f(address)", recipient), encodeArgs(true)); } -BOOST_AUTO_TEST_CASE(multi_modifiers) -{ - // This triggered a bug in some version because the variable in the modifier was not - // unregistered correctly. - char const* sourceCode = R"( - contract C { - uint public x; - modifier m1 { - address a1 = msg.sender; - x++; - _; - } - function f1() m1() public { - x += 7; - } - function f2() m1() public { - x += 3; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f1()"), bytes()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(8))); - ABI_CHECK(callContractFunction("f2()"), bytes()); - ABI_CHECK(callContractFunction("x()"), encodeArgs(u256(12))); -} - BOOST_AUTO_TEST_CASE(bare_call_invalid_address) { char const* sourceCode = R"YY( @@ -6840,110 +6014,6 @@ BOOST_AUTO_TEST_CASE(bare_call_return_data) } } -BOOST_AUTO_TEST_CASE(delegatecall_return_value) -{ - if (solidity::test::CommonOptions::get().evmVersion().supportsReturndata()) - { - char const* sourceCode = R"DELIMITER( - contract C { - uint value; - function set(uint _value) external { - value = _value; - } - function get() external view returns (uint) { - return value; - } - function get_delegated() external returns (bool, bytes memory) { - return address(this).delegatecall(abi.encodeWithSignature("get()")); - } - function assert0() external view { - assert(value == 0); - } - function assert0_delegated() external returns (bool, bytes memory) { - return address(this).delegatecall(abi.encodeWithSignature("assert0()")); - } - } - )DELIMITER"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(1), 0x40, 0x00)); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1), 0x40, 0x20, 0x00)); - ABI_CHECK(callContractFunction("set(uint256)", u256(1)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0), 0x40, 0x00)); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1), 0x40, 0x20, 1)); - ABI_CHECK(callContractFunction("set(uint256)", u256(42)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0), 0x40, 0x00)); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1), 0x40, 0x20, 42)); - } - else - { - char const* sourceCode = R"DELIMITER( - contract C { - uint value; - function set(uint _value) external { - value = _value; - } - function get() external view returns (uint) { - return value; - } - function get_delegated() external returns (bool) { - (bool success,) = address(this).delegatecall(abi.encodeWithSignature("get()")); - return success; - } - function assert0() external view { - assert(value == 0); - } - function assert0_delegated() external returns (bool) { - (bool success,) = address(this).delegatecall(abi.encodeWithSignature("assert0()")); - return success; - } - } - )DELIMITER"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("set(uint256)", u256(1)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); - ABI_CHECK(callContractFunction("set(uint256)", u256(42)), encodeArgs()); - ABI_CHECK(callContractFunction("get()"), encodeArgs(u256(42))); - ABI_CHECK(callContractFunction("assert0_delegated()"), encodeArgs(u256(0))); - ABI_CHECK(callContractFunction("get_delegated()"), encodeArgs(u256(1))); - } -} - -BOOST_AUTO_TEST_CASE(function_types_sig) -{ - char const* sourceCode = R"( - contract C { - uint public x; - function f() public pure returns (bytes4) { - return this.f.selector; - } - function g() public returns (bytes4) { - function () pure external returns (bytes4) fun = this.f; - return fun.selector; - } - function h() public returns (bytes4) { - function () pure external returns (bytes4) fun = this.f; - return fun.selector; - } - function i() public pure returns (bytes4) { - return this.x.selector; - } - } - )"; - compileAndRun(sourceCode, 0, "C"); - ABI_CHECK(callContractFunction("f()"), encodeArgs(asString(FixedHash<4>(util::keccak256("f()")).asBytes()))); - ABI_CHECK(callContractFunction("g()"), encodeArgs(asString(FixedHash<4>(util::keccak256("f()")).asBytes()))); - ABI_CHECK(callContractFunction("h()"), encodeArgs(asString(FixedHash<4>(util::keccak256("f()")).asBytes()))); - ABI_CHECK(callContractFunction("i()"), encodeArgs(asString(FixedHash<4>(util::keccak256("x()")).asBytes()))); -} - BOOST_AUTO_TEST_CASE(abi_encodePacked) { char const* sourceCode = R"( @@ -7488,344 +6558,6 @@ BOOST_AUTO_TEST_CASE(abi_encode_with_signaturev2) ABI_CHECK(callContractFunction("f4()"), expectation); } -BOOST_AUTO_TEST_CASE(abi_encode_empty_string) -{ - char const* sourceCode = R"( - // Tests that this will not end up using a "bytes0" type - // (which would assert) - contract C { - function f() public pure returns (bytes memory, bytes memory) { - return (abi.encode(""), abi.encodePacked("")); - } - } - )"; - - compileAndRun(sourceCode, 0, "C"); - if (!solidity::test::CommonOptions::get().useABIEncoderV2) - { - // ABI Encoder V2 has slightly different padding, tested below. - ABI_CHECK(callContractFunction("f()"), encodeArgs( - 0x40, 0xc0, - 0x60, 0x20, 0x00, 0x00, - 0x00 - )); - } -} - -BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure) -{ - char const* sourceCode = R"( - contract C { - uint x; - function f() public returns (uint) { - x = 3; - return 1; - } - } - interface CView { - function f() view external returns (uint); - } - interface CPure { - function f() pure external returns (uint); - } - contract D { - function f() public returns (uint) { - return (new C()).f(); - } - function fview() public returns (uint) { - return (CView(address(new C()))).f(); - } - function fpure() public returns (uint) { - return (CPure(address(new C()))).f(); - } - } - )"; - compileAndRun(sourceCode, 0, "D"); - // This should work (called via CALL) - ABI_CHECK(callContractFunction("f()"), encodeArgs(1)); - if (solidity::test::CommonOptions::get().evmVersion().hasStaticCall()) - { - // These should throw (called via STATICCALL) - ABI_CHECK(callContractFunction("fview()"), encodeArgs()); - ABI_CHECK(callContractFunction("fpure()"), encodeArgs()); - } - else - { - ABI_CHECK(callContractFunction("fview()"), encodeArgs(1)); - ABI_CHECK(callContractFunction("fpure()"), encodeArgs(1)); - } -} - -BOOST_AUTO_TEST_CASE(bitwise_shifting_constantinople) -{ - if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) - return; - char const* sourceCode = R"( - contract C { - function shl(uint a, uint b) public returns (uint c) { - assembly { - c := shl(b, a) - } - } - function shr(uint a, uint b) public returns (uint c) { - assembly { - c := shr(b, a) - } - } - function sar(uint a, uint b) public returns (uint c) { - assembly { - c := sar(b, a) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256(1), u256(2)) == encodeArgs(u256(4))); - BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe"))); - BOOST_CHECK(callContractFunction("shl(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256(3), u256(1)) == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(255)) == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256(3), u256(1)) == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(1)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(255)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar(uint256,uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), u256(256)) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - ) -} - -BOOST_AUTO_TEST_CASE(bitwise_shifting_constants_constantinople) -{ - if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) - return; - char const* sourceCode = R"( - contract C { - function shl_1() public returns (bool) { - uint c; - assembly { - c := shl(2, 1) - } - assert(c == 4); - return true; - } - function shl_2() public returns (bool) { - uint c; - assembly { - c := shl(1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - } - assert(c == 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe); - return true; - } - function shl_3() public returns (bool) { - uint c; - assembly { - c := shl(256, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - } - assert(c == 0); - return true; - } - function shr_1() public returns (bool) { - uint c; - assembly { - c := shr(1, 3) - } - assert(c == 1); - return true; - } - function shr_2() public returns (bool) { - uint c; - assembly { - c := shr(1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - } - assert(c == 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); - return true; - } - function shr_3() public returns (bool) { - uint c; - assembly { - c := shr(256, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) - } - assert(c == 0); - return true; - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("shl_1()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shl_2()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shl_3()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr_1()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr_2()") == encodeArgs(u256(1))); - BOOST_CHECK(callContractFunction("shr_3()") == encodeArgs(u256(1))); - ) -} - -BOOST_AUTO_TEST_CASE(bitwise_shifting_constantinople_combined) -{ - if (!solidity::test::CommonOptions::get().evmVersion().hasBitwiseShifting()) - return; - char const* sourceCode = R"( - contract C { - function shl_zero(uint a) public returns (uint c) { - assembly { - c := shl(0, a) - } - } - function shr_zero(uint a) public returns (uint c) { - assembly { - c := shr(0, a) - } - } - function sar_zero(uint a) public returns (uint c) { - assembly { - c := sar(0, a) - } - } - - function shl_large(uint a) public returns (uint c) { - assembly { - c := shl(0x110, a) - } - } - function shr_large(uint a) public returns (uint c) { - assembly { - c := shr(0x110, a) - } - } - function sar_large(uint a) public returns (uint c) { - assembly { - c := sar(0x110, a) - } - } - - function shl_combined(uint a) public returns (uint c) { - assembly { - c := shl(4, shl(12, a)) - } - } - function shr_combined(uint a) public returns (uint c) { - assembly { - c := shr(4, shr(12, a)) - } - } - function sar_combined(uint a) public returns (uint c) { - assembly { - c := sar(4, sar(12, a)) - } - } - - function shl_combined_large(uint a) public returns (uint c) { - assembly { - c := shl(0xd0, shl(0x40, a)) - } - } - function shl_combined_overflow(uint a) public returns (uint c) { - assembly { - c := shl(0x01, shl(not(0x00), a)) - } - } - function shr_combined_large(uint a) public returns (uint c) { - assembly { - c := shr(0xd0, shr(0x40, a)) - } - } - function shr_combined_overflow(uint a) public returns (uint c) { - assembly { - c := shr(0x01, shr(not(0x00), a)) - } - } - function sar_combined_large(uint a) public returns (uint c) { - assembly { - c := sar(0xd0, sar(0x40, a)) - } - } - } - )"; - ALSO_VIA_YUL( - compileAndRun(sourceCode); - - BOOST_CHECK(callContractFunction("shl_zero(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_zero(uint256)", u256("0xffff")) == encodeArgs(u256("0xffff"))); - BOOST_CHECK(callContractFunction("shl_zero(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("shr_zero(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_zero(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar_zero(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_zero(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - - BOOST_CHECK(callContractFunction("shl_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_large(uint256)", u256("0xffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_large(uint256)", u256("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - - BOOST_CHECK(callContractFunction("shl_combined(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_combined(uint256)", u256("0xffff")) == encodeArgs(u256("0xffff0000"))); - BOOST_CHECK(callContractFunction("shl_combined(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000"))); - BOOST_CHECK(callContractFunction("shr_combined(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_combined(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar_combined(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_combined(uint256)", u256("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0x00007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - BOOST_CHECK(callContractFunction("sar_combined(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - - BOOST_CHECK(callContractFunction("shl_combined_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_combined_large(uint256)", u256("0xffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_combined_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shl_combined_overflow(uint256)", u256(2)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_combined_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_combined_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("shr_combined_overflow(uint256)", u256(2)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_combined_large(uint256)", u256(0)) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_combined_large(uint256)", u256("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256(0))); - BOOST_CHECK(callContractFunction("sar_combined_large(uint256)", u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) == encodeArgs(u256("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"))); - ) -} - -BOOST_AUTO_TEST_CASE(abi_decode_simple) -{ - char const* sourceCode = R"( - contract C { - function f(bytes memory data) public pure returns (uint, bytes memory) { - return abi.decode(data, (uint, bytes)); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"), encodeArgs(33, 0x40, 7, "abcdefg")); -} - -BOOST_AUTO_TEST_CASE(abi_decode_simple_storage) -{ - char const* sourceCode = R"( - contract C { - bytes data; - function f(bytes memory _data) public returns (uint, bytes memory) { - data = _data; - return abi.decode(data, (uint, bytes)); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"), encodeArgs(33, 0x40, 7, "abcdefg")); -} - -BOOST_AUTO_TEST_CASE(abi_decode_calldata) -{ - char const* sourceCode = R"( - contract C { - function f(bytes calldata data) external pure returns (uint, bytes memory r) { - return abi.decode(data, (uint, bytes)); - } - } - )"; - compileAndRun(sourceCode); - ABI_CHECK(callContractFunction("f(bytes)", 0x20, 0x20 * 4, 33, 0x40, 7, "abcdefg"), encodeArgs(33, 0x40, 7, "abcdefg")); -} - BOOST_AUTO_TEST_CASE(code_access) { char const* sourceCode = R"( diff --git a/test/libsolidity/semanticTests/extracted/abi_decode_calldata.sol b/test/libsolidity/semanticTests/extracted/abi_decode_calldata.sol new file mode 100644 index 000000000..6b6e55e02 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/abi_decode_calldata.sol @@ -0,0 +1,11 @@ +contract C { + function f(bytes calldata data) + external + pure + returns (uint256, bytes memory r) + { + return abi.decode(data, (uint256, bytes)); + } +} +// ---- +// f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg" diff --git a/test/libsolidity/semanticTests/extracted/abi_decode_simple.sol b/test/libsolidity/semanticTests/extracted/abi_decode_simple.sol new file mode 100644 index 000000000..9ae6602f3 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/abi_decode_simple.sol @@ -0,0 +1,7 @@ +contract C { + function f(bytes memory data) public pure returns (uint256, bytes memory) { + return abi.decode(data, (uint256, bytes)); + } +} +// ---- +// f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg" diff --git a/test/libsolidity/semanticTests/extracted/abi_decode_simple_storage.sol b/test/libsolidity/semanticTests/extracted/abi_decode_simple_storage.sol new file mode 100644 index 000000000..af37480a3 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/abi_decode_simple_storage.sol @@ -0,0 +1,10 @@ +contract C { + bytes data; + + function f(bytes memory _data) public returns (uint256, bytes memory) { + data = _data; + return abi.decode(data, (uint256, bytes)); + } +} +// ---- +// f(bytes): 0x20, 0x80, 0x21, 0x40, 0x7, "abcdefg" -> 0x21, 0x40, 0x7, "abcdefg" diff --git a/test/libsolidity/semanticTests/extracted/abi_encode_empty_string.sol b/test/libsolidity/semanticTests/extracted/abi_encode_empty_string.sol new file mode 100644 index 000000000..6ca284908 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/abi_encode_empty_string.sol @@ -0,0 +1,11 @@ +// Tests that this will not end up using a "bytes0" type +// (which would assert) +contract C { + function f() public pure returns (bytes memory, bytes memory) { + return (abi.encode(""), abi.encodePacked("")); + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f() -> 0x40, 0xc0, 0x60, 0x20, 0x0, 0x0, 0x0 diff --git a/test/libsolidity/semanticTests/extracted/arrays_complex_from_and_to_storage.sol b/test/libsolidity/semanticTests/extracted/arrays_complex_from_and_to_storage.sol new file mode 100644 index 000000000..9cec148c7 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/arrays_complex_from_and_to_storage.sol @@ -0,0 +1,18 @@ +contract Test { + uint24[3][] public data; + + function set(uint24[3][] memory _data) public returns (uint256) { + data = _data; + return data.length; + } + + function get() public returns (uint24[3][] memory) { + return data; + } +} +// ---- +// set(uint24[3][]): 0x20, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 -> 0x06 +// data(uint256,uint256): 0x02, 0x02 -> 0x09 +// data(uint256,uint256): 0x05, 0x01 -> 0x11 +// data(uint256,uint256): 0x06, 0x00 -> FAILURE +// get() -> 0x20, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 diff --git a/test/libsolidity/semanticTests/extracted/arrays_complex_memory_index_access.sol b/test/libsolidity/semanticTests/extracted/arrays_complex_memory_index_access.sol new file mode 100644 index 000000000..d21bcc53f --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/arrays_complex_memory_index_access.sol @@ -0,0 +1,13 @@ +contract Test { + function set(uint24[3][] memory _data, uint256 a, uint256 b) + public + returns (uint256 l, uint256 e) + { + l = _data.length; + e = _data[a][b]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(uint24[3][],uint256,uint256): 0x60, 0x03, 0x02, 0x06, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12 -> 0x06, 0x0c diff --git a/test/libsolidity/semanticTests/extracted/assignment_to_const_var_involving_keccak.sol b/test/libsolidity/semanticTests/extracted/assignment_to_const_var_involving_keccak.sol new file mode 100644 index 000000000..2fc479d0e --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/assignment_to_const_var_involving_keccak.sol @@ -0,0 +1,9 @@ +contract C { + bytes32 constant x = keccak256("abc"); + + function f() public returns (bytes32) { + return x; + } +} +// ---- +// f() -> 0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 diff --git a/test/libsolidity/semanticTests/extracted/bitwise_shifting_constantinople.sol b/test/libsolidity/semanticTests/extracted/bitwise_shifting_constantinople.sol new file mode 100644 index 000000000..c6ada8c6a --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/bitwise_shifting_constantinople.sol @@ -0,0 +1,34 @@ +contract C { + function shl(uint256 a, uint256 b) public returns (uint256 c) { + assembly { + c := shl(b, a) + } + } + + function shr(uint256 a, uint256 b) public returns (uint256 c) { + assembly { + c := shr(b, a) + } + } + + function sar(uint256 a, uint256 b) public returns (uint256 c) { + assembly { + c := sar(b, a) + } + } +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: also +// ---- +// shl(uint256,uint256): 0x01, 0x02 -> 0x04 +// shl(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x01 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// shl(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x100 -> 0x00 +// shr(uint256,uint256): 0x03, 0x01 -> 0x01 +// shr(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x01 -> 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shr(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0xff -> 0x01 +// shr(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x100 -> 0x00 +// sar(uint256,uint256): 0x03, 0x01 -> 0x01 +// sar(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x01 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar(uint256,uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x100 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/extracted/bitwise_shifting_constantinople_combined.sol b/test/libsolidity/semanticTests/extracted/bitwise_shifting_constantinople_combined.sol new file mode 100644 index 000000000..6cacbe300 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/bitwise_shifting_constantinople_combined.sol @@ -0,0 +1,122 @@ +contract C { + function shl_zero(uint256 a) public returns (uint256 c) { + assembly { + c := shl(0, a) + } + } + + function shr_zero(uint256 a) public returns (uint256 c) { + assembly { + c := shr(0, a) + } + } + + function sar_zero(uint256 a) public returns (uint256 c) { + assembly { + c := sar(0, a) + } + } + + function shl_large(uint256 a) public returns (uint256 c) { + assembly { + c := shl(0x110, a) + } + } + + function shr_large(uint256 a) public returns (uint256 c) { + assembly { + c := shr(0x110, a) + } + } + + function sar_large(uint256 a) public returns (uint256 c) { + assembly { + c := sar(0x110, a) + } + } + + function shl_combined(uint256 a) public returns (uint256 c) { + assembly { + c := shl(4, shl(12, a)) + } + } + + function shr_combined(uint256 a) public returns (uint256 c) { + assembly { + c := shr(4, shr(12, a)) + } + } + + function sar_combined(uint256 a) public returns (uint256 c) { + assembly { + c := sar(4, sar(12, a)) + } + } + + function shl_combined_large(uint256 a) public returns (uint256 c) { + assembly { + c := shl(0xd0, shl(0x40, a)) + } + } + + function shl_combined_overflow(uint256 a) public returns (uint256 c) { + assembly { + c := shl(0x01, shl(not(0x00), a)) + } + } + + function shr_combined_large(uint256 a) public returns (uint256 c) { + assembly { + c := shr(0xd0, shr(0x40, a)) + } + } + + function shr_combined_overflow(uint256 a) public returns (uint256 c) { + assembly { + c := shr(0x01, shr(not(0x00), a)) + } + } + + function sar_combined_large(uint256 a) public returns (uint256 c) { + assembly { + c := sar(0xd0, sar(0x40, a)) + } + } +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: also +// ---- +// shl_zero(uint256): 0x00 -> 0x00 +// shl_zero(uint256): 0xffff -> 0xffff +// shl_zero(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shr_zero(uint256): 0x00 -> 0x00 +// shr_zero(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar_zero(uint256): 0x00 -> 0x00 +// sar_zero(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shl_large(uint256): 0x00 -> 0x00 +// shl_large(uint256): 0xffff -> 0x00 +// shl_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// shr_large(uint256): 0x00 -> 0x00 +// shr_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// sar_large(uint256): 0x00 -> 0x00 +// sar_large(uint256): 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// sar_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shl_combined(uint256): 0x00 -> 0x00 +// shl_combined(uint256): 0xffff -> 0xffff0000 +// shl_combined(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000 +// shr_combined(uint256): 0x00 -> 0x00 +// shr_combined(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar_combined(uint256): 0x00 -> 0x00 +// sar_combined(uint256): 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// sar_combined(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// shl_combined_large(uint256): 0x00 -> 0x00 +// shl_combined_large(uint256): 0xffff -> 0x00 +// shl_combined_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// shl_combined_overflow(uint256): 0x02 -> 0x00 +// shr_combined_large(uint256): 0x00 -> 0x00 +// shr_combined_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// shr_combined_overflow(uint256): 0x02 -> 0x00 +// sar_combined_large(uint256): 0x00 -> 0x00 +// sar_combined_large(uint256): 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0x00 +// sar_combined_large(uint256): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/extracted/bitwise_shifting_constants_constantinople.sol b/test/libsolidity/semanticTests/extracted/bitwise_shifting_constants_constantinople.sol new file mode 100644 index 000000000..bbc5ddaa4 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/bitwise_shifting_constants_constantinople.sol @@ -0,0 +1,83 @@ +contract C { + function shl_1() public returns (bool) { + uint256 c; + assembly { + c := shl(2, 1) + } + assert(c == 4); + return true; + } + + function shl_2() public returns (bool) { + uint256 c; + assembly { + c := shl( + 1, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) + } + assert( + c == + 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe + ); + return true; + } + + function shl_3() public returns (bool) { + uint256 c; + assembly { + c := shl( + 256, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) + } + assert(c == 0); + return true; + } + + function shr_1() public returns (bool) { + uint256 c; + assembly { + c := shr(1, 3) + } + assert(c == 1); + return true; + } + + function shr_2() public returns (bool) { + uint256 c; + assembly { + c := shr( + 1, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) + } + assert( + c == + 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ); + return true; + } + + function shr_3() public returns (bool) { + uint256 c; + assembly { + c := shr( + 256, + 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + ) + } + assert(c == 0); + return true; + } +} +// ==== +// EVMVersion: >=constantinople +// compileViaYul: also +// ---- +// shl_1() -> 0x01 +// shl_2() -> 0x01 +// shl_3() -> 0x01 +// shr_1() -> 0x01 +// shr_2() -> 0x01 +// shr_3() -> 0x01 diff --git a/test/libsolidity/semanticTests/extracted/bool_conversion.sol b/test/libsolidity/semanticTests/extracted/bool_conversion.sol new file mode 100644 index 000000000..20f594764 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/bool_conversion.sol @@ -0,0 +1,23 @@ +contract C { + function f(bool _b) public returns (uint256) { + if (_b) return 1; + else return 0; + } + + function g(bool _in) public returns (bool _out) { + _out = _in; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(bool): 0x0 -> 0x0 +// f(bool): 0x1 -> 0x1 +// f(bool): 0x2 -> 0x1 +// f(bool): 0x3 -> 0x1 +// f(bool): 0xff -> 0x1 +// g(bool): 0x0 -> 0x0 +// g(bool): 0x1 -> 0x1 +// g(bool): 0x2 -> 0x1 +// g(bool): 0x3 -> 0x1 +// g(bool): 0xff -> 0x1 diff --git a/test/libsolidity/semanticTests/extracted/bool_conversion_v2.sol b/test/libsolidity/semanticTests/extracted/bool_conversion_v2.sol new file mode 100644 index 000000000..a8fd89833 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/bool_conversion_v2.sol @@ -0,0 +1,24 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(bool _b) public returns (uint256) { + if (_b) return 1; + else return 0; + } + + function g(bool _in) public returns (bool _out) { + _out = _in; + } +} +// ---- +// f(bool): 0x0 -> 0x0 +// f(bool): 0x1 -> 0x1 +// f(bool): 0x2 -> FAILURE +// f(bool): 0x3 -> FAILURE +// f(bool): 0xff -> FAILURE +// g(bool): 0x0 -> 0x0 +// g(bool): 0x1 -> 0x1 +// g(bool): 0x2 -> FAILURE +// g(bool): 0x3 -> FAILURE +// g(bool): 0xff -> FAILURE diff --git a/test/libsolidity/semanticTests/extracted/bytes_memory_index_access.sol b/test/libsolidity/semanticTests/extracted/bytes_memory_index_access.sol new file mode 100644 index 000000000..5b7fd7ac3 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/bytes_memory_index_access.sol @@ -0,0 +1,13 @@ +contract Test { + function set(bytes memory _data, uint256 i) + public + returns (uint256 l, bytes1 c) + { + l = _data.length; + c = _data[i]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(bytes,uint256): 0x40, 0x03, 0x08, "abcdefgh" -> 0x08, "d" diff --git a/test/libsolidity/semanticTests/extracted/calldata_array_dynamic_bytes.sol b/test/libsolidity/semanticTests/extracted/calldata_array_dynamic_bytes.sol new file mode 100644 index 000000000..c2c2fa70a --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/calldata_array_dynamic_bytes.sol @@ -0,0 +1,74 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f1(bytes[1] calldata a) + external + returns (uint256, uint256, uint256, uint256) + { + return (a[0].length, uint8(a[0][0]), uint8(a[0][1]), uint8(a[0][2])); + } + + function f2(bytes[1] calldata a, bytes[1] calldata b) + external + returns (uint256, uint256, uint256, uint256, uint256, uint256, uint256) + { + return ( + a[0].length, + uint8(a[0][0]), + uint8(a[0][1]), + uint8(a[0][2]), + b[0].length, + uint8(b[0][0]), + uint8(b[0][1]) + ); + } + + function g1(bytes[2] calldata a) + external + returns ( + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256, + uint256 + ) + { + return ( + a[0].length, + uint8(a[0][0]), + uint8(a[0][1]), + uint8(a[0][2]), + a[1].length, + uint8(a[1][0]), + uint8(a[1][1]), + uint8(a[1][2]) + ); + } + + function g2(bytes[] calldata a) external returns (uint256[8] memory) { + return [ + a.length, + a[0].length, + uint8(a[0][0]), + uint8(a[0][1]), + a[1].length, + uint8(a[1][0]), + uint8(a[1][1]), + uint8(a[1][2]) + ]; + } +} + +// found expectation comments: +// same offset for both arrays @ ABI_CHECK( + +// ---- +// f1(bytes[1]): 0x20, 0x20, 0x3, hex"0102030000000000000000000000000000000000000000000000000000000000" -> 0x3, 0x1, 0x2, 0x3 +// f2(bytes[1],bytes[1]): 0x40, 0xa0, 0x20, 0x3, hex"0102030000000000000000000000000000000000000000000000000000000000", 0x20, 0x2, hex"0102000000000000000000000000000000000000000000000000000000000000" -> 0x3, 0x1, 0x2, 0x3, 0x2, 0x1, 0x2 +// g1(bytes[2]): 0x20, 0x40, 0x80, 0x3, hex"0102030000000000000000000000000000000000000000000000000000000000", 0x3, hex"0405060000000000000000000000000000000000000000000000000000000000" -> 0x3, 0x1, 0x2, 0x3, 0x3, 0x4, 0x5, 0x6 +// g1(bytes[2]): 0x20, 0x40, 0x40, 0x3, hex"0102030000000000000000000000000000000000000000000000000000000000" -> 0x3, 0x1, 0x2, 0x3, 0x3, 0x1, 0x2, 0x3 +// g2(bytes[]): 0x20, 0x2, 0x40, 0x80, 0x2, hex"0102000000000000000000000000000000000000000000000000000000000000", 0x3, hex"0405060000000000000000000000000000000000000000000000000000000000" -> 0x2, 0x2, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 diff --git a/test/libsolidity/semanticTests/extracted/calldata_bytes_array_to_memory.sol b/test/libsolidity/semanticTests/extracted/calldata_bytes_array_to_memory.sol new file mode 100644 index 000000000..04b2ed15d --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/calldata_bytes_array_to_memory.sol @@ -0,0 +1,18 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(bytes[] calldata a) + external + returns (uint256, uint256, bytes memory) + { + bytes memory m = a[0]; + return (a.length, m.length, m); + } +} +// ---- +// f(bytes[]): 0x20, 0x1, 0x20, 0x2, hex"6162000000000000000000000000000000000000000000000000000000000000" -> 0x1, 0x2, 0x60, 0x2, hex"6162000000000000000000000000000000000000000000000000000000000000" +// f(bytes[]): 0x20, 0x1, 0x20, 0x20, hex"7878787878787878787878787878787878787878787878787878787878787878" -> 0x1, 0x20, 0x60, 0x20, hex"7878787878787878787878787878787878787878787878787878787878787878" +// f(bytes[]): 0x20, 0x1, 0x20, 0x20, hex"7800000000000000000000000000000000000000000000000000000000000061" -> 0x1, 0x20, 0x60, 0x20, hex"7800000000000000000000000000000000000000000000000000000000000061" +// f(bytes[]): 0x20, 0x1, 0x20, 0x20, hex"6100000000000000000000000000000000000000000000000000000000000078" -> 0x1, 0x20, 0x60, 0x20, hex"6100000000000000000000000000000000000000000000000000000000000078" +// f(bytes[]): 0x20, 0x1, 0x20, 0x20, hex"616d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d78" -> 0x1, 0x20, 0x60, 0x20, hex"616d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d6d78" diff --git a/test/libsolidity/semanticTests/extracted/calldata_string_array.sol b/test/libsolidity/semanticTests/extracted/calldata_string_array.sol new file mode 100644 index 000000000..ae7d1e2ef --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/calldata_string_array.sol @@ -0,0 +1,15 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(string[] calldata a) + external + returns (uint256, uint256, uint256, string memory) + { + string memory s1 = a[0]; + bytes memory m1 = bytes(s1); + return (a.length, m1.length, uint8(m1[0]), s1); + } +} +// ---- +// f(string[]): 0x20, 0x1, 0x20, 0x2, hex"6162000000000000000000000000000000000000000000000000000000000000" -> 1, 2, 97, 0x80, 2, "ab" diff --git a/test/libsolidity/semanticTests/extracted/calldata_struct_cleaning.sol b/test/libsolidity/semanticTests/extracted/calldata_struct_cleaning.sol new file mode 100644 index 000000000..dbf6e4440 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/calldata_struct_cleaning.sol @@ -0,0 +1,22 @@ +pragma experimental ABIEncoderV2; + + +contract C { + struct S { + uint8 a; + bytes1 b; + } + + function f(S calldata s) external pure returns (uint256 a, bytes32 b) { + uint8 tmp1 = s.a; + bytes1 tmp2 = s.b; + assembly { + a := tmp1 + b := tmp2 + } + } +} +// ---- +// f((uint8,bytes1)): 0x12, hex"3400000000000000000000000000000000000000000000000000000000000000" -> 0x12, hex"3400000000000000000000000000000000000000000000000000000000000000" # double check that the valid case goes through # +// f((uint8,bytes1)): 0x1234, hex"5678000000000000000000000000000000000000000000000000000000000000" -> FAILURE +// f((uint8,bytes1)): 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -> FAILURE diff --git a/test/libsolidity/semanticTests/extracted/cleanup_address_types.sol b/test/libsolidity/semanticTests/extracted/cleanup_address_types.sol new file mode 100644 index 000000000..c94cfc776 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/cleanup_address_types.sol @@ -0,0 +1,17 @@ +// Checks that address types are properly cleaned before they are compared. +contract C { + function f(address a) public returns (uint256) { + if (a != 0x1234567890123456789012345678901234567890) return 1; + return 0; + } + + function g(address payable a) public returns (uint256) { + if (a != 0x1234567890123456789012345678901234567890) return 1; + return 0; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(address): 0xffff1234567890123456789012345678901234567890 -> 0x0 # We input longer data on purpose.# +// g(address): 0xffff1234567890123456789012345678901234567890 -> 0x0 diff --git a/test/libsolidity/semanticTests/extracted/cleanup_address_types_v2.sol b/test/libsolidity/semanticTests/extracted/cleanup_address_types_v2.sol new file mode 100644 index 000000000..beff156f7 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/cleanup_address_types_v2.sol @@ -0,0 +1,18 @@ +pragma experimental ABIEncoderV2; + + +// Checks that address types are properly cleaned before they are compared. +contract C { + function f(address a) public returns (uint256) { + if (a != 0x1234567890123456789012345678901234567890) return 1; + return 0; + } + + function g(address payable a) public returns (uint256) { + if (a != 0x1234567890123456789012345678901234567890) return 1; + return 0; + } +} +// ---- +// f(address): 0xffff1234567890123456789012345678901234567890 -> FAILURE # We input longer data on purpose.# +// g(address): 0xffff1234567890123456789012345678901234567890 -> FAILURE diff --git a/test/libsolidity/semanticTests/extracted/cleanup_bytes_types.sol b/test/libsolidity/semanticTests/extracted/cleanup_bytes_types.sol new file mode 100644 index 000000000..32b90748e --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/cleanup_bytes_types.sol @@ -0,0 +1,13 @@ +// Checks that bytesXX types are properly cleaned before they are compared. +contract C { + function f(bytes2 a, uint16 x) public returns (uint256) { + if (a != "ab") return 1; + if (x != 0x0102) return 2; + if (bytes3(uint24(x)) != 0x000102) return 3; + return 0; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(bytes2,uint16): "abc", 0x40102 -> 0x0 # We input longer data on purpose. # diff --git a/test/libsolidity/semanticTests/extracted/cleanup_bytes_types_v2.sol b/test/libsolidity/semanticTests/extracted/cleanup_bytes_types_v2.sol new file mode 100644 index 000000000..5adc97378 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/cleanup_bytes_types_v2.sol @@ -0,0 +1,14 @@ +pragma experimental ABIEncoderV2; + + +// Checks that bytesXX types are properly cleaned before they are compared. +contract C { + function f(bytes2 a, uint16 x) public returns (uint256) { + if (a != "ab") return 1; + if (x != 0x0102) return 2; + if (bytes3(uint24(x)) != 0x000102) return 3; + return 0; + } +} +// ---- +// f(bytes2,uint16): "abc", 0x40102 -> FAILURE # We input longer data on purpose. # diff --git a/test/libsolidity/semanticTests/extracted/delegatecall_return_value.sol b/test/libsolidity/semanticTests/extracted/delegatecall_return_value.sol new file mode 100644 index 000000000..ffa22b8a2 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/delegatecall_return_value.sol @@ -0,0 +1,37 @@ +contract C { + uint256 value; + + function set(uint256 _value) external { + value = _value; + } + + function get() external view returns (uint256) { + return value; + } + + function get_delegated() external returns (bool, bytes memory) { + return address(this).delegatecall(abi.encodeWithSignature("get()")); + } + + function assert0() external view { + assert(value == 0); + } + + function assert0_delegated() external returns (bool, bytes memory) { + return address(this).delegatecall(abi.encodeWithSignature("assert0()")); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// get() -> 0x00 +// assert0_delegated() -> 0x01, 0x40, 0x0 +// get_delegated() -> 0x01, 0x40, 0x20, 0x0 +// set(uint256): 0x01 -> +// get() -> 0x01 +// assert0_delegated() -> 0x00, 0x40, 0x0 +// get_delegated() -> 0x01, 0x40, 0x20, 0x1 +// set(uint256): 0x2a -> +// get() -> 0x2a +// assert0_delegated() -> 0x00, 0x40, 0x0 +// get_delegated() -> 0x01, 0x40, 0x20, 0x2a diff --git a/test/libsolidity/semanticTests/extracted/delegatecall_return_value_pre_byzantium.sol b/test/libsolidity/semanticTests/extracted/delegatecall_return_value_pre_byzantium.sol new file mode 100644 index 000000000..498449d17 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/delegatecall_return_value_pre_byzantium.sol @@ -0,0 +1,39 @@ +contract C { + uint256 value; + + function set(uint256 _value) external { + value = _value; + } + + function get() external view returns (uint256) { + return value; + } + + function get_delegated() external returns (bool) { + (bool success,) = address(this).delegatecall(abi.encodeWithSignature("get()")); + return success; + } + + function assert0() external view { + assert(value == 0); + } + + function assert0_delegated() external returns (bool) { + (bool success,) = address(this).delegatecall(abi.encodeWithSignature("assert0()")); + return success; + } +} +// ==== +// EVMVersion: 0x00 +// assert0_delegated() -> true +// get_delegated() -> true +// set(uint256): 0x01 -> +// get() -> 0x01 +// assert0_delegated() -> false +// get_delegated() -> true +// set(uint256): 0x2a -> +// get() -> 0x2a +// assert0_delegated() -> false +// get_delegated() -> true diff --git a/test/libsolidity/semanticTests/extracted/fixed_arrays_in_storage.sol b/test/libsolidity/semanticTests/extracted/fixed_arrays_in_storage.sol new file mode 100644 index 000000000..b65a3d254 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/fixed_arrays_in_storage.sol @@ -0,0 +1,45 @@ +contract c { + struct Data { + uint256 x; + uint256 y; + } + Data[2**10] data; + uint256[2**10 + 3] ids; + + function setIDStatic(uint256 id) public { + ids[2] = id; + } + + function setID(uint256 index, uint256 id) public { + ids[index] = id; + } + + function setData(uint256 index, uint256 x, uint256 y) public { + data[index].x = x; + data[index].y = y; + } + + function getID(uint256 index) public returns (uint256) { + return ids[index]; + } + + function getData(uint256 index) public returns (uint256 x, uint256 y) { + x = data[index].x; + y = data[index].y; + } + + function getLengths() public returns (uint256 l1, uint256 l2) { + l1 = data.length; + l2 = ids.length; + } +} +// ---- +// setIDStatic(uint256): 0xb -> +// getID(uint256): 0x2 -> 0xb +// setID(uint256,uint256): 0x7, 0x8 -> +// getID(uint256): 0x7 -> 0x8 +// setData(uint256,uint256,uint256): 0x7, 0x8, 0x9 -> +// setData(uint256,uint256,uint256): 0x8, 0xa, 0xb -> +// getData(uint256): 0x7 -> 0x8, 0x9 +// getData(uint256): 0x8 -> 0xa, 0xb +// getLengths() -> 0x400, 0x403 diff --git a/test/libsolidity/semanticTests/extracted/fixed_bytes_index_access.sol b/test/libsolidity/semanticTests/extracted/fixed_bytes_index_access.sol new file mode 100644 index 000000000..9ab74eaf3 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/fixed_bytes_index_access.sol @@ -0,0 +1,17 @@ +contract C { + bytes16[] public data; + + function f(bytes32 x) public returns (bytes1) { + return x[2]; + } + + function g(bytes32 x) public returns (uint256) { + data = [x[0], x[1], x[2]]; + data[0] = "12345"; + return uint256(uint8(data[0][4])); + } +} +// ---- +// f(bytes32): "789" -> "9" +// g(bytes32): "789" -> 0x35 +// data(uint256): 0x01 -> "8" diff --git a/test/libsolidity/semanticTests/extracted/function_types_sig.sol b/test/libsolidity/semanticTests/extracted/function_types_sig.sol new file mode 100644 index 000000000..2e771032b --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/function_types_sig.sol @@ -0,0 +1,26 @@ +contract C { + uint256 public x; + + function f() public pure returns (bytes4) { + return this.f.selector; + } + + function g() public returns (bytes4) { + function () pure external returns (bytes4) fun = this.f; + return fun.selector; + } + + function h() public returns (bytes4) { + function () pure external returns (bytes4) fun = this.f; + return fun.selector; + } + + function i() public pure returns (bytes4) { + return this.x.selector; + } +} +// ---- +// f() -> 0x26121ff000000000000000000000000000000000000000000000000000000000 +// g() -> 0x26121ff000000000000000000000000000000000000000000000000000000000 +// h() -> 0x26121ff000000000000000000000000000000000000000000000000000000000 +// i() -> 0x0c55699c00000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/extracted/iterated_keccak256_with_bytes.sol b/test/libsolidity/semanticTests/extracted/iterated_keccak256_with_bytes.sol new file mode 100644 index 000000000..91a26945c --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/iterated_keccak256_with_bytes.sol @@ -0,0 +1,12 @@ +contract c { + bytes data; + + function foo() public returns (bytes32) { + data.push("x"); + data.push("y"); + data.push("z"); + return keccak256(abi.encodePacked("b", keccak256(data), "a")); + } +} +// ---- +// foo() -> 0xb338eefce206f9f57b83aa738deecd5326dc4b72dd81ee6a7c621a6facb7acdc diff --git a/test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments.sol b/test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments.sol new file mode 100644 index 000000000..972aee839 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments.sol @@ -0,0 +1,7 @@ +contract c { + function foo(uint256 a, uint256 b, uint256 c) public returns (bytes32 d) { + d = keccak256(abi.encodePacked(a, b, c)); + } +} +// ---- +// foo(uint256,uint256,uint256): 0xa, 0xc, 0xd -> 0xbc740a98aae5923e8f04c9aa798c9ee82f69e319997699f2782c40828db9fd81 diff --git a/test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments_with_numeric_literals.sol b/test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments_with_numeric_literals.sol new file mode 100644 index 000000000..01397f55f --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments_with_numeric_literals.sol @@ -0,0 +1,7 @@ +contract c { + function foo(uint256 a, uint16 b) public returns (bytes32 d) { + d = keccak256(abi.encodePacked(a, b, uint8(145))); + } +} +// ---- +// foo(uint256,uint16): 0xa, 0xc -> 0x88acd45f75907e7c560318bc1a5249850a0999c4896717b1167d05d116e6dbad diff --git a/test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments_with_string_literals.sol b/test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments_with_string_literals.sol new file mode 100644 index 000000000..b157178fb --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments_with_string_literals.sol @@ -0,0 +1,12 @@ +contract c { + function foo() public returns (bytes32 d) { + d = keccak256("foo"); + } + + function bar(uint256 a, uint16 b) public returns (bytes32 d) { + d = keccak256(abi.encodePacked(a, b, uint8(145), "foo")); + } +} +// ---- +// foo() -> 0x41b1a0649752af1b28b3dc29a1556eee781e4a4c3a1f7f53f90fa834de098c4d +// bar(uint256,uint16): 0xa, 0xc -> 0x6990f36476dc412b1c4baa48e2d9f4aa4bb313f61fda367c8fdbbb2232dc6146 diff --git a/test/libsolidity/semanticTests/extracted/memory_arrays_dynamic_index_access_write.sol b/test/libsolidity/semanticTests/extracted/memory_arrays_dynamic_index_access_write.sol new file mode 100644 index 000000000..be824b759 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/memory_arrays_dynamic_index_access_write.sol @@ -0,0 +1,19 @@ +contract Test { + uint24[3][][4] data; + + function set(uint24[3][][4] memory x) + internal + returns (uint24[3][][4] memory) + { + x[1][2][2] = 1; + x[1][3][2] = 7; + return x; + } + + function f() public returns (uint24[3][] memory) { + while (data[1].length < 4) data[1].push(); + return set(data)[1]; + } +} +// ---- +// f() -> 0x20, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07 diff --git a/test/libsolidity/semanticTests/extracted/memory_arrays_index_access_write.sol b/test/libsolidity/semanticTests/extracted/memory_arrays_index_access_write.sol new file mode 100644 index 000000000..7a8a18670 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/memory_arrays_index_access_write.sol @@ -0,0 +1,14 @@ +contract Test { + function set(uint24[3][4] memory x) public { + x[2][2] = 1; + x[3][2] = 7; + } + + function f() public returns (uint24[3][4] memory) { + uint24[3][4] memory data; + set(data); + return data; + } +} +// ---- +// f() -> 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x07 diff --git a/test/libsolidity/semanticTests/extracted/memory_structs_nested_load.sol b/test/libsolidity/semanticTests/extracted/memory_structs_nested_load.sol new file mode 100644 index 000000000..c16705643 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/memory_structs_nested_load.sol @@ -0,0 +1,69 @@ +contract Test { + struct S { + uint8 x; + uint16 y; + uint256 z; + } + struct X { + uint8 x; + S s; + uint8[2] a; + } + X m_x; + + function load() + public + returns ( + uint256 a, + uint256 x, + uint256 y, + uint256 z, + uint256 a1, + uint256 a2 + ) + { + m_x.x = 1; + m_x.s.x = 2; + m_x.s.y = 3; + m_x.s.z = 4; + m_x.a[0] = 5; + m_x.a[1] = 6; + X memory d = m_x; + a = d.x; + x = d.s.x; + y = d.s.y; + z = d.s.z; + a1 = d.a[0]; + a2 = d.a[1]; + } + + function store() + public + returns ( + uint256 a, + uint256 x, + uint256 y, + uint256 z, + uint256 a1, + uint256 a2 + ) + { + X memory d; + d.x = 1; + d.s.x = 2; + d.s.y = 3; + d.s.z = 4; + d.a[0] = 5; + d.a[1] = 6; + m_x = d; + a = m_x.x; + x = m_x.s.x; + y = m_x.s.y; + z = m_x.s.z; + a1 = m_x.a[0]; + a2 = m_x.a[1]; + } +} +// ---- +// load() -> 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 +// store() -> 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 diff --git a/test/libsolidity/semanticTests/extracted/msg_sig.sol b/test/libsolidity/semanticTests/extracted/msg_sig.sol new file mode 100644 index 000000000..11b9a5339 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/msg_sig.sol @@ -0,0 +1,9 @@ +contract test { + function foo(uint256 a) public returns (bytes4 value) { + return msg.sig; + } +} +// ==== +// compileViaYul: also +// ---- +// foo(uint256): 0x0 -> 0x2fbebd3800000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/extracted/msg_sig_after_internal_call_is_same.sol b/test/libsolidity/semanticTests/extracted/msg_sig_after_internal_call_is_same.sol new file mode 100644 index 000000000..646f8f6e9 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/msg_sig_after_internal_call_is_same.sol @@ -0,0 +1,13 @@ +contract test { + function boo() public returns (bytes4 value) { + return msg.sig; + } + + function foo(uint256 a) public returns (bytes4 value) { + return boo(); + } +} +// ==== +// compileViaYul: also +// ---- +// foo(uint256): 0x0 -> 0x2fbebd3800000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/extracted/multi_modifiers.sol b/test/libsolidity/semanticTests/extracted/multi_modifiers.sol new file mode 100644 index 000000000..07f6c038d --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/multi_modifiers.sol @@ -0,0 +1,23 @@ +// This triggered a bug in some version because the variable in the modifier was not +// unregistered correctly. +contract C { + uint256 public x; + modifier m1 { + address a1 = msg.sender; + x++; + _; + } + + function f1() public m1() { + x += 7; + } + + function f2() public m1() { + x += 3; + } +} +// ---- +// f1() -> +// x() -> 0x08 +// f2() -> +// x() -> 0x0c diff --git a/test/libsolidity/semanticTests/extracted/reusing_memory.sol b/test/libsolidity/semanticTests/extracted/reusing_memory.sol new file mode 100644 index 000000000..b2876eeb3 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/reusing_memory.sol @@ -0,0 +1,26 @@ +// Invoke some features that use memory and test that they do not interfere with each other. +contract Helper { + uint256 public flag; + + constructor(uint256 x) public { + flag = x; + } +} + + +contract Main { + mapping(uint256 => uint256) map; + + function f(uint256 x) public returns (uint256) { + map[x] = x; + return + (new Helper(uint256(keccak256(abi.encodePacked(this.g(map[x])))))) + .flag(); + } + + function g(uint256 a) public returns (uint256) { + return map[a]; + } +} +// ---- +// f(uint256): 0x34 -> 0x46bddb1178e94d7f2892ff5f366840eb658911794f2c3a44c450aa2c505186c1 diff --git a/test/libsolidity/semanticTests/extracted/shift_right_garbled.sol b/test/libsolidity/semanticTests/extracted/shift_right_garbled.sol new file mode 100644 index 000000000..c0d6be8bd --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/shift_right_garbled.sol @@ -0,0 +1,14 @@ +contract C { + function f(uint8 a, uint8 b) public returns (uint256) { + assembly { + a := 0xffffffff + } + // Higher bits should be cleared before the shift + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(uint8,uint8): 0x00, 0x04 -> 0x0f +// f(uint8,uint8): 0x00, 0x1004 -> 0x0f diff --git a/test/libsolidity/semanticTests/extracted/shift_right_garbled_signed.sol b/test/libsolidity/semanticTests/extracted/shift_right_garbled_signed.sol new file mode 100644 index 000000000..df0570cb7 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/shift_right_garbled_signed.sol @@ -0,0 +1,30 @@ +contract C { + function f(int8 a, uint8 b) public returns (int256) { + assembly { + a := 0xfffffff0 + } + // Higher bits should be signextended before the shift + return a >> b; + } + + function g(int8 a, uint8 b) public returns (int256) { + assembly { + a := 0xf0 + } + // Higher bits should be signextended before the shift + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(int8,uint8): 0x00, 0x03 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// f(int8,uint8): 0x00, 0x04 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// f(int8,uint8): 0x00, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// f(int8,uint8): 0x00, 0x1003 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// f(int8,uint8): 0x00, 0x1004 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0x03 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// g(int8,uint8): 0x00, 0x04 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0x1003 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// g(int8,uint8): 0x00, 0x1004 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/extracted/shift_right_garbled_signed_v2.sol b/test/libsolidity/semanticTests/extracted/shift_right_garbled_signed_v2.sol new file mode 100644 index 000000000..0c1949a59 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/shift_right_garbled_signed_v2.sol @@ -0,0 +1,31 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(int8 a, uint8 b) public returns (int256) { + assembly { + a := 0xfffffff0 + } + // Higher bits should be signextended before the shift + return a >> b; + } + + function g(int8 a, uint8 b) public returns (int256) { + assembly { + a := 0xf0 + } + // Higher bits should be signextended before the shift + return a >> b; + } +} +// ---- +// f(int8,uint8): 0x00, 0x03 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// f(int8,uint8): 0x00, 0x04 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// f(int8,uint8): 0x00, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// f(int8,uint8): 0x00, 0x1003 -> FAILURE +// f(int8,uint8): 0x00, 0x1004 -> FAILURE +// g(int8,uint8): 0x00, 0x03 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe +// g(int8,uint8): 0x00, 0x04 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0xff -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +// g(int8,uint8): 0x00, 0x1003 -> FAILURE +// g(int8,uint8): 0x00, 0x1004 -> FAILURE diff --git a/test/libsolidity/semanticTests/extracted/shift_right_garbled_v2.sol b/test/libsolidity/semanticTests/extracted/shift_right_garbled_v2.sol new file mode 100644 index 000000000..54ac9540f --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/shift_right_garbled_v2.sol @@ -0,0 +1,15 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(uint8 a, uint8 b) public returns (uint256) { + assembly { + a := 0xffffffff + } + // Higher bits should be cleared before the shift + return a >> b; + } +} +// ---- +// f(uint8,uint8): 0x00, 0x04 -> 0x0f +// f(uint8,uint8): 0x00, 0x1004 -> FAILURE diff --git a/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int16.sol b/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int16.sol new file mode 100644 index 000000000..c59107f52 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int16.sol @@ -0,0 +1,13 @@ +contract C { + function f(int16 a, int16 b) public returns (int16) { + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(int16,int16): 0xff99, 0x00 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff99 +// f(int16,int16): 0xff99, 0x01 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcc +// f(int16,int16): 0xff99, 0x02 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe6 +// f(int16,int16): 0xff99, 0x04 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9 +// f(int16,int16): 0xff99, 0x08 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int16_v2.sol b/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int16_v2.sol new file mode 100644 index 000000000..6e462e704 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int16_v2.sol @@ -0,0 +1,14 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(int16 a, int16 b) public returns (int16) { + return a >> b; + } +} +// ---- +// f(int16,int16): 0xff99, 0x00 -> FAILURE +// f(int16,int16): 0xff99, 0x01 -> FAILURE +// f(int16,int16): 0xff99, 0x02 -> FAILURE +// f(int16,int16): 0xff99, 0x04 -> FAILURE +// f(int16,int16): 0xff99, 0x08 -> FAILURE diff --git a/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int32.sol b/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int32.sol new file mode 100644 index 000000000..74e9d53b0 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int32.sol @@ -0,0 +1,13 @@ +contract C { + function f(int32 a, int32 b) public returns (int32) { + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(int32,int32): 0xffffff99, 0x00 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff99 +// f(int32,int32): 0xffffff99, 0x01 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcc +// f(int32,int32): 0xffffff99, 0x02 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe6 +// f(int32,int32): 0xffffff99, 0x04 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9 +// f(int32,int32): 0xffffff99, 0x08 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int32_v2.sol b/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int32_v2.sol new file mode 100644 index 000000000..2466298f0 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int32_v2.sol @@ -0,0 +1,14 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(int32 a, int32 b) public returns (int32) { + return a >> b; + } +} +// ---- +// f(int32,int32): 0xffffff99, 0x00 -> FAILURE +// f(int32,int32): 0xffffff99, 0x01 -> FAILURE +// f(int32,int32): 0xffffff99, 0x02 -> FAILURE +// f(int32,int32): 0xffffff99, 0x04 -> FAILURE +// f(int32,int32): 0xffffff99, 0x08 -> FAILURE diff --git a/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int8.sol b/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int8.sol new file mode 100644 index 000000000..06dcf8eb5 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int8.sol @@ -0,0 +1,13 @@ +contract C { + function f(int8 a, int8 b) public returns (int8) { + return a >> b; + } +} +// ==== +// ABIEncoderV1Only: true +// ---- +// f(int8,int8): 0x99, 0x00 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff99 +// f(int8,int8): 0x99, 0x01 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffcc +// f(int8,int8): 0x99, 0x02 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe6 +// f(int8,int8): 0x99, 0x04 -> 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9 +// f(int8,int8): 0x99, 0x08 -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int8_v2.sol b/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int8_v2.sol new file mode 100644 index 000000000..643bc5e62 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int8_v2.sol @@ -0,0 +1,14 @@ +pragma experimental ABIEncoderV2; + + +contract C { + function f(int8 a, int8 b) public returns (int8) { + return a >> b; + } +} +// ---- +// f(int8,int8): 0x99, 0x00 -> FAILURE +// f(int8,int8): 0x99, 0x01 -> FAILURE +// f(int8,int8): 0x99, 0x02 -> FAILURE +// f(int8,int8): 0x99, 0x04 -> FAILURE +// f(int8,int8): 0x99, 0x08 -> FAILURE diff --git a/test/libsolidity/semanticTests/extracted/staticcall_for_view_and_pure.sol b/test/libsolidity/semanticTests/extracted/staticcall_for_view_and_pure.sol new file mode 100644 index 000000000..96c5419be --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/staticcall_for_view_and_pure.sol @@ -0,0 +1,39 @@ +contract C { + uint256 x; + + function f() public returns (uint256) { + x = 3; + return 1; + } +} + + +interface CView { + function f() external view returns (uint256); +} + + +interface CPure { + function f() external pure returns (uint256); +} + + +contract D { + function f() public returns (uint256) { + return (new C()).f(); + } + + function fview() public returns (uint256) { + return (CView(address(new C()))).f(); + } + + function fpure() public returns (uint256) { + return (CPure(address(new C()))).f(); + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// f() -> 0x1 # This should work, next should throw # +// fview() -> FAILURE +// fpure() -> FAILURE diff --git a/test/libsolidity/semanticTests/extracted/staticcall_for_view_and_pure_pre_byzantium.sol b/test/libsolidity/semanticTests/extracted/staticcall_for_view_and_pure_pre_byzantium.sol new file mode 100644 index 000000000..a193e8752 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/staticcall_for_view_and_pure_pre_byzantium.sol @@ -0,0 +1,39 @@ +contract C { + uint256 x; + + function f() public returns (uint256) { + x = 3; + return 1; + } +} + + +interface CView { + function f() external view returns (uint256); +} + + +interface CPure { + function f() external pure returns (uint256); +} + + +contract D { + function f() public returns (uint256) { + return (new C()).f(); + } + + function fview() public returns (uint256) { + return (CView(address(new C()))).f(); + } + + function fpure() public returns (uint256) { + return (CPure(address(new C()))).f(); + } +} +// ==== +// EVMVersion: 0x1 +// fview() -> 1 +// fpure() -> 1 diff --git a/test/libsolidity/semanticTests/extracted/string_allocation_bug.sol b/test/libsolidity/semanticTests/extracted/string_allocation_bug.sol new file mode 100644 index 000000000..9c2ec2e53 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/string_allocation_bug.sol @@ -0,0 +1,20 @@ +contract Sample { + struct s { + uint16 x; + uint16 y; + string a; + string b; + } + s[2] public p; + + constructor() public { + s memory m; + m.x = 0xbbbb; + m.y = 0xcccc; + m.a = "hello"; + m.b = "world"; + p[0] = m; + } +} +// ---- +// p(uint256): 0x0 -> 0xbbbb, 0xcccc, 0x80, 0xc0, 0x05, "hello", 0x05, "world" diff --git a/test/libsolidity/semanticTests/extracted/string_bytes_conversion.sol b/test/libsolidity/semanticTests/extracted/string_bytes_conversion.sol new file mode 100644 index 000000000..9578fc4f7 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/string_bytes_conversion.sol @@ -0,0 +1,17 @@ +contract Test { + string s; + bytes b; + + function f(string memory _s, uint256 n) public returns (bytes1) { + b = bytes(_s); + s = string(b); + return bytes(s)[n]; + } + + function l() public returns (uint256) { + return bytes(s).length; + } +} +// ---- +// f(string,uint256): 0x40, 0x02, 0x06, "abcdef" -> "c" +// l() -> 0x06 diff --git a/test/libsolidity/semanticTests/extracted/strings_in_struct.sol b/test/libsolidity/semanticTests/extracted/strings_in_struct.sol new file mode 100644 index 000000000..a6ec607f4 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/strings_in_struct.sol @@ -0,0 +1,35 @@ +contract buggystruct { + Buggy public bug; + + struct Buggy { + uint256 first; + uint256 second; + uint256 third; + string last; + } + + constructor() public { + bug = Buggy(10, 20, 30, "asdfghjkl"); + } + + function getFirst() public returns (uint256) { + return bug.first; + } + + function getSecond() public returns (uint256) { + return bug.second; + } + + function getThird() public returns (uint256) { + return bug.third; + } + + function getLast() public returns (string memory) { + return bug.last; + } +} +// ---- +// getFirst() -> 0x0a +// getSecond() -> 0x14 +// getThird() -> 0x1e +// getLast() -> 0x20, 0x09, "asdfghjkl" diff --git a/test/libsolidity/semanticTests/extracted/struct_constructor_nested.sol b/test/libsolidity/semanticTests/extracted/struct_constructor_nested.sol new file mode 100644 index 000000000..dbd67ab58 --- /dev/null +++ b/test/libsolidity/semanticTests/extracted/struct_constructor_nested.sol @@ -0,0 +1,30 @@ +contract C { + struct X { + uint256 x1; + uint256 x2; + } + struct S { + uint256 s1; + uint256[3] s2; + X s3; + } + S s; + + constructor() public { + uint256[3] memory s2; + s2[1] = 9; + s = S(1, s2, X(4, 5)); + } + + function get() + public + returns (uint256 s1, uint256[3] memory s2, uint256 x1, uint256 x2) + { + s1 = s.s1; + s2 = s.s2; + x1 = s.s3.x1; + x2 = s.s3.x2; + } +} +// ---- +// get() -> 0x01, 0x00, 0x09, 0x00, 0x04, 0x05 From 165f7bf60f50a0ad8799591331c6e3363da17aa3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 26 Mar 2020 17:47:36 +0100 Subject: [PATCH 33/76] Move files. --- .../{extracted => abiencodedecode}/abi_decode_calldata.sol | 0 .../{extracted => abiencodedecode}/abi_decode_simple.sol | 0 .../{extracted => abiencodedecode}/abi_decode_simple_storage.sol | 0 .../{extracted => abiencodedecode}/abi_encode_empty_string.sol | 0 .../{extracted => array}/arrays_complex_from_and_to_storage.sol | 0 .../{extracted => array}/arrays_complex_memory_index_access.sol | 0 .../{extracted => array}/bytes_memory_index_access.sol | 0 .../{extracted => array}/fixed_arrays_in_storage.sol | 0 .../{extracted => array}/fixed_bytes_index_access.sol | 0 .../memory_arrays_dynamic_index_access_write.sol | 0 .../{extracted => array}/memory_arrays_index_access_write.sol | 0 .../semanticTests/{extracted => array}/reusing_memory.sol | 0 .../semanticTests/{extracted => array}/string_allocation_bug.sol | 0 .../{extracted => array}/string_bytes_conversion.sol | 0 .../semanticTests/{extracted => array}/strings_in_struct.sol | 0 .../assignment_to_const_var_involving_keccak.sol | 0 .../{extracted => builtinFunctions}/function_types_sig.sol | 0 .../iterated_keccak256_with_bytes.sol | 0 .../keccak256_multiple_arguments.sol | 0 .../keccak256_multiple_arguments_with_numeric_literals.sol | 0 .../keccak256_multiple_arguments_with_string_literals.sol | 0 .../semanticTests/{extracted => builtinFunctions}/msg_sig.sol | 0 .../msg_sig_after_internal_call_is_same.sol | 0 .../{extracted => calldata}/calldata_array_dynamic_bytes.sol | 0 .../{extracted => calldata}/calldata_bytes_array_to_memory.sol | 0 .../{extracted => calldata}/calldata_string_array.sol | 0 .../{extracted => calldata}/calldata_struct_cleaning.sol | 0 .../semanticTests/{extracted => cleanup}/bool_conversion.sol | 0 .../semanticTests/{extracted => cleanup}/bool_conversion_v2.sol | 0 .../{extracted => cleanup}/cleanup_address_types.sol | 0 .../{extracted => cleanup}/cleanup_address_types_v2.sol | 0 .../semanticTests/{extracted => cleanup}/cleanup_bytes_types.sol | 0 .../{extracted => cleanup}/cleanup_bytes_types_v2.sol | 0 .../{extracted => functionCall}/delegatecall_return_value.sol | 0 .../delegatecall_return_value_pre_byzantium.sol | 0 .../{extracted => shifts}/bitwise_shifting_constantinople.sol | 0 .../bitwise_shifting_constantinople_combined.sol | 0 .../bitwise_shifting_constants_constantinople.sol | 0 .../semanticTests/{extracted => shifts}/shift_right_garbled.sol | 0 .../{extracted => shifts}/shift_right_garbled_signed.sol | 0 .../{extracted => shifts}/shift_right_garbled_signed_v2.sol | 0 .../{extracted => shifts}/shift_right_garbled_v2.sol | 0 .../shift_right_negative_lvalue_signextend_int16.sol | 0 .../shift_right_negative_lvalue_signextend_int16_v2.sol | 0 .../shift_right_negative_lvalue_signextend_int32.sol | 0 .../shift_right_negative_lvalue_signextend_int32_v2.sol | 0 .../shift_right_negative_lvalue_signextend_int8.sol | 0 .../shift_right_negative_lvalue_signextend_int8_v2.sol | 0 .../{extracted => structs}/memory_structs_nested_load.sol | 0 .../{extracted => structs}/struct_constructor_nested.sol | 0 .../semanticTests/{extracted => various}/multi_modifiers.sol | 0 .../{extracted => various}/staticcall_for_view_and_pure.sol | 0 .../staticcall_for_view_and_pure_pre_byzantium.sol | 0 53 files changed, 0 insertions(+), 0 deletions(-) rename test/libsolidity/semanticTests/{extracted => abiencodedecode}/abi_decode_calldata.sol (100%) rename test/libsolidity/semanticTests/{extracted => abiencodedecode}/abi_decode_simple.sol (100%) rename test/libsolidity/semanticTests/{extracted => abiencodedecode}/abi_decode_simple_storage.sol (100%) rename test/libsolidity/semanticTests/{extracted => abiencodedecode}/abi_encode_empty_string.sol (100%) rename test/libsolidity/semanticTests/{extracted => array}/arrays_complex_from_and_to_storage.sol (100%) rename test/libsolidity/semanticTests/{extracted => array}/arrays_complex_memory_index_access.sol (100%) rename test/libsolidity/semanticTests/{extracted => array}/bytes_memory_index_access.sol (100%) rename test/libsolidity/semanticTests/{extracted => array}/fixed_arrays_in_storage.sol (100%) rename test/libsolidity/semanticTests/{extracted => array}/fixed_bytes_index_access.sol (100%) rename test/libsolidity/semanticTests/{extracted => array}/memory_arrays_dynamic_index_access_write.sol (100%) rename test/libsolidity/semanticTests/{extracted => array}/memory_arrays_index_access_write.sol (100%) rename test/libsolidity/semanticTests/{extracted => array}/reusing_memory.sol (100%) rename test/libsolidity/semanticTests/{extracted => array}/string_allocation_bug.sol (100%) rename test/libsolidity/semanticTests/{extracted => array}/string_bytes_conversion.sol (100%) rename test/libsolidity/semanticTests/{extracted => array}/strings_in_struct.sol (100%) rename test/libsolidity/semanticTests/{extracted => builtinFunctions}/assignment_to_const_var_involving_keccak.sol (100%) rename test/libsolidity/semanticTests/{extracted => builtinFunctions}/function_types_sig.sol (100%) rename test/libsolidity/semanticTests/{extracted => builtinFunctions}/iterated_keccak256_with_bytes.sol (100%) rename test/libsolidity/semanticTests/{extracted => builtinFunctions}/keccak256_multiple_arguments.sol (100%) rename test/libsolidity/semanticTests/{extracted => builtinFunctions}/keccak256_multiple_arguments_with_numeric_literals.sol (100%) rename test/libsolidity/semanticTests/{extracted => builtinFunctions}/keccak256_multiple_arguments_with_string_literals.sol (100%) rename test/libsolidity/semanticTests/{extracted => builtinFunctions}/msg_sig.sol (100%) rename test/libsolidity/semanticTests/{extracted => builtinFunctions}/msg_sig_after_internal_call_is_same.sol (100%) rename test/libsolidity/semanticTests/{extracted => calldata}/calldata_array_dynamic_bytes.sol (100%) rename test/libsolidity/semanticTests/{extracted => calldata}/calldata_bytes_array_to_memory.sol (100%) rename test/libsolidity/semanticTests/{extracted => calldata}/calldata_string_array.sol (100%) rename test/libsolidity/semanticTests/{extracted => calldata}/calldata_struct_cleaning.sol (100%) rename test/libsolidity/semanticTests/{extracted => cleanup}/bool_conversion.sol (100%) rename test/libsolidity/semanticTests/{extracted => cleanup}/bool_conversion_v2.sol (100%) rename test/libsolidity/semanticTests/{extracted => cleanup}/cleanup_address_types.sol (100%) rename test/libsolidity/semanticTests/{extracted => cleanup}/cleanup_address_types_v2.sol (100%) rename test/libsolidity/semanticTests/{extracted => cleanup}/cleanup_bytes_types.sol (100%) rename test/libsolidity/semanticTests/{extracted => cleanup}/cleanup_bytes_types_v2.sol (100%) rename test/libsolidity/semanticTests/{extracted => functionCall}/delegatecall_return_value.sol (100%) rename test/libsolidity/semanticTests/{extracted => functionCall}/delegatecall_return_value_pre_byzantium.sol (100%) rename test/libsolidity/semanticTests/{extracted => shifts}/bitwise_shifting_constantinople.sol (100%) rename test/libsolidity/semanticTests/{extracted => shifts}/bitwise_shifting_constantinople_combined.sol (100%) rename test/libsolidity/semanticTests/{extracted => shifts}/bitwise_shifting_constants_constantinople.sol (100%) rename test/libsolidity/semanticTests/{extracted => shifts}/shift_right_garbled.sol (100%) rename test/libsolidity/semanticTests/{extracted => shifts}/shift_right_garbled_signed.sol (100%) rename test/libsolidity/semanticTests/{extracted => shifts}/shift_right_garbled_signed_v2.sol (100%) rename test/libsolidity/semanticTests/{extracted => shifts}/shift_right_garbled_v2.sol (100%) rename test/libsolidity/semanticTests/{extracted => shifts}/shift_right_negative_lvalue_signextend_int16.sol (100%) rename test/libsolidity/semanticTests/{extracted => shifts}/shift_right_negative_lvalue_signextend_int16_v2.sol (100%) rename test/libsolidity/semanticTests/{extracted => shifts}/shift_right_negative_lvalue_signextend_int32.sol (100%) rename test/libsolidity/semanticTests/{extracted => shifts}/shift_right_negative_lvalue_signextend_int32_v2.sol (100%) rename test/libsolidity/semanticTests/{extracted => shifts}/shift_right_negative_lvalue_signextend_int8.sol (100%) rename test/libsolidity/semanticTests/{extracted => shifts}/shift_right_negative_lvalue_signextend_int8_v2.sol (100%) rename test/libsolidity/semanticTests/{extracted => structs}/memory_structs_nested_load.sol (100%) rename test/libsolidity/semanticTests/{extracted => structs}/struct_constructor_nested.sol (100%) rename test/libsolidity/semanticTests/{extracted => various}/multi_modifiers.sol (100%) rename test/libsolidity/semanticTests/{extracted => various}/staticcall_for_view_and_pure.sol (100%) rename test/libsolidity/semanticTests/{extracted => various}/staticcall_for_view_and_pure_pre_byzantium.sol (100%) diff --git a/test/libsolidity/semanticTests/extracted/abi_decode_calldata.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_calldata.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/abi_decode_calldata.sol rename to test/libsolidity/semanticTests/abiencodedecode/abi_decode_calldata.sol diff --git a/test/libsolidity/semanticTests/extracted/abi_decode_simple.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/abi_decode_simple.sol rename to test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple.sol diff --git a/test/libsolidity/semanticTests/extracted/abi_decode_simple_storage.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/abi_decode_simple_storage.sol rename to test/libsolidity/semanticTests/abiencodedecode/abi_decode_simple_storage.sol diff --git a/test/libsolidity/semanticTests/extracted/abi_encode_empty_string.sol b/test/libsolidity/semanticTests/abiencodedecode/abi_encode_empty_string.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/abi_encode_empty_string.sol rename to test/libsolidity/semanticTests/abiencodedecode/abi_encode_empty_string.sol diff --git a/test/libsolidity/semanticTests/extracted/arrays_complex_from_and_to_storage.sol b/test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/arrays_complex_from_and_to_storage.sol rename to test/libsolidity/semanticTests/array/arrays_complex_from_and_to_storage.sol diff --git a/test/libsolidity/semanticTests/extracted/arrays_complex_memory_index_access.sol b/test/libsolidity/semanticTests/array/arrays_complex_memory_index_access.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/arrays_complex_memory_index_access.sol rename to test/libsolidity/semanticTests/array/arrays_complex_memory_index_access.sol diff --git a/test/libsolidity/semanticTests/extracted/bytes_memory_index_access.sol b/test/libsolidity/semanticTests/array/bytes_memory_index_access.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/bytes_memory_index_access.sol rename to test/libsolidity/semanticTests/array/bytes_memory_index_access.sol diff --git a/test/libsolidity/semanticTests/extracted/fixed_arrays_in_storage.sol b/test/libsolidity/semanticTests/array/fixed_arrays_in_storage.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/fixed_arrays_in_storage.sol rename to test/libsolidity/semanticTests/array/fixed_arrays_in_storage.sol diff --git a/test/libsolidity/semanticTests/extracted/fixed_bytes_index_access.sol b/test/libsolidity/semanticTests/array/fixed_bytes_index_access.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/fixed_bytes_index_access.sol rename to test/libsolidity/semanticTests/array/fixed_bytes_index_access.sol diff --git a/test/libsolidity/semanticTests/extracted/memory_arrays_dynamic_index_access_write.sol b/test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/memory_arrays_dynamic_index_access_write.sol rename to test/libsolidity/semanticTests/array/memory_arrays_dynamic_index_access_write.sol diff --git a/test/libsolidity/semanticTests/extracted/memory_arrays_index_access_write.sol b/test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/memory_arrays_index_access_write.sol rename to test/libsolidity/semanticTests/array/memory_arrays_index_access_write.sol diff --git a/test/libsolidity/semanticTests/extracted/reusing_memory.sol b/test/libsolidity/semanticTests/array/reusing_memory.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/reusing_memory.sol rename to test/libsolidity/semanticTests/array/reusing_memory.sol diff --git a/test/libsolidity/semanticTests/extracted/string_allocation_bug.sol b/test/libsolidity/semanticTests/array/string_allocation_bug.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/string_allocation_bug.sol rename to test/libsolidity/semanticTests/array/string_allocation_bug.sol diff --git a/test/libsolidity/semanticTests/extracted/string_bytes_conversion.sol b/test/libsolidity/semanticTests/array/string_bytes_conversion.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/string_bytes_conversion.sol rename to test/libsolidity/semanticTests/array/string_bytes_conversion.sol diff --git a/test/libsolidity/semanticTests/extracted/strings_in_struct.sol b/test/libsolidity/semanticTests/array/strings_in_struct.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/strings_in_struct.sol rename to test/libsolidity/semanticTests/array/strings_in_struct.sol diff --git a/test/libsolidity/semanticTests/extracted/assignment_to_const_var_involving_keccak.sol b/test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/assignment_to_const_var_involving_keccak.sol rename to test/libsolidity/semanticTests/builtinFunctions/assignment_to_const_var_involving_keccak.sol diff --git a/test/libsolidity/semanticTests/extracted/function_types_sig.sol b/test/libsolidity/semanticTests/builtinFunctions/function_types_sig.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/function_types_sig.sol rename to test/libsolidity/semanticTests/builtinFunctions/function_types_sig.sol diff --git a/test/libsolidity/semanticTests/extracted/iterated_keccak256_with_bytes.sol b/test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/iterated_keccak256_with_bytes.sol rename to test/libsolidity/semanticTests/builtinFunctions/iterated_keccak256_with_bytes.sol diff --git a/test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments.sol rename to test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments.sol diff --git a/test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments_with_numeric_literals.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments_with_numeric_literals.sol rename to test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_numeric_literals.sol diff --git a/test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments_with_string_literals.sol b/test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/keccak256_multiple_arguments_with_string_literals.sol rename to test/libsolidity/semanticTests/builtinFunctions/keccak256_multiple_arguments_with_string_literals.sol diff --git a/test/libsolidity/semanticTests/extracted/msg_sig.sol b/test/libsolidity/semanticTests/builtinFunctions/msg_sig.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/msg_sig.sol rename to test/libsolidity/semanticTests/builtinFunctions/msg_sig.sol diff --git a/test/libsolidity/semanticTests/extracted/msg_sig_after_internal_call_is_same.sol b/test/libsolidity/semanticTests/builtinFunctions/msg_sig_after_internal_call_is_same.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/msg_sig_after_internal_call_is_same.sol rename to test/libsolidity/semanticTests/builtinFunctions/msg_sig_after_internal_call_is_same.sol diff --git a/test/libsolidity/semanticTests/extracted/calldata_array_dynamic_bytes.sol b/test/libsolidity/semanticTests/calldata/calldata_array_dynamic_bytes.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/calldata_array_dynamic_bytes.sol rename to test/libsolidity/semanticTests/calldata/calldata_array_dynamic_bytes.sol diff --git a/test/libsolidity/semanticTests/extracted/calldata_bytes_array_to_memory.sol b/test/libsolidity/semanticTests/calldata/calldata_bytes_array_to_memory.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/calldata_bytes_array_to_memory.sol rename to test/libsolidity/semanticTests/calldata/calldata_bytes_array_to_memory.sol diff --git a/test/libsolidity/semanticTests/extracted/calldata_string_array.sol b/test/libsolidity/semanticTests/calldata/calldata_string_array.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/calldata_string_array.sol rename to test/libsolidity/semanticTests/calldata/calldata_string_array.sol diff --git a/test/libsolidity/semanticTests/extracted/calldata_struct_cleaning.sol b/test/libsolidity/semanticTests/calldata/calldata_struct_cleaning.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/calldata_struct_cleaning.sol rename to test/libsolidity/semanticTests/calldata/calldata_struct_cleaning.sol diff --git a/test/libsolidity/semanticTests/extracted/bool_conversion.sol b/test/libsolidity/semanticTests/cleanup/bool_conversion.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/bool_conversion.sol rename to test/libsolidity/semanticTests/cleanup/bool_conversion.sol diff --git a/test/libsolidity/semanticTests/extracted/bool_conversion_v2.sol b/test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/bool_conversion_v2.sol rename to test/libsolidity/semanticTests/cleanup/bool_conversion_v2.sol diff --git a/test/libsolidity/semanticTests/extracted/cleanup_address_types.sol b/test/libsolidity/semanticTests/cleanup/cleanup_address_types.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/cleanup_address_types.sol rename to test/libsolidity/semanticTests/cleanup/cleanup_address_types.sol diff --git a/test/libsolidity/semanticTests/extracted/cleanup_address_types_v2.sol b/test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/cleanup_address_types_v2.sol rename to test/libsolidity/semanticTests/cleanup/cleanup_address_types_v2.sol diff --git a/test/libsolidity/semanticTests/extracted/cleanup_bytes_types.sol b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/cleanup_bytes_types.sol rename to test/libsolidity/semanticTests/cleanup/cleanup_bytes_types.sol diff --git a/test/libsolidity/semanticTests/extracted/cleanup_bytes_types_v2.sol b/test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/cleanup_bytes_types_v2.sol rename to test/libsolidity/semanticTests/cleanup/cleanup_bytes_types_v2.sol diff --git a/test/libsolidity/semanticTests/extracted/delegatecall_return_value.sol b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/delegatecall_return_value.sol rename to test/libsolidity/semanticTests/functionCall/delegatecall_return_value.sol diff --git a/test/libsolidity/semanticTests/extracted/delegatecall_return_value_pre_byzantium.sol b/test/libsolidity/semanticTests/functionCall/delegatecall_return_value_pre_byzantium.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/delegatecall_return_value_pre_byzantium.sol rename to test/libsolidity/semanticTests/functionCall/delegatecall_return_value_pre_byzantium.sol diff --git a/test/libsolidity/semanticTests/extracted/bitwise_shifting_constantinople.sol b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/bitwise_shifting_constantinople.sol rename to test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople.sol diff --git a/test/libsolidity/semanticTests/extracted/bitwise_shifting_constantinople_combined.sol b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople_combined.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/bitwise_shifting_constantinople_combined.sol rename to test/libsolidity/semanticTests/shifts/bitwise_shifting_constantinople_combined.sol diff --git a/test/libsolidity/semanticTests/extracted/bitwise_shifting_constants_constantinople.sol b/test/libsolidity/semanticTests/shifts/bitwise_shifting_constants_constantinople.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/bitwise_shifting_constants_constantinople.sol rename to test/libsolidity/semanticTests/shifts/bitwise_shifting_constants_constantinople.sol diff --git a/test/libsolidity/semanticTests/extracted/shift_right_garbled.sol b/test/libsolidity/semanticTests/shifts/shift_right_garbled.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/shift_right_garbled.sol rename to test/libsolidity/semanticTests/shifts/shift_right_garbled.sol diff --git a/test/libsolidity/semanticTests/extracted/shift_right_garbled_signed.sol b/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/shift_right_garbled_signed.sol rename to test/libsolidity/semanticTests/shifts/shift_right_garbled_signed.sol diff --git a/test/libsolidity/semanticTests/extracted/shift_right_garbled_signed_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/shift_right_garbled_signed_v2.sol rename to test/libsolidity/semanticTests/shifts/shift_right_garbled_signed_v2.sol diff --git a/test/libsolidity/semanticTests/extracted/shift_right_garbled_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/shift_right_garbled_v2.sol rename to test/libsolidity/semanticTests/shifts/shift_right_garbled_v2.sol diff --git a/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int16.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int16.sol rename to test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16.sol diff --git a/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int16_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int16_v2.sol rename to test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int16_v2.sol diff --git a/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int32.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int32.sol rename to test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32.sol diff --git a/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int32_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int32_v2.sol rename to test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int32_v2.sol diff --git a/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int8.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int8.sol rename to test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8.sol diff --git a/test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int8_v2.sol b/test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/shift_right_negative_lvalue_signextend_int8_v2.sol rename to test/libsolidity/semanticTests/shifts/shift_right_negative_lvalue_signextend_int8_v2.sol diff --git a/test/libsolidity/semanticTests/extracted/memory_structs_nested_load.sol b/test/libsolidity/semanticTests/structs/memory_structs_nested_load.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/memory_structs_nested_load.sol rename to test/libsolidity/semanticTests/structs/memory_structs_nested_load.sol diff --git a/test/libsolidity/semanticTests/extracted/struct_constructor_nested.sol b/test/libsolidity/semanticTests/structs/struct_constructor_nested.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/struct_constructor_nested.sol rename to test/libsolidity/semanticTests/structs/struct_constructor_nested.sol diff --git a/test/libsolidity/semanticTests/extracted/multi_modifiers.sol b/test/libsolidity/semanticTests/various/multi_modifiers.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/multi_modifiers.sol rename to test/libsolidity/semanticTests/various/multi_modifiers.sol diff --git a/test/libsolidity/semanticTests/extracted/staticcall_for_view_and_pure.sol b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/staticcall_for_view_and_pure.sol rename to test/libsolidity/semanticTests/various/staticcall_for_view_and_pure.sol diff --git a/test/libsolidity/semanticTests/extracted/staticcall_for_view_and_pure_pre_byzantium.sol b/test/libsolidity/semanticTests/various/staticcall_for_view_and_pure_pre_byzantium.sol similarity index 100% rename from test/libsolidity/semanticTests/extracted/staticcall_for_view_and_pure_pre_byzantium.sol rename to test/libsolidity/semanticTests/various/staticcall_for_view_and_pure_pre_byzantium.sol From 173f2348600a04c544c02d4a2b8f93946d2cc7b4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 24 Mar 2020 18:25:59 +0100 Subject: [PATCH 34/76] Refactor: Replace inheritance hierarchy by most derived contract. --- libsolidity/ast/AST.cpp | 58 +++++++++++ libsolidity/ast/AST.h | 32 +++++++ libsolidity/codegen/CompilerContext.cpp | 95 +++++++------------ libsolidity/codegen/CompilerContext.h | 22 ++--- libsolidity/codegen/ContractCompiler.cpp | 9 +- libsolidity/codegen/ExpressionCompiler.cpp | 7 +- .../codegen/ir/IRGenerationContext.cpp | 27 ++---- libsolidity/codegen/ir/IRGenerationContext.h | 10 +- libsolidity/codegen/ir/IRGenerator.cpp | 4 +- .../codegen/ir/IRGeneratorForStatements.cpp | 2 +- .../SolidityExpressionCompiler.cpp | 6 +- 11 files changed, 157 insertions(+), 115 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index a76e6e274..b711758d6 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -319,6 +319,37 @@ FunctionDefinitionAnnotation& FunctionDefinition::annotation() const return initAnnotation(); } +FunctionDefinition const& FunctionDefinition::resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart +) const +{ + solAssert(!isConstructor(), ""); + // If we are not doing super-lookup and the function is not virtual, we can stop here. + if (_searchStart == nullptr && !virtualSemantics()) + return *this; + + solAssert(!dynamic_cast(*scope()).isLibrary(), ""); + + FunctionType const* functionType = TypeProvider::function(*this)->asCallableFunction(false); + + for (ContractDefinition const* c: _mostDerivedContract.annotation().linearizedBaseContracts) + { + if (_searchStart != nullptr && c != _searchStart) + continue; + _searchStart = nullptr; + for (FunctionDefinition const* function: c->definedFunctions()) + if ( + function->name() == name() && + !function->isConstructor() && + FunctionType(*function).asCallableFunction(false)->hasEqualParameterTypes(*functionType) + ) + return *function; + } + solAssert(false, "Virtual function " + name() + " not found."); + return *this; // not reached +} + TypePointer ModifierDefinition::type() const { return TypeProvider::modifier(*this); @@ -329,6 +360,33 @@ ModifierDefinitionAnnotation& ModifierDefinition::annotation() const return initAnnotation(); } +ModifierDefinition const& ModifierDefinition::resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart +) const +{ + solAssert(_searchStart == nullptr, "Used super in connection with modifiers."); + + // If we are not doing super-lookup and the modifier is not virtual, we can stop here. + if (_searchStart == nullptr && !virtualSemantics()) + return *this; + + solAssert(!dynamic_cast(*scope()).isLibrary(), ""); + + for (ContractDefinition const* c: _mostDerivedContract.annotation().linearizedBaseContracts) + { + if (_searchStart != nullptr && c != _searchStart) + continue; + _searchStart = nullptr; + for (ModifierDefinition const* modifier: c->functionModifiers()) + if (modifier->name() == name()) + return *modifier; + } + solAssert(false, "Virtual modifier " + name() + " not found."); + return *this; // not reached +} + + TypePointer EventDefinition::type() const { return TypeProvider::function(*this); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 052970bc8..4bbacbdf3 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -689,6 +689,18 @@ public: CallableDeclarationAnnotation& annotation() const override = 0; + /// Performs virtual or super function/modifier lookup: + /// If @a _searchStart is nullptr, performs virtual function lookup, i.e. + /// searches the inheritance hierarchy of @a _mostDerivedContract towards the base + /// and returns the first function/modifier definition that + /// is overwritten by this callable. + /// If @a _searchStart is non-null, starts searching only from that contract, but + /// still in the hierarchy of @a _mostDerivedContract. + virtual CallableDeclaration const& resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart = nullptr + ) const = 0; + protected: ASTPointer m_parameters; ASTPointer m_overrides; @@ -799,6 +811,12 @@ public: CallableDeclaration::virtualSemantics() || (annotation().contract && annotation().contract->isInterface()); } + + FunctionDefinition const& resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart = nullptr + ) const override; + private: StateMutability m_stateMutability; Token const m_kind; @@ -945,6 +963,12 @@ public: ModifierDefinitionAnnotation& annotation() const override; + ModifierDefinition const& resolveVirtual( + ContractDefinition const& _mostDerivedContract, + ContractDefinition const* _searchStart = nullptr + ) const override; + + private: ASTPointer m_body; }; @@ -1010,6 +1034,14 @@ public: EventDefinitionAnnotation& annotation() const override; + CallableDeclaration const& resolveVirtual( + ContractDefinition const&, + ContractDefinition const* + ) const override + { + solAssert(false, "Tried to resolve virtual event."); + } + private: bool m_anonymous = false; }; diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 26cff59ec..6984a030b 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -272,57 +272,43 @@ evmasm::AssemblyItem CompilerContext::functionEntryLabelIfExists(Declaration con return m_functionCompilationQueue.entryLabelIfExists(_declaration); } -FunctionDefinition const& CompilerContext::resolveVirtualFunction(FunctionDefinition const& _function) -{ - // Libraries do not allow inheritance and their functions can be inlined, so we should not - // search the inheritance hierarchy (which will be the wrong one in case the function - // is inlined). - if (auto scope = dynamic_cast(_function.scope())) - if (scope->isLibrary()) - return _function; - solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - return resolveVirtualFunction(_function, m_inheritanceHierarchy.begin()); -} - FunctionDefinition const& CompilerContext::superFunction(FunctionDefinition const& _function, ContractDefinition const& _base) { - solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - return resolveVirtualFunction(_function, superContract(_base)); + solAssert(m_mostDerivedContract, "No most derived contract set."); + ContractDefinition const* super = superContract(_base); + solAssert(super, "Super contract not available."); + return _function.resolveVirtual(mostDerivedContract(), super); } FunctionDefinition const* CompilerContext::nextConstructor(ContractDefinition const& _contract) const { - vector::const_iterator it = superContract(_contract); - for (; it != m_inheritanceHierarchy.end(); ++it) - if ((*it)->constructor()) - return (*it)->constructor(); + ContractDefinition const* next = superContract(_contract); + if (next == nullptr) + return nullptr; + for (ContractDefinition const* c: m_mostDerivedContract->annotation().linearizedBaseContracts) + if (next != nullptr && next != c) + continue; + else + { + next = nullptr; + if (c->constructor()) + return c->constructor(); + } return nullptr; } +ContractDefinition const& CompilerContext::mostDerivedContract() const +{ + solAssert(m_mostDerivedContract, "Most derived contract not set."); + return *m_mostDerivedContract; +} + Declaration const* CompilerContext::nextFunctionToCompile() const { return m_functionCompilationQueue.nextFunctionToCompile(); } -ModifierDefinition const& CompilerContext::resolveVirtualFunctionModifier( - ModifierDefinition const& _modifier -) const -{ - // Libraries do not allow inheritance and their functions can be inlined, so we should not - // search the inheritance hierarchy (which will be the wrong one in case the function - // is inlined). - if (auto scope = dynamic_cast(_modifier.scope())) - if (scope->isLibrary()) - return _modifier; - solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - for (ContractDefinition const* contract: m_inheritanceHierarchy) - for (ModifierDefinition const* modifier: contract->functionModifiers()) - if (modifier->name() == _modifier.name()) - return *modifier; - solAssert(false, "Function modifier " + _modifier.name() + " not found in inheritance hierarchy."); -} - unsigned CompilerContext::baseStackOffsetOfVariable(Declaration const& _declaration) const { auto res = m_localVariables.find(&_declaration); @@ -556,32 +542,19 @@ LinkerObject const& CompilerContext::assembledObject() const return object; } -FunctionDefinition const& CompilerContext::resolveVirtualFunction( - FunctionDefinition const& _function, - vector::const_iterator _searchStart -) +ContractDefinition const* CompilerContext::superContract(ContractDefinition const& _contract) const { - string name = _function.name(); - FunctionType functionType(_function); - auto it = _searchStart; - for (; it != m_inheritanceHierarchy.end(); ++it) - for (FunctionDefinition const* function: (*it)->definedFunctions()) - if ( - function->name() == name && - !function->isConstructor() && - FunctionType(*function).asCallableFunction(false)->hasEqualParameterTypes(functionType) - ) - return *function; - solAssert(false, "Super function " + name + " not found."); - return _function; // not reached -} - -vector::const_iterator CompilerContext::superContract(ContractDefinition const& _contract) const -{ - solAssert(!m_inheritanceHierarchy.empty(), "No inheritance hierarchy set."); - auto it = find(m_inheritanceHierarchy.begin(), m_inheritanceHierarchy.end(), &_contract); - solAssert(it != m_inheritanceHierarchy.end(), "Base not found in inheritance hierarchy."); - return ++it; + auto const& hierarchy = mostDerivedContract().annotation().linearizedBaseContracts; + auto it = find(hierarchy.begin(), hierarchy.end(), &_contract); + solAssert(it != hierarchy.end(), "Base not found in inheritance hierarchy."); + ++it; + if (it == hierarchy.end()) + return nullptr; + else + { + solAssert(*it != &_contract, ""); + return *it; + } } string CompilerContext::revertReasonIfDebug(string const& _message) diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index ec846af83..a8e494435 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -114,15 +114,14 @@ public: /// @returns the entry label of the given function. Might return an AssemblyItem of type /// UndefinedItem if it does not exist yet. evmasm::AssemblyItem functionEntryLabelIfExists(Declaration const& _declaration) const; - /// @returns the entry label of the given function and takes overrides into account. - FunctionDefinition const& resolveVirtualFunction(FunctionDefinition const& _function); /// @returns the function that overrides the given declaration from the most derived class just /// above _base in the current inheritance hierarchy. FunctionDefinition const& superFunction(FunctionDefinition const& _function, ContractDefinition const& _base); /// @returns the next constructor in the inheritance hierarchy. FunctionDefinition const* nextConstructor(ContractDefinition const& _contract) const; - /// Sets the current inheritance hierarchy from derived to base. - void setInheritanceHierarchy(std::vector const& _hierarchy) { m_inheritanceHierarchy = _hierarchy; } + /// Sets the contract currently being compiled - the most derived one. + void setMostDerivedContract(ContractDefinition const& _contract) { m_mostDerivedContract = &_contract; } + ContractDefinition const& mostDerivedContract() const; /// @returns the next function in the queue of functions that are still to be compiled /// (i.e. that were referenced during compilation but where we did not yet generate code for). @@ -171,7 +170,6 @@ public: /// empty return value. std::pair> requestedYulFunctions(); - ModifierDefinition const& resolveVirtualFunctionModifier(ModifierDefinition const& _modifier) const; /// Returns the distance of the given local variable from the bottom of the stack (of the current function). unsigned baseStackOffsetOfVariable(Declaration const& _declaration) const; /// If supplied by a value returned by @ref baseStackOffsetOfVariable(variable), returns @@ -315,14 +313,8 @@ public: RevertStrings revertStrings() const { return m_revertStrings; } private: - /// Searches the inheritance hierarchy towards the base starting from @a _searchStart and returns - /// the first function definition that is overwritten by _function. - FunctionDefinition const& resolveVirtualFunction( - FunctionDefinition const& _function, - std::vector::const_iterator _searchStart - ); - /// @returns an iterator to the contract directly above the given contract. - std::vector::const_iterator superContract(ContractDefinition const& _contract) const; + /// @returns a pointer to the contract directly above the given contract. + ContractDefinition const* superContract(ContractDefinition const& _contract) const; /// Updates source location set in the assembly. void updateSourceLocation(); @@ -381,8 +373,8 @@ private: /// modifier is applied twice, the position of the variable needs to be restored /// after the nested modifier is left. std::map> m_localVariables; - /// List of current inheritance hierarchy from derived to base. - std::vector m_inheritanceHierarchy; + /// The contract currently being compiled. Virtual function lookup starts from this contarct. + ContractDefinition const* m_mostDerivedContract = nullptr; /// Stack of current visited AST nodes, used for location attachment std::stack m_visitedNodes; /// The runtime context if in Creation mode, this is used for generating tags that would be stored into the storage and then used at runtime. diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index d5f5ffe33..50fb030d2 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -129,7 +129,7 @@ void ContractCompiler::initializeContext( { m_context.setExperimentalFeatures(_contract.sourceUnit().annotation().experimentalFeatures); m_context.setOtherCompilers(_otherCompilers); - m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); + m_context.setMostDerivedContract(_contract); if (m_runtimeCompiler) registerImmutableVariables(_contract); CompilerUtils(m_context).initialiseFreeMemoryPointer(); @@ -689,7 +689,7 @@ bool ContractCompiler::visit(InlineAssembly const& _inlineAssembly) if (FunctionDefinition const* functionDef = dynamic_cast(decl)) { solAssert(!ref->second.isOffset && !ref->second.isSlot, ""); - functionDef = &m_context.resolveVirtualFunction(*functionDef); + functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract()); auto functionEntryLabel = m_context.functionEntryLabel(*functionDef).pushTag(); solAssert(functionEntryLabel.data() <= std::numeric_limits::max(), ""); _assembly.appendLabelReference(size_t(functionEntryLabel.data())); @@ -1335,10 +1335,9 @@ void ContractCompiler::appendModifierOrFunctionCode() appendModifierOrFunctionCode(); else { - ModifierDefinition const& nonVirtualModifier = dynamic_cast( + ModifierDefinition const& modifier = dynamic_cast( *modifierInvocation->name()->annotation().referencedDeclaration - ); - ModifierDefinition const& modifier = m_context.resolveVirtualFunctionModifier(nonVirtualModifier); + ).resolveVirtual(m_context.mostDerivedContract()); CompilerContext::LocationSetter locationSetter(m_context, modifier); std::vector> const& modifierArguments = modifierInvocation->arguments() ? *modifierInvocation->arguments() : std::vector>(); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index c71d067c4..58992426b 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -572,7 +572,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // Do not directly visit the identifier, because this way, we can avoid // the runtime entry label to be created at the creation time context. CompilerContext::LocationSetter locationSetter2(m_context, *identifier); - utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef), false); + utils().pushCombinedFunctionEntryLabel( + functionDef->resolveVirtual(m_context.mostDerivedContract()), + false + ); shortcutTaken = true; } } @@ -1861,7 +1864,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) // we want to avoid having a reference to the runtime function entry point in the // constructor context, since this would force the compiler to include unreferenced // internal functions in the runtime contex. - utils().pushCombinedFunctionEntryLabel(m_context.resolveVirtualFunction(*functionDef)); + utils().pushCombinedFunctionEntryLabel(functionDef->resolveVirtual(m_context.mostDerivedContract())); else if (auto variable = dynamic_cast(declaration)) appendVariable(*variable, static_cast(_identifier)); else if (auto contract = dynamic_cast(declaration)) diff --git a/libsolidity/codegen/ir/IRGenerationContext.cpp b/libsolidity/codegen/ir/IRGenerationContext.cpp index 7184247f4..288da5937 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.cpp +++ b/libsolidity/codegen/ir/IRGenerationContext.cpp @@ -31,6 +31,12 @@ using namespace solidity; using namespace solidity::util; using namespace solidity::frontend; +ContractDefinition const& IRGenerationContext::mostDerivedContract() const +{ + solAssert(m_mostDerivedContract, "Most derived contract requested but not set."); + return *m_mostDerivedContract; +} + IRVariable const& IRGenerationContext::addLocalVariable(VariableDeclaration const& _varDecl) { auto const& [it, didInsert] = m_localVariables.emplace( @@ -70,26 +76,9 @@ string IRGenerationContext::functionName(VariableDeclaration const& _varDecl) return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id()); } -FunctionDefinition const& IRGenerationContext::virtualFunction(FunctionDefinition const& _function) -{ - // @TODO previously, we had to distinguish creation context and runtime context, - // but since we do not work with jump positions anymore, this should not be a problem, right? - string name = _function.name(); - FunctionType functionType(_function); - for (auto const& contract: m_inheritanceHierarchy) - for (FunctionDefinition const* function: contract->definedFunctions()) - if ( - function->name() == name && - !function->isConstructor() && - FunctionType(*function).asCallableFunction(false)->hasEqualParameterTypes(functionType) - ) - return *function; - solAssert(false, "Super function " + name + " not found."); -} - string IRGenerationContext::virtualFunctionName(FunctionDefinition const& _functionDeclaration) { - return functionName(virtualFunction(_functionDeclaration)); + return functionName(_functionDeclaration.resolveVirtual(mostDerivedContract())); } string IRGenerationContext::newYulVariable() @@ -120,7 +109,7 @@ string IRGenerationContext::internalDispatch(size_t _in, size_t _out) templ("arrow", _out > 0 ? "->" : ""); templ("out", suffixedVariableNameList("out_", 0, _out)); vector> functions; - for (auto const& contract: m_inheritanceHierarchy) + for (auto const& contract: mostDerivedContract().annotation().linearizedBaseContracts) for (FunctionDefinition const* function: contract->definedFunctions()) if ( !function->isConstructor() && diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h index 473b62482..b0fc92cdb 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.h +++ b/libsolidity/codegen/ir/IRGenerationContext.h @@ -61,11 +61,12 @@ public: MultiUseYulFunctionCollector& functionCollector() { return m_functions; } - /// Sets the current inheritance hierarchy from derived to base. - void setInheritanceHierarchy(std::vector _hierarchy) + /// Sets the most derived contract (the one currently being compiled)> + void setMostDerivedContract(ContractDefinition const& _mostDerivedContract) { - m_inheritanceHierarchy = std::move(_hierarchy); + m_mostDerivedContract = &_mostDerivedContract; } + ContractDefinition const& mostDerivedContract() const; IRVariable const& addLocalVariable(VariableDeclaration const& _varDecl); @@ -81,7 +82,6 @@ public: std::string functionName(FunctionDefinition const& _function); std::string functionName(VariableDeclaration const& _varDecl); - FunctionDefinition const& virtualFunction(FunctionDefinition const& _functionDeclaration); std::string virtualFunctionName(FunctionDefinition const& _functionDeclaration); std::string newYulVariable(); @@ -103,7 +103,7 @@ private: langutil::EVMVersion m_evmVersion; RevertStrings m_revertStrings; OptimiserSettings m_optimiserSettings; - std::vector m_inheritanceHierarchy; + ContractDefinition const* m_mostDerivedContract = nullptr; std::map m_localVariables; /// Storage offsets of state variables std::map> m_stateVariables; diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 9c7d9c8ac..4ca00befb 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -110,7 +110,7 @@ string IRGenerator::generate(ContractDefinition const& _contract) t("functions", m_context.functionCollector().requestedFunctions()); resetContext(_contract); - m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); + m_context.setMostDerivedContract(_contract); t("RuntimeObject", runtimeObjectName(_contract)); t("dispatch", dispatchRoutine(_contract)); for (auto const* contract: _contract.annotation().linearizedBaseContracts) @@ -389,7 +389,7 @@ void IRGenerator::resetContext(ContractDefinition const& _contract) ); m_context = IRGenerationContext(m_evmVersion, m_context.revertStrings(), m_optimiserSettings); - m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts); + m_context.setMostDerivedContract(_contract); for (auto const& var: ContractType(_contract).stateVariables()) m_context.addStateVariable(*get<0>(var), get<1>(var), get<2>(var)); } diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 1d40c35b1..b5d9589c0 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1165,7 +1165,7 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) return; } else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) - define(_identifier) << to_string(m_context.virtualFunction(*functionDef).id()) << "\n"; + define(_identifier) << to_string(functionDef->resolveVirtual(m_context.mostDerivedContract()).id()) << "\n"; else if (VariableDeclaration const* varDecl = dynamic_cast(declaration)) { // TODO for the constant case, we have to be careful: diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index cb0066bee..cdaee104b 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -119,13 +119,9 @@ bytes compileFirstExpression( NameAndTypeResolver resolver(globalContext, solidity::test::CommonOptions::get().evmVersion(), scopes, errorReporter); resolver.registerDeclarations(*sourceUnit); - vector inheritanceHierarchy; for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) - { BOOST_REQUIRE_MESSAGE(resolver.resolveNamesAndTypes(*contract), "Resolving names failed"); - inheritanceHierarchy = vector(1, contract); - } for (ASTPointer const& node: sourceUnit->nodes()) if (ContractDefinition* contract = dynamic_cast(node.get())) { @@ -144,7 +140,7 @@ bytes compileFirstExpression( RevertStrings::Default ); context.resetVisitedNodes(contract); - context.setInheritanceHierarchy(inheritanceHierarchy); + context.setMostDerivedContract(*contract); unsigned parametersSize = _localVariables.size(); // assume they are all one slot on the stack context.adjustStackOffset(parametersSize); for (vector const& variable: _localVariables) From c184844932a468b5d7b3ce1a2d2351c11fc74aea Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Thu, 26 Mar 2020 18:57:43 -0400 Subject: [PATCH 35/76] Remove forward declaration of class Population --- tools/yulPhaser/Population.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/tools/yulPhaser/Population.h b/tools/yulPhaser/Population.h index 151474171..40d51498b 100644 --- a/tools/yulPhaser/Population.h +++ b/tools/yulPhaser/Population.h @@ -29,13 +29,6 @@ namespace solidity::phaser { -class Population; - -} - -namespace solidity::phaser -{ - class PairSelection; class Selection; From 89d5ecdd24973692fc25a74854a0bc21dff2581d Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Mon, 23 Mar 2020 16:04:36 +0100 Subject: [PATCH 36/76] [Sol2Yul] Adding support for constructor with parameters --- libsolidity/codegen/ir/IRGenerator.cpp | 23 ++++++++++++++++--- .../semanticTests/constructor_with_params.sol | 15 ++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/semanticTests/constructor_with_params.sol diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 4ca00befb..2aff9c3f8 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -259,11 +259,28 @@ string IRGenerator::constructorCode(ContractDefinition const& _contract) if (constructor) { - solUnimplementedAssert(constructor->parameters().empty(), ""); + ABIFunctions abiFunctions(m_evmVersion, m_context.revertStrings(), m_context.functionCollector()); + unsigned paramVars = make_shared(constructor->functionType(false)->parameterTypes())->sizeOnStack(); - // TODO base constructors + Whiskers t(R"X( + let programSize := datasize("") + let argSize := sub(codesize(), programSize) - out << m_context.functionName(*constructor) + "()\n"; + let memoryDataOffset := (argSize) + codecopy(memoryDataOffset, programSize, argSize) + + (memoryDataOffset, add(memoryDataOffset, argSize)) + + () + )X"); + t("object", creationObjectName(_contract)); + t("allocate", m_utils.allocationFunction()); + t("assignToParams", paramVars == 0 ? "" : "let " + suffixedVariableNameList("param_", 0, paramVars) + " := "); + t("params", suffixedVariableNameList("param_", 0, paramVars)); + t("abiDecode", abiFunctions.tupleDecoder(constructor->functionType(false)->parameterTypes(), true)); + t("constructorName", m_context.functionName(*constructor)); + + out << t.render(); } return out.str(); diff --git a/test/libsolidity/semanticTests/constructor_with_params.sol b/test/libsolidity/semanticTests/constructor_with_params.sol new file mode 100644 index 000000000..19f0bdb57 --- /dev/null +++ b/test/libsolidity/semanticTests/constructor_with_params.sol @@ -0,0 +1,15 @@ +contract C { + uint public i; + uint public k; + + constructor(uint newI, uint newK) public { + i = newI; + k = newK; + } +} +// ==== +// compileViaYul: also +// ---- +// constructor(): 2, 0 -> +// i() -> 2 +// k() -> 0 From 9cc967eb3ab3bad029fcbcf0da0f1f61d0d81525 Mon Sep 17 00:00:00 2001 From: Christian Parpart Date: Mon, 30 Mar 2020 11:35:34 +0200 Subject: [PATCH 37/76] Removing grammar.txt in favor of Solidity.g4 (ANTLR based grammar) --- docs/grammar.txt | 193 ----------------------------------------- docs/miscellaneous.rst | 4 +- 2 files changed, 2 insertions(+), 195 deletions(-) delete mode 100644 docs/grammar.txt diff --git a/docs/grammar.txt b/docs/grammar.txt deleted file mode 100644 index b8b2893a2..000000000 --- a/docs/grammar.txt +++ /dev/null @@ -1,193 +0,0 @@ -SourceUnit = (PragmaDirective | ImportDirective | ContractDefinition)* - -// Pragma actually parses anything up to the trailing ';' to be fully forward-compatible. -PragmaDirective = 'pragma' Identifier ([^;]+) ';' - -ImportDirective = 'import' StringLiteral ('as' Identifier)? ';' - | 'import' ('*' | Identifier) ('as' Identifier)? 'from' StringLiteral ';' - | 'import' '{' Identifier ('as' Identifier)? ( ',' Identifier ('as' Identifier)? )* '}' 'from' StringLiteral ';' - -ContractDefinition = 'abstract'? ( 'contract' | 'library' | 'interface' ) Identifier - ( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )? - '{' ContractPart* '}' - -ContractPart = StateVariableDeclaration | UsingForDeclaration - | StructDefinition | ModifierDefinition | FunctionDefinition | EventDefinition | EnumDefinition - -InheritanceSpecifier = UserDefinedTypeName ( '(' Expression ( ',' Expression )* ')' )? - -StateVariableDeclaration = TypeName ( 'public' | 'internal' | 'private' | 'constant' | OverrideSpecifier )* Identifier ('=' Expression)? ';' -UsingForDeclaration = 'using' Identifier 'for' ('*' | TypeName) ';' -StructDefinition = 'struct' Identifier '{' - ( VariableDeclaration ';' (VariableDeclaration ';')* ) '}' - -ModifierDefinition = 'modifier' Identifier ParameterList? ( 'virtual' | OverrideSpecifier )* Block -ModifierInvocation = Identifier ( '(' ExpressionList? ')' )? - -FunctionDefinition = FunctionDescriptor ParameterList - ( ModifierInvocation | StateMutability | 'external' | 'public' | 'internal' | 'private' | 'virtual' | OverrideSpecifier )* - ( 'returns' ParameterList )? ( ';' | Block ) - -FunctionDescriptor = 'function' Identifier | 'constructor' | 'fallback' | 'receive' - -OverrideSpecifier = 'override' ( '(' UserDefinedTypeName (',' UserDefinedTypeName)* ')' )? - -EventDefinition = 'event' Identifier EventParameterList 'anonymous'? ';' - -EnumValue = Identifier -EnumDefinition = 'enum' Identifier '{' EnumValue? (',' EnumValue)* '}' - -ParameterList = '(' ( Parameter (',' Parameter)* )? ')' -Parameter = TypeName StorageLocation? Identifier? - -EventParameterList = '(' ( EventParameter (',' EventParameter )* )? ')' -EventParameter = TypeName 'indexed'? Identifier? - -FunctionTypeParameterList = '(' ( FunctionTypeParameter (',' FunctionTypeParameter )* )? ')' -FunctionTypeParameter = TypeName StorageLocation? - -// semantic restriction: mappings and structs (recursively) containing mappings -// are not allowed in argument lists -VariableDeclaration = TypeName StorageLocation? Identifier - -TypeName = ElementaryTypeName - | UserDefinedTypeName - | Mapping - | ArrayTypeName - | FunctionTypeName - | ( 'address' 'payable' ) - -UserDefinedTypeName = Identifier ( '.' Identifier )* - -Mapping = 'mapping' '(' ( ElementaryTypeName | UserDefinedTypeName ) '=>' TypeName ')' -ArrayTypeName = TypeName '[' Expression? ']' -FunctionTypeName = 'function' FunctionTypeParameterList ( 'internal' | 'external' | StateMutability )* - ( 'returns' FunctionTypeParameterList )? -StorageLocation = 'memory' | 'storage' | 'calldata' -StateMutability = 'pure' | 'view' | 'payable' - -Block = '{' Statement* '}' -Statement = IfStatement | TryStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement | - ( DoWhileStatement | PlaceholderStatement | Continue | Break | Return | - Throw | EmitStatement | SimpleStatement ) ';' - -ExpressionStatement = Expression -IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? -TryStatement = 'try' Expression ( 'returns' ParameterList )? Block CatchClause+ -CatchClause = 'catch' ( Identifier? ParameterList )? Block -WhileStatement = 'while' '(' Expression ')' Statement -PlaceholderStatement = '_' -SimpleStatement = VariableDefinition | ExpressionStatement -ForStatement = 'for' '(' (SimpleStatement)? ';' (Expression)? ';' (ExpressionStatement)? ')' Statement -InlineAssemblyStatement = 'assembly' StringLiteral? AssemblyBlock -DoWhileStatement = 'do' Statement 'while' '(' Expression ')' -Continue = 'continue' -Break = 'break' -Return = 'return' Expression? -Throw = 'throw' -EmitStatement = 'emit' FunctionCall -VariableDefinition = (VariableDeclaration | '(' VariableDeclaration? (',' VariableDeclaration? )* ')' ) ( '=' Expression )? - -// Precedence by order (see github.com/ethereum/solidity/pull/732) -Expression - = Expression ('++' | '--') - | NewExpression - | IndexAccess - | IndexRangeAccess - | MemberAccess - | FunctionCall - | Expression '{' NameValueList '}' - | '(' Expression ')' - | ('!' | '~' | 'delete' | '++' | '--' | '+' | '-') Expression - | Expression '**' Expression - | Expression ('*' | '/' | '%') Expression - | Expression ('+' | '-') Expression - | Expression ('<<' | '>>') Expression - | Expression '&' Expression - | Expression '^' Expression - | Expression '|' Expression - | Expression ('<' | '>' | '<=' | '>=') Expression - | Expression ('==' | '!=') Expression - | Expression '&&' Expression - | Expression '||' Expression - | Expression '?' Expression ':' Expression - | Expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' | '+=' | '-=' | '*=' | '/=' | '%=') Expression - | PrimaryExpression - -PrimaryExpression = BooleanLiteral - | NumberLiteral - | HexLiteral - | StringLiteral - | TupleExpression - | Identifier - | ElementaryTypeNameExpression - -ExpressionList = Expression ( ',' Expression )* -NameValueList = Identifier ':' Expression ( ',' Identifier ':' Expression )* - -FunctionCall = Expression '(' FunctionCallArguments ')' -FunctionCallArguments = '{' NameValueList? '}' - | ExpressionList? - -NewExpression = 'new' TypeName -MemberAccess = Expression '.' Identifier -IndexAccess = Expression '[' Expression? ']' -IndexRangeAccess = Expression '[' Expression? ':' Expression? ']' - -BooleanLiteral = 'true' | 'false' -NumberLiteral = ( HexNumber | DecimalNumber ) (' ' NumberUnit)? -NumberUnit = 'wei' | 'szabo' | 'finney' | 'ether' - | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years' -HexLiteral = 'hex' ('"' ([0-9a-fA-F]{2})* '"' | '\'' ([0-9a-fA-F]{2})* '\'') -StringLiteral = '"' ([^"\r\n\\] | '\\' .)* '"' -Identifier = [a-zA-Z_$] [a-zA-Z_$0-9]* - -HexNumber = '0x' [0-9a-fA-F]+ -DecimalNumber = [0-9]+ ( '.' [0-9]* )? ( [eE] [0-9]+ )? - -TupleExpression = '(' ( Expression? ( ',' Expression? )* )? ')' - | '[' ( Expression ( ',' Expression )* )? ']' - -ElementaryTypeNameExpression = ElementaryTypeName - -ElementaryTypeName = 'address' | 'bool' | 'string' | Int | Uint | Byte | Fixed | Ufixed - -Int = 'int' | 'int8' | 'int16' | 'int24' | 'int32' | 'int40' | 'int48' | 'int56' | 'int64' | 'int72' | 'int80' | 'int88' | 'int96' | 'int104' | 'int112' | 'int120' | 'int128' | 'int136' | 'int144' | 'int152' | 'int160' | 'int168' | 'int176' | 'int184' | 'int192' | 'int200' | 'int208' | 'int216' | 'int224' | 'int232' | 'int240' | 'int248' | 'int256' - -Uint = 'uint' | 'uint8' | 'uint16' | 'uint24' | 'uint32' | 'uint40' | 'uint48' | 'uint56' | 'uint64' | 'uint72' | 'uint80' | 'uint88' | 'uint96' | 'uint104' | 'uint112' | 'uint120' | 'uint128' | 'uint136' | 'uint144' | 'uint152' | 'uint160' | 'uint168' | 'uint176' | 'uint184' | 'uint192' | 'uint200' | 'uint208' | 'uint216' | 'uint224' | 'uint232' | 'uint240' | 'uint248' | 'uint256' - -Byte = 'byte' | 'bytes' | 'bytes1' | 'bytes2' | 'bytes3' | 'bytes4' | 'bytes5' | 'bytes6' | 'bytes7' | 'bytes8' | 'bytes9' | 'bytes10' | 'bytes11' | 'bytes12' | 'bytes13' | 'bytes14' | 'bytes15' | 'bytes16' | 'bytes17' | 'bytes18' | 'bytes19' | 'bytes20' | 'bytes21' | 'bytes22' | 'bytes23' | 'bytes24' | 'bytes25' | 'bytes26' | 'bytes27' | 'bytes28' | 'bytes29' | 'bytes30' | 'bytes31' | 'bytes32' - -Fixed = 'fixed' | ( 'fixed' [0-9]+ 'x' [0-9]+ ) - -Ufixed = 'ufixed' | ( 'ufixed' [0-9]+ 'x' [0-9]+ ) - - -AssemblyBlock = '{' AssemblyStatement* '}' - -AssemblyStatement = AssemblyBlock - | AssemblyFunctionDefinition - | AssemblyVariableDeclaration - | AssemblyAssignment - | AssemblyIf - | AssemblyExpression - | AssemblySwitch - | AssemblyForLoop - | AssemblyBreakContinue - | AssemblyLeave -AssemblyFunctionDefinition = - 'function' Identifier '(' AssemblyIdentifierList? ')' - ( '->' AssemblyIdentifierList )? AssemblyBlock -AssemblyVariableDeclaration = 'let' AssemblyIdentifierList ( ':=' AssemblyExpression )? -AssemblyAssignment = AssemblyIdentifierList ':=' AssemblyExpression -AssemblyExpression = AssemblyFunctionCall | Identifier | Literal -AssemblyIf = 'if' AssemblyExpression AssemblyBlock -AssemblySwitch = 'switch' AssemblyExpression ( AssemblyCase+ AssemblyDefault? | AssemblyDefault ) -AssemblyCase = 'case' Literal AssemblyBlock -AssemblyDefault = 'default' AssemblyBlock -AssemblyForLoop = 'for' AssemblyBlock AssemblyExpression AssemblyBlock AssemblyBlock -AssemblyBreakContinue = 'break' | 'continue' -AssemblyLeave = 'leave' -AssemblyFunctionCall = Identifier '(' ( AssemblyExpression ( ',' AssemblyExpression )* )? ')' - -AssemblyIdentifierList = Identifier ( ',' Identifier )* diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 085bf99e7..b4082fced 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -809,5 +809,5 @@ These keywords are reserved in Solidity. They might become part of the syntax in Language Grammar ================ -.. literalinclude:: grammar.txt - :language: none +.. literalinclude:: Solidity.g4 + :language: antlr From c002cae6914a7d5d8d48450e55f12c290e321418 Mon Sep 17 00:00:00 2001 From: a3d4 Date: Mon, 30 Mar 2020 15:18:51 +0200 Subject: [PATCH 38/76] Fix #8450. Prevented internal compiler errors when assigning nested tuples. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 4 ++-- .../semanticTests/types/tuple_in_tuple.sol | 24 +++++++++++++++++++ .../tupleAssignments/tuple_in_tuple_long.sol | 12 ++++++++++ .../tupleAssignments/tuple_in_tuple_short.sol | 6 +++++ 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/semanticTests/types/tuple_in_tuple.sol create mode 100644 test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_long.sol create mode 100644 test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_short.sol diff --git a/Changelog.md b/Changelog.md index 96f3a1444..f269e049f 100644 --- a/Changelog.md +++ b/Changelog.md @@ -10,6 +10,7 @@ Compiler Features: Bugfixes: * Inline Assembly: Fix internal error when accessing invalid constant variables. * Reference Resolver: Fix internal error when accessing invalid struct members. + * Type Checker: Fix internal errors when assigning nested tuples. * Inheritance: Allow public state variables to override functions with dynamic memory types in their return values. * JSON AST: Always add pointer suffix for memory reference types. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 1345fbf90..784701d22 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1317,11 +1317,11 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const& if (auto const* tupleExpression = dynamic_cast(&_expression)) { auto const* tupleType = dynamic_cast(&_type); - auto const& types = tupleType ? tupleType->components() : vector { &_type }; + auto const& types = tupleType && tupleExpression->components().size() > 1 ? tupleType->components() : vector { &_type }; solAssert( tupleExpression->components().size() == types.size() || m_errorReporter.hasErrors(), - "Array sizes don't match or no errors generated." + "Array sizes don't match and no errors generated." ); for (size_t i = 0; i < min(tupleExpression->components().size(), types.size()); i++) diff --git a/test/libsolidity/semanticTests/types/tuple_in_tuple.sol b/test/libsolidity/semanticTests/types/tuple_in_tuple.sol new file mode 100644 index 000000000..d9737a807 --- /dev/null +++ b/test/libsolidity/semanticTests/types/tuple_in_tuple.sol @@ -0,0 +1,24 @@ +contract test { + function f0() public returns(int, bool) { + int a; + bool b; + ((a, b)) = (2, true); + return (a, b); + } + function f1() public returns(int) { + int a; + (((a, ), )) = ((1, 2) ,3); + return a; + } + function f2() public returns(int) { + int a; + (((, a),)) = ((1, 2), 3); + return a; + } +} +// ==== +// compileViaYul: also +// ---- +// f0() -> 2, true +// f1() -> 1 +// f2() -> 2 diff --git a/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_long.sol b/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_long.sol new file mode 100644 index 000000000..2fa48cfb1 --- /dev/null +++ b/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_long.sol @@ -0,0 +1,12 @@ +contract C { + function f() { + (((((((((((,2),)),)),),))=4))); + } +} +// ---- +// SyntaxError: (15-69): No visibility specified. Did you intend to add "public"? +// TypeError: (46-47): Expression has to be an lvalue. +// TypeError: (60-61): Type int_const 4 is not implicitly convertible to expected type tuple(tuple(tuple(tuple(tuple(,int_const 2),),),),). +// TypeError: (37-61): Tuple component cannot be empty. +// TypeError: (36-62): Tuple component cannot be empty. +// TypeError: (35-63): Tuple component cannot be empty. diff --git a/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_short.sol b/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_short.sol new file mode 100644 index 000000000..2a5d62752 --- /dev/null +++ b/test/libsolidity/syntaxTests/tupleAssignments/tuple_in_tuple_short.sol @@ -0,0 +1,6 @@ +contract C { + function f() public pure { + int a; + (((a,),)) = ((1,2),3); + } +} From 64ae889dd71b7ed70a221c8eda879c67836b4c57 Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Sun, 29 Mar 2020 00:02:44 +0100 Subject: [PATCH 39/76] Enabling pragma with > in minimum version check --- .circleci/config.yml | 14 ++++ docs/examples/blind-auction.rst | 2 +- docs/using-the-compiler.rst | 4 +- scripts/docs_version_pragma_check.sh | 120 +++++++++++++++++++++++---- 4 files changed, 123 insertions(+), 17 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ac1dda7dd..9ccf7a663 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -651,6 +651,19 @@ jobs: SOLTEST_FLAGS: --no-smt ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2 + t_ubu_pragma_docs_test: &t_ubu_pragma_docs_test + docker: + - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >> + environment: + TERM: xterm + steps: + - checkout + - attach_workspace: + at: build + - run: *run_docs_version_pragma_check + - store_test_results: *store_test_results + - store_artifacts: *artifacts_test_results + t_ems_solcjs: docker: - image: ethereum/solidity-buildpack-deps:ubuntu1904 @@ -802,6 +815,7 @@ workflows: - b_ubu: *workflow_trigger_on_tags - b_ubu18: *workflow_trigger_on_tags - t_ubu_cli: *workflow_ubuntu1904 + - t_ubu_pragma_docs_test: *workflow_ubuntu1904 - t_ubu_soltest: *workflow_ubuntu1904 - b_ubu_clang: *workflow_trigger_on_tags - t_ubu_clang_soltest: *workflow_ubuntu1904_clang diff --git a/docs/examples/blind-auction.rst b/docs/examples/blind-auction.rst index 4461bfc65..f2034272a 100644 --- a/docs/examples/blind-auction.rst +++ b/docs/examples/blind-auction.rst @@ -184,7 +184,7 @@ invalid bids. :: - pragma solidity >0.4.23 <0.7.0; + pragma solidity >=0.5.0 <0.7.0; contract BlindAuction { struct Bid { diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 12fe4b3f9..8d647a4a7 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -605,8 +605,8 @@ Assume you have the following contracts you want to update declared in ``Source. .. code-block:: none - // This will not compile - pragma solidity >0.4.23; + // This will not compile after 0.5.0 + pragma solidity >0.4.23 <0.5.0; contract Updateable { function run() public view returns (bool); diff --git a/scripts/docs_version_pragma_check.sh b/scripts/docs_version_pragma_check.sh index a66798841..c56bdc6c9 100755 --- a/scripts/docs_version_pragma_check.sh +++ b/scripts/docs_version_pragma_check.sh @@ -32,6 +32,97 @@ SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-build} source "${REPO_ROOT}/scripts/common.sh" source "${REPO_ROOT}/scripts/common_cmdline.sh" +function versionGreater() +{ + v1=$1 + v2=$2 + ver1=( ${v1//./ } ) + ver2=( ${v2//./ } ) + + if (( ${ver1[0]} > ${ver2[0]} )) + then + return 0 + elif (( ${ver1[0]} == ${ver2[0]} )) && (( ${ver1[1]} > ${ver2[1]} )) + then + return 0 + elif (( ${ver1[0]} == ${ver2[0]} )) && (( ${ver1[1]} == ${ver2[1]} )) && (( ${ver1[2]} > ${ver2[2]} )) + then + return 0 + fi + return 1 +} + +function versionEqual() +{ + if [ "$1" == "$2" ] + then + return 0 + fi + return 1 +} + +function getAllAvailableVersions() +{ + allVersions=() + local allListedVersions=( $( + wget -q -O- https://ethereum.github.io/solc-bin/bin/list.txt | + grep -Po '(?<=soljson-v)\d+.\d+.\d+(?=\+commit)' | + sort -V + ) ) + for listed in "${allListedVersions[@]}" + do + if versionGreater "$listed" "0.4.10" + then + allVersions+=( $listed ) + fi + done +} + +function findMinimalVersion() +{ + local f=$1 + local greater=false + local pragmaVersion + + # Get minimum compiler version defined by pragma + if (grep -Po '(?<=pragma solidity >=)\d+.\d+.\d+' "$f" >/dev/null) + then + pragmaVersion="$(grep -Po '(?<=pragma solidity >=)\d+.\d+.\d+' "$f")" + sign=">=" + elif (grep -Po '(?<=pragma solidity \^)\d+.\d+.\d+' "$f" >/dev/null) + then + pragmaVersion="$(grep -Po '(?<=pragma solidity \^)\d+.\d+.\d+' "$f")" + sign="^" + elif (grep -Po '(?<=pragma solidity >)\d+.\d+.\d+' "$f" >/dev/null) + then + pragmaVersion="$(grep -Po '(?<=pragma solidity >)\d+.\d+.\d+' "$f")" + sign=">" + greater=true; + else + printError "No valid pragma statement in file. Skipping..." + return + fi + + version="" + for ver in "${allVersions[@]}" + do + if versionGreater "$ver" "$pragmaVersion" + then + minVersion="$ver" + break + elif ([ $greater == false ]) && versionEqual "$ver" "$pragmaVersion" + then + version="$ver" + break + fi + done + + if [ -z version ] + then + printError "No release $sign$pragmaVersion was listed in available releases!" + fi +} + printTask "Verifying that all examples from the documentation have the correct version range..." SOLTMPDIR=$(mktemp -d) ( @@ -39,6 +130,8 @@ SOLTMPDIR=$(mktemp -d) cd "$SOLTMPDIR" "$REPO_ROOT"/scripts/isolate_tests.py "$REPO_ROOT"/docs/ docs + getAllAvailableVersions + for f in *.sol do # The contributors guide uses syntax tests, but we cannot @@ -61,27 +154,26 @@ SOLTMPDIR=$(mktemp -d) # ignore warnings in this case opts="$opts -o" - # Get minimum compiler version defined by pragma - if (grep -Po '(?<=pragma solidity >=)\d+.\d+.\d+' "$f" >/dev/null); then - version="$(grep -Po '(?<=pragma solidity >=)\d+.\d+.\d+' "$f")" - if (echo $version | grep -Po '(?<=0.4.)\d+' >/dev/null); then - patch=$(echo $version | grep -Po '(?<=0.4.)\d+') - if (( patch < 11 )); then - version="0.4.11" # first available release on github - fi - fi - elif (grep -Po '(?<=pragma solidity \^)\d+.\d+.\d+' "$f" >/dev/null); then - version="$(grep -Po '(?<=pragma solidity \^)\d+.\d+.\d+' "$f")" + findMinimalVersion $f + if [ -z "$version" ] + then + continue fi opts="$opts -v $version" solc_bin="solc-$version" echo "$solc_bin" - if [[ ! -f "$solc_bin" ]]; then + if [[ ! -f "$solc_bin" ]] + then echo "Downloading release from github..." - wget https://github.com/ethereum/solidity/releases/download/v$version/solc-static-linux - mv solc-static-linux $solc_bin + if wget -q https://github.com/ethereum/solidity/releases/download/v$version/solc-static-linux >/dev/null + then + mv solc-static-linux $solc_bin + else + printError "No release $version was found on github!" + continue + fi fi ln -sf "$solc_bin" "solc" From 3e649eb8e1b85020de3aea52eb53e57d0c1743cb Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Mon, 30 Mar 2020 20:01:50 -0500 Subject: [PATCH 40/76] Disallow access to functions from inline assembly. --- Changelog.md | 1 + libsolidity/analysis/TypeChecker.cpp | 2 ++ .../inlineAssembly/assignment_from_functiontype.sol | 2 ++ .../inlineAssembly/assignment_from_functiontype2.sol | 10 ++++++++++ 4 files changed, 15 insertions(+) create mode 100644 test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype2.sol diff --git a/Changelog.md b/Changelog.md index 4d132d684..b512f9eb2 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,7 @@ Compiler Features: Bugfixes: * Inline Assembly: Fix internal error when accessing invalid constant variables. + * Inline Assembly: Fix internal error when accessing functions. * Reference Resolver: Fix internal error when accessing invalid struct members. * Type Checker: Fix internal errors when assigning nested tuples. * Inheritance: Allow public state variables to override functions with dynamic memory types in their return values. diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 784701d22..8067580c8 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -740,6 +740,8 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) solAssert(!!declaration->type(), "Type of declaration required but not yet determined."); if (dynamic_cast(declaration)) { + m_errorReporter.declarationError(_identifier.location, "Access to functions is not allowed in inline assembly."); + return size_t(-1); } else if (dynamic_cast(declaration)) { diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol index ecda3e994..625502f25 100644 --- a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype.sol @@ -5,3 +5,5 @@ contract C { } } } +// ---- +// DeclarationError: (72-73): Access to functions is not allowed in inline assembly. diff --git a/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype2.sol b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype2.sol new file mode 100644 index 000000000..2fc49b1db --- /dev/null +++ b/test/libsolidity/syntaxTests/inlineAssembly/assignment_from_functiontype2.sol @@ -0,0 +1,10 @@ +contract C { + function f() public pure {} + constructor() public { + assembly { + let x := f + } + } +} +// ---- +// DeclarationError: (112-113): Access to functions is not allowed in inline assembly. From 9c4a02db0fb901cea81bca06e3f0b73f3c9e5f16 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 19 Mar 2020 17:57:55 +0100 Subject: [PATCH 41/76] Documentation for immutables. --- docs/contracts/constant-state-variables.rst | 73 ++++++++++++++++----- docs/miscellaneous.rst | 1 + docs/types/reference-types.rst | 6 ++ 3 files changed, 62 insertions(+), 18 deletions(-) diff --git a/docs/contracts/constant-state-variables.rst b/docs/contracts/constant-state-variables.rst index 33511f020..7a0a7380a 100644 --- a/docs/contracts/constant-state-variables.rst +++ b/docs/contracts/constant-state-variables.rst @@ -1,11 +1,49 @@ .. index:: ! constant -************************ -Constant State Variables -************************ +************************************** +Constant and Immutable State Variables +************************************** -State variables can be declared as ``constant``. In this case, they have to be -assigned from an expression which is a constant at compile time. Any expression +State variables can be declared as ``constant`` or ``immutable``. +In both cases, the variables cannot be modified after the contract has been constructed. +For ``constant`` variables, the value has to be fixed at compile-time, while +for ``immutable``, it can still be assigned at construction time. + +The compiler does not reserve a storage slot for these variables, and every occurrence is +replaced by the respective value. + +Not all types for constants and immutables are implemented at this time. The only supported types are +`strings `_ (only for constants) and `value types `_. + +:: + + pragma solidity >0.6.4 <0.7.0; + + contract C { + uint constant X = 32**22 + 8; + string constant TEXT = "abc"; + bytes32 constant MY_HASH = keccak256("abc"); + uint immutable decimals; + uint immutable maxBalance; + address immutable owner = msg.sender; + + constructor(uint _decimals, address _reference) public { + decimals = _decimals; + // Assignments to immutables can even access the environment. + maxBalance = _reference.balance; + } + + function isBalanceTooHigh(address _other) public view returns (bool) { + return _other.balance > maxBalance; + } + } + + +Constant +======== + +For ``constant`` variables, the value has to be a constant at compile time and it has to be +assigned where the variable is declared. Any expression that accesses storage, blockchain data (e.g. ``now``, ``address(this).balance`` or ``block.number``) or execution data (``msg.value`` or ``gasleft()``) or makes calls to external contracts is disallowed. Expressions @@ -18,18 +56,17 @@ The reason behind allowing side-effects on the memory allocator is that it should be possible to construct complex objects like e.g. lookup-tables. This feature is not yet fully usable. -The compiler does not reserve a storage slot for these variables, and every occurrence is -replaced by the respective constant expression (which might be computed to a single value by the optimizer). +Immutable +========= -Not all types for constants are implemented at this time. The only supported types are -value types and strings. +Variables declared as ``immutable`` are a bit less restricted than those +declared as ``constant``: Immutable variables can be assigned an arbitrary +value in the constructor of the contract or at the point of their declaration. +They cannot be read during construction time and can only be assigned once. -:: - - pragma solidity >=0.4.0 <0.7.0; - - contract C { - uint constant X = 32**22 + 8; - string constant TEXT = "abc"; - bytes32 constant MY_HASH = keccak256("abc"); - } +The contract creation code generated by the compiler will modify the +contract's runtime code before it is returned by replacing all references +to immutables by the values assigned to the them. This is important if +you are comparing the +runtime code generated by the compiler with the one actually stored in the +blockchain. diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index b4082fced..3c30fec86 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -788,6 +788,7 @@ Modifiers - ``view`` for functions: Disallows modification of state. - ``payable`` for functions: Allows them to receive Ether together with a call. - ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot. +- ``immutable`` for state variables: Allows exactly one assignment at construction time and is constant afterwards. Is stored in code. - ``anonymous`` for events: Does not store event signature as topic. - ``indexed`` for event parameters: Stores the parameter as topic. - ``virtual`` for functions and modifiers: Allows the function's or modifier's diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 275e8b575..1566a8859 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -124,6 +124,12 @@ Accessing an array past its end causes a failing assertion. Methods ``.push()`` to append a new element at the end of the array, where ``.push()`` appends a zero-initialized element and returns a reference to it. +.. index:: ! string, ! bytes + +.. _strings: + +.. _bytes: + ``bytes`` and ``strings`` as Arrays ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 2853cba3e5c98fe828be722391c4c5e6417fc4be Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 1 Apr 2020 12:13:32 +0200 Subject: [PATCH 42/76] Fix call to internalDispatch. --- libsolidity/codegen/ir/IRGeneratorForStatements.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index b5d9589c0..8a98e8d36 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -568,7 +568,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) } define(_functionCall) << - m_context.internalDispatch(functionType->parameterTypes().size(), functionType->returnParameterTypes().size()) << + m_context.internalDispatch( + TupleType(functionType->parameterTypes()).sizeOnStack(), + TupleType(functionType->returnParameterTypes()).sizeOnStack() + ) << "(" << IRVariable(_functionCall.expression()).part("functionIdentifier").name() << joinHumanReadablePrefixed(args) << From fe9f8d520cd17e0bf69e6ee507db05fa8a5bf833 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 1 Apr 2020 14:02:30 +0200 Subject: [PATCH 43/76] Restrict size for dynamic memory array creation. --- Changelog.md | 4 ++++ libsolidity/codegen/ExpressionCompiler.cpp | 6 +++++ .../array/create_memory_array_too_large.sol | 24 +++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 test/libsolidity/semanticTests/array/create_memory_array_too_large.sol diff --git a/Changelog.md b/Changelog.md index b512f9eb2..f20b05aa5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ ### 0.6.5 (unreleased) +Important Bugfixes: + * Code Generator: Restrict the size of dynamic memory arrays to 64 bits during creation at runtime fixing a possible overflow. + + Language Features: diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 58992426b..fed3751fa 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -995,6 +995,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // Fetch requested length. acceptAndConvert(*arguments[0], *TypeProvider::uint256()); + // Make sure we can allocate memory without overflow + m_context << u256(0xffffffffffffffff); + m_context << Instruction::DUP2; + m_context << Instruction::GT; + m_context.appendConditionalRevert(); + // Stack: requested_length utils().fetchFreeMemoryPointer(); diff --git a/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol b/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol new file mode 100644 index 000000000..c42f84862 --- /dev/null +++ b/test/libsolidity/semanticTests/array/create_memory_array_too_large.sol @@ -0,0 +1,24 @@ +contract C { + function f() public returns (uint256) { + uint256 l = 2**256 / 32; + // This used to work without causing an error. + uint256[] memory x = new uint256[](l); + uint256[] memory y = new uint256[](1); + x[1] = 42; + // This used to overwrite the value written above. + y[0] = 23; + return x[1]; + } + function g() public returns (uint256) { + uint256 l = 2**256 / 2 + 1; + // This used to work without causing an error. + uint16[] memory x = new uint16[](l); + uint16[] memory y = new uint16[](1); + x[2] = 42; + // This used to overwrite the value written above. + y[0] = 23; + return x[2]; + }} +// ---- +// f() -> FAILURE +// g() -> FAILURE From d343143be763a64c2ccbb8302a2d00353d13f64f Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Wed, 1 Apr 2020 18:16:47 +0200 Subject: [PATCH 44/76] [docs] Renaming CI job for documentation pragma version check --- .circleci/config.yml | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9ccf7a663..23da90b8f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -122,8 +122,8 @@ defaults: name: command line tests command: ./test/cmdlineTests.sh - - run_docs_version_pragma_check: &run_docs_version_pragma_check - name: docs version pragma check + - run_docs_pragma_min_version: &run_docs_pragma_min_version + name: docs pragma version check command: ./scripts/docs_version_pragma_check.sh - test_ubuntu1604_clang: &test_ubuntu1604_clang @@ -351,6 +351,15 @@ jobs: pip install --user z3-solver - run: *run_proofs + chk_docs_pragma_min_version: + docker: + - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >> + environment: + TERM: xterm + steps: + - checkout + - run: *run_docs_pragma_min_version + b_ubu_clang: &build_ubuntu1904_clang docker: - image: ethereum/solidity-buildpack-deps:ubuntu1904-clang-<< pipeline.parameters.ubuntu-1904-clang-docker-image-rev >> @@ -621,7 +630,6 @@ jobs: - attach_workspace: at: build - run: *run_cmdline_tests - - run: *run_docs_version_pragma_check - store_test_results: *store_test_results - store_artifacts: *artifacts_test_results @@ -651,19 +659,6 @@ jobs: SOLTEST_FLAGS: --no-smt ASAN_OPTIONS: check_initialization_order=true:detect_stack_use_after_return=true:strict_init_order=true:strict_string_checks=true:detect_invalid_pointer_pairs=2 - t_ubu_pragma_docs_test: &t_ubu_pragma_docs_test - docker: - - image: ethereum/solidity-buildpack-deps:ubuntu1904-<< pipeline.parameters.ubuntu-1904-docker-image-rev >> - environment: - TERM: xterm - steps: - - checkout - - attach_workspace: - at: build - - run: *run_docs_version_pragma_check - - store_test_results: *store_test_results - - store_artifacts: *artifacts_test_results - t_ems_solcjs: docker: - image: ethereum/solidity-buildpack-deps:ubuntu1904 @@ -799,6 +794,7 @@ workflows: - chk_proofs: *workflow_trigger_on_tags - chk_pylint: *workflow_trigger_on_tags - chk_antlr_grammar: *workflow_trigger_on_tags + - chk_docs_pragma_min_version: *workflow_trigger_on_tags # build-only - b_docs: *workflow_trigger_on_tags @@ -815,7 +811,6 @@ workflows: - b_ubu: *workflow_trigger_on_tags - b_ubu18: *workflow_trigger_on_tags - t_ubu_cli: *workflow_ubuntu1904 - - t_ubu_pragma_docs_test: *workflow_ubuntu1904 - t_ubu_soltest: *workflow_ubuntu1904 - b_ubu_clang: *workflow_trigger_on_tags - t_ubu_clang_soltest: *workflow_ubuntu1904_clang From 06d184712cef983fff519cf231ae9aed9131d2c9 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 1 Apr 2020 16:42:23 +0200 Subject: [PATCH 45/76] Add buglist entry. --- docs/bugs.json | 8 +++++ docs/bugs_by_version.json | 75 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/docs/bugs.json b/docs/bugs.json index 388bd4b56..066e17e93 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,12 @@ [ + { + "name": "MemoryArrayCreationOverflow", + "summary": "The creation of very large memory arrays can result in overlapping memory regions and thus memory corruption.", + "description": "No runtime overflow checks were performed for the length of memory arrays during creation. In cases for which the memory size of an array in bytes, i.e. the array length times 32, is larger than 2^256-1, the memory allocation will overflow, potentially resulting in overlapping memory areas. The length of the array is still stored correctly, so copying or iterating over such an array will result in out-of-gas.", + "introduced": "0.2.0", + "fixed": "0.6.5", + "severity": "low" + }, { "name": "YulOptimizerRedundantAssignmentBreakContinue", "summary": "The Yul optimizer can remove essential assignments to variables declared inside for loops when Yul's continue or break statement is used. You are unlikely to be affected if you do not use inline assembly with for loops and continue and break statements.", diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index d8daf565e..0d0f30c48 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -151,6 +151,7 @@ }, "0.2.0": { "bugs": [ + "MemoryArrayCreationOverflow", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", @@ -171,6 +172,7 @@ }, "0.2.1": { "bugs": [ + "MemoryArrayCreationOverflow", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", @@ -191,6 +193,7 @@ }, "0.2.2": { "bugs": [ + "MemoryArrayCreationOverflow", "ExpExponentCleanup", "NestedArrayFunctionCallDecoder", "ZeroFunctionSelector", @@ -211,6 +214,7 @@ }, "0.3.0": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -233,6 +237,7 @@ }, "0.3.1": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -254,6 +259,7 @@ }, "0.3.2": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -275,6 +281,7 @@ }, "0.3.3": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -295,6 +302,7 @@ }, "0.3.4": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -315,6 +323,7 @@ }, "0.3.5": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -335,6 +344,7 @@ }, "0.3.6": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -353,6 +363,7 @@ }, "0.4.0": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -371,6 +382,7 @@ }, "0.4.1": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -389,6 +401,7 @@ }, "0.4.10": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", @@ -405,6 +418,7 @@ }, "0.4.11": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", @@ -420,6 +434,7 @@ }, "0.4.12": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", @@ -434,6 +449,7 @@ }, "0.4.13": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", @@ -448,6 +464,7 @@ }, "0.4.14": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", @@ -461,6 +478,7 @@ }, "0.4.15": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", @@ -473,6 +491,7 @@ }, "0.4.16": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -487,6 +506,7 @@ }, "0.4.17": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -502,6 +522,7 @@ }, "0.4.18": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -516,6 +537,7 @@ }, "0.4.19": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -531,6 +553,7 @@ }, "0.4.2": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -548,6 +571,7 @@ }, "0.4.20": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -563,6 +587,7 @@ }, "0.4.21": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -578,6 +603,7 @@ }, "0.4.22": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -593,6 +619,7 @@ }, "0.4.23": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -607,6 +634,7 @@ }, "0.4.24": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -621,6 +649,7 @@ }, "0.4.25": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -633,6 +662,7 @@ }, "0.4.26": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -642,6 +672,7 @@ }, "0.4.3": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -658,6 +689,7 @@ }, "0.4.4": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "IncorrectEventSignatureInLibraries_0.4.x", "ExpExponentCleanup", @@ -673,6 +705,7 @@ }, "0.4.5": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -690,6 +723,7 @@ }, "0.4.6": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "UninitializedFunctionPointerInConstructor_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x", @@ -706,6 +740,7 @@ }, "0.4.7": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", @@ -722,6 +757,7 @@ }, "0.4.8": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", @@ -738,6 +774,7 @@ }, "0.4.9": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "UninitializedFunctionPointerInConstructor_0.4.x", @@ -754,6 +791,7 @@ }, "0.5.0": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -766,6 +804,7 @@ }, "0.5.1": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -778,6 +817,7 @@ }, "0.5.10": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers" @@ -786,6 +826,7 @@ }, "0.5.11": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5" ], @@ -793,6 +834,7 @@ }, "0.5.12": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5" ], @@ -800,6 +842,7 @@ }, "0.5.13": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5" ], @@ -807,6 +850,7 @@ }, "0.5.14": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5", "ABIEncoderV2LoopYulOptimizer" @@ -815,6 +859,7 @@ }, "0.5.15": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5" ], @@ -822,16 +867,20 @@ }, "0.5.16": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden" ], "released": "2020-01-02" }, "0.5.17": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow" + ], "released": "2020-03-17" }, "0.5.2": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -844,6 +893,7 @@ }, "0.5.3": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -856,6 +906,7 @@ }, "0.5.4": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -868,6 +919,7 @@ }, "0.5.5": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "SignedArrayStorageCopy", "ABIEncoderV2StorageArrayWithMultiSlotElement", @@ -882,6 +934,7 @@ }, "0.5.6": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers", "SignedArrayStorageCopy", @@ -896,6 +949,7 @@ }, "0.5.7": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers", "SignedArrayStorageCopy", @@ -908,6 +962,7 @@ }, "0.5.8": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers", @@ -919,6 +974,7 @@ }, "0.5.9": { "bugs": [ + "MemoryArrayCreationOverflow", "privateCanBeOverridden", "YulOptimizerRedundantAssignmentBreakContinue0.5", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers", @@ -929,24 +985,33 @@ }, "0.6.0": { "bugs": [ + "MemoryArrayCreationOverflow", "YulOptimizerRedundantAssignmentBreakContinue" ], "released": "2019-12-17" }, "0.6.1": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow" + ], "released": "2020-01-02" }, "0.6.2": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow" + ], "released": "2020-01-27" }, "0.6.3": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow" + ], "released": "2020-02-18" }, "0.6.4": { - "bugs": [], + "bugs": [ + "MemoryArrayCreationOverflow" + ], "released": "2020-03-10" } } \ No newline at end of file From 616fd8df1ef87bc43929469edf44f4a9f6b00af2 Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Tue, 31 Mar 2020 20:42:05 -0500 Subject: [PATCH 46/76] Apply modernize-use-nullptr. --- test/boostTest.cpp | 2 +- tools/yulPhaser/SimulationRNG.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 5a76a3f18..fd7bca132 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -187,7 +187,7 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) if (solidity::test::CommonOptions::get().disableSMT) removeTestSuite("SMTChecker"); - return 0; + return nullptr; } // BOOST_TEST_DYN_LINK should be defined if user want to link against shared boost test library diff --git a/tools/yulPhaser/SimulationRNG.cpp b/tools/yulPhaser/SimulationRNG.cpp index 1103db5a8..2b596f4ed 100644 --- a/tools/yulPhaser/SimulationRNG.cpp +++ b/tools/yulPhaser/SimulationRNG.cpp @@ -52,5 +52,5 @@ uint32_t SimulationRNG::generateSeed() // This is not a secure way to seed the generator but it's good enough for simulation purposes. // The only thing that matters for us is that the sequence is different on each run and that // it fits the expected distribution. It does not have to be 100% unpredictable. - return time(0); + return time(nullptr); } From 77ea896b68f844728f0adce34166563fd702a24f Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 2 Apr 2020 08:34:20 +0200 Subject: [PATCH 47/76] Properly handle assignments of immutables at declaration. --- libsolidity/codegen/ContractCompiler.cpp | 2 +- libsolidity/codegen/ExpressionCompiler.cpp | 5 ++++- libsolidity/codegen/LValue.cpp | 1 + .../semanticTests/immutable/assign_at_declaration.sol | 8 ++++++++ 4 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 test/libsolidity/semanticTests/immutable/assign_at_declaration.sol diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 50fb030d2..49b08b6da 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -550,7 +550,7 @@ void ContractCompiler::initializeStateVariables(ContractDefinition const& _contr { solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library."); for (VariableDeclaration const* variable: _contract.stateVariables()) - if (variable->value() && !variable->isConstant() && !variable->immutable()) + if (variable->value() && !variable->isConstant()) ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals).appendStateVariableInitialization(*variable); } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index fed3751fa..a406cdeb8 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -73,7 +73,10 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c utils().convertType(*type, *_varDecl.annotation().type); type = _varDecl.annotation().type; } - StorageItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true); + if (_varDecl.immutable()) + ImmutableItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true); + else + StorageItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true); } void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration const& _varDecl) diff --git a/libsolidity/codegen/LValue.cpp b/libsolidity/codegen/LValue.cpp index 4b1c80ede..3a04d6f92 100644 --- a/libsolidity/codegen/LValue.cpp +++ b/libsolidity/codegen/LValue.cpp @@ -183,6 +183,7 @@ void ImmutableItem::setToZero(SourceLocation const&, bool) const StorageItem::StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration): StorageItem(_compilerContext, *_declaration.annotation().type) { + solAssert(!_declaration.immutable(), ""); auto const& location = m_context.storageLocationOfVariable(_declaration); m_context << location.first << u256(location.second); } diff --git a/test/libsolidity/semanticTests/immutable/assign_at_declaration.sol b/test/libsolidity/semanticTests/immutable/assign_at_declaration.sol new file mode 100644 index 000000000..3f71a31e0 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/assign_at_declaration.sol @@ -0,0 +1,8 @@ +contract A { + uint8 immutable a = 2; + function f() public view returns (uint) { + return a; + } +} +// ---- +// f() -> 2 From 79387b2adaea7ba869ca9c8ad6c1d514fa5e323d Mon Sep 17 00:00:00 2001 From: hrkrshnn Date: Thu, 2 Apr 2020 12:27:38 +0530 Subject: [PATCH 48/76] Removed redundant declaration check; changed relevant test cases --- libsolidity/analysis/TypeChecker.cpp | 12 +----------- .../syntaxTests/array/uninitialized_storage_var.sol | 6 ++++-- .../211_uninitialized_mapping_array_variable.sol | 2 +- .../233_non_initialized_references.sol | 2 +- .../syntaxTests/parsing/arrays_in_expressions.sol | 1 - 5 files changed, 7 insertions(+), 16 deletions(-) diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 1345fbf90..4f4cbe379 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1061,17 +1061,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement) if (!varDecl.annotation().type) m_errorReporter.fatalTypeError(_statement.location(), "Use of the \"var\" keyword is disallowed."); - if (auto ref = dynamic_cast(type(varDecl))) - { - if (ref->dataStoredIn(DataLocation::Storage)) - { - string errorText{"Uninitialized storage pointer."}; - solAssert(varDecl.referenceLocation() != VariableDeclaration::Location::Unspecified, "Expected a specified location at this point"); - solAssert(m_scope, ""); - m_errorReporter.declarationError(varDecl.location(), errorText); - } - } - else if (dynamic_cast(type(varDecl))) + if (dynamic_cast(type(varDecl))) m_errorReporter.typeError( varDecl.location(), "Uninitialized mapping. Mappings cannot be created dynamically, you have to assign them from a state variable." diff --git a/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol b/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol index f3be90715..4966b4e85 100644 --- a/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol +++ b/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol @@ -2,8 +2,10 @@ contract C { function f() public { uint[] storage x; uint[10] storage y; + x; + y; } } // ---- -// DeclarationError: (38-54): Uninitialized storage pointer. -// DeclarationError: (58-76): Uninitialized storage pointer. +// TypeError: (80-81): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (85-86): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol index edae7549d..c43dbad6d 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol @@ -5,4 +5,4 @@ contract C { } } // ---- -// DeclarationError: (52-85): Uninitialized storage pointer. +// TypeError: (95-96): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol index a0b6f71e7..68e3b2c44 100644 --- a/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol @@ -8,4 +8,4 @@ contract C { } } // ---- -// DeclarationError: (84-95): Uninitialized storage pointer. +// TypeError: (105-106): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol b/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol index 4c1f96e6e..9035caa25 100644 --- a/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol +++ b/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol @@ -3,4 +3,3 @@ contract c { } // ---- // TypeError: (39-58): Type int_const 7 is not implicitly convertible to expected type contract c[10] storage pointer. -// DeclarationError: (60-83): Uninitialized storage pointer. From aec0ae8ec1d9d0fda5b64356bb4b357a7e7125ef Mon Sep 17 00:00:00 2001 From: hrkrshnn Date: Thu, 2 Apr 2020 15:45:04 +0530 Subject: [PATCH 49/76] Extended test-coverage for storage declaration --- .../assembly/for_declaration_err.sol | 38 +++++++++++ .../assembly/for_declaration_fine.sol | 19 ++++++ .../assembly/if_declaration_err.sol | 13 ++++ .../returning_function_declaration.sol | 15 +++++ .../reverting_function_declaration.sol | 15 +++++ .../assembly/stub_declaration.sol | 12 ++++ .../assembly/switch_declaration_err.sol | 33 ++++++++++ .../assembly/switch_declaration_fine.sol | 31 +++++++++ .../storageReturn/dowhile_declaration_err.sol | 66 +++++++++++++++++++ .../dowhile_declaration_fine.sol | 44 +++++++++++++ .../storageReturn/for_declaration_err.sol | 20 ++++++ .../storageReturn/for_declaration_fine.sol | 17 +++++ .../storageReturn/if_declaration_err.sol | 22 +++++++ .../storageReturn/if_declaration_fine.sol | 39 +++++++++++ .../modifier_declaration_fine.sol | 20 ++++++ .../storageReturn/revert_declaration_fine.sol | 11 ++++ .../short_circuit_declaration_err.sol | 24 +++++++ .../short_circuit_declaration_fine.sol | 15 +++++ .../storageReturn/smoke_declaration.sol | 17 +++++ .../storageReturn/ternary_declaration_err.sol | 17 +++++ .../ternary_declaration_fine.sol | 20 ++++++ .../storageReturn/try_declaration_err.sol | 42 ++++++++++++ .../storageReturn/try_declaration_fine.sol | 30 +++++++++ .../storageReturn/tuple_declaration_fine.sol | 17 +++++ .../storageReturn/while_declaration_err.sol | 13 ++++ .../storageReturn/while_declaration_fine.sol | 25 +++++++ 26 files changed, 635 insertions(+) create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_declaration_err.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_declaration_fine.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/if_declaration_err.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/returning_function_declaration.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/reverting_function_declaration.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/stub_declaration.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_declaration_err.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_declaration_fine.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_declaration_err.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_declaration_fine.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/for_declaration_err.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/for_declaration_fine.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/if_declaration_err.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/if_declaration_fine.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_declaration_fine.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/revert_declaration_fine.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_declaration_err.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_declaration_fine.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/smoke_declaration.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_declaration_err.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_declaration_fine.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/try_declaration_err.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/try_declaration_fine.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/tuple_declaration_fine.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/while_declaration_err.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/storageReturn/while_declaration_fine.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_declaration_err.sol new file mode 100644 index 000000000..ddb5faa9f --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_declaration_err.sol @@ -0,0 +1,38 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + assembly { + for {} eq(0,0) { c_slot := s_slot } {} + } + c; + } + function g() internal pure { + S storage c; + assembly { + for {} eq(0,1) { c_slot := s_slot } {} + } + c; + } + function h() internal pure { + S storage c; + assembly { + for {} eq(0,0) {} { c_slot := s_slot } + } + c; + } + function i() internal pure { + S storage c; + assembly { + for {} eq(0,1) {} { c_slot := s_slot } + } + c; + } +} +// ---- +// TypeError: (189-190): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (340-341): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (491-492): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (642-643): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. + diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_declaration_fine.sol new file mode 100644 index 000000000..19a58b234 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_declaration_fine.sol @@ -0,0 +1,19 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + assembly { + for { c_slot := s_slot } iszero(0) {} {} + } + c; + } + function g() internal pure { + S storage c; + assembly { + for { c_slot := s_slot } iszero(1) {} {} + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/if_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/if_declaration_err.sol new file mode 100644 index 000000000..1d4302755 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/if_declaration_err.sol @@ -0,0 +1,13 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal pure { + S storage c; + assembly { + if flag { c_slot := s_slot } + } + c; + } +} +// ---- +// TypeError: (188-189): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/returning_function_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/returning_function_declaration.sol new file mode 100644 index 000000000..180feb770 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/returning_function_declaration.sol @@ -0,0 +1,15 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + // this should warn about unreachable code, but currently function flow is ignored + assembly { + function f() { return(0, 0) } + f() + c_slot := s_slot + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/reverting_function_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/reverting_function_declaration.sol new file mode 100644 index 000000000..bba9daf34 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/reverting_function_declaration.sol @@ -0,0 +1,15 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + // this could be allowed, but currently control flow for functions is not analysed + assembly { + function f() { revert(0, 0) } + f() + } + c; + } +} +// ---- +// TypeError: (287-288): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/stub_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/stub_declaration.sol new file mode 100644 index 000000000..c3f240915 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/stub_declaration.sol @@ -0,0 +1,12 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure { + S storage c; + assembly { + c_slot := s_slot + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_declaration_err.sol new file mode 100644 index 000000000..72884e741 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_declaration_err.sol @@ -0,0 +1,33 @@ +contract C { + struct S { bool f; } + S s; + function f(uint256 a) internal pure { + S storage c; + assembly { + switch a + case 0 { c_slot := s_slot } + } + c; + } + function g(bool flag) internal pure { + S storage c; + assembly { + switch flag + case 0 { c_slot := s_slot } + case 1 { c_slot := s_slot } + } + c; + } + function h(uint256 a) internal pure { + S storage c; + assembly { + switch a + case 0 { c_slot := s_slot } + default { return(0,0) } + } + c; + } +} +// ---- +// TypeError: (208-209): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (421-422): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_declaration_fine.sol new file mode 100644 index 000000000..d72310924 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_declaration_fine.sol @@ -0,0 +1,31 @@ +contract C { + struct S { bool f; } + S s; + function f(uint256 a) internal pure { + S storage c; + assembly { + switch a + default { c_slot := s_slot } + } + c; + } + function g(bool flag) internal pure { + S storage c; + assembly { + switch flag + case 0 { c_slot := s_slot } + default { c_slot := s_slot } + } + c; + } + function h(uint256 a) internal pure { + S storage c; + assembly { + switch a + case 0 { revert(0, 0) } + default { c_slot := s_slot } + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_declaration_err.sol new file mode 100644 index 000000000..25654b1dd --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_declaration_err.sol @@ -0,0 +1,66 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + do { + break; + c = s; + } while(false); + c; + } + function g() internal view { + S storage c; + do { + if (s.f) { + continue; + c = s; + } + else { + } + } while(false); + c; + } + function h() internal view { + S storage c; + do { + if (s.f) { + break; + } + else { + c = s; + } + } while(false); + c; + } + function i() internal view { + S storage c; + do { + if (s.f) { + continue; + } + else { + c = s; + } + } while(false); + c; + } + function j() internal view { + S storage c; + do { + continue; + c = s; + } while(false); + c; + } +} +// ---- +// TypeError: (184-185): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// Warning: (145-150): Unreachable code. +// Warning: (168-173): Unreachable code. +// TypeError: (411-412): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// Warning: (325-330): Unreachable code. +// TypeError: (635-636): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (862-863): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (1011-1012): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// Warning: (972-977): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_declaration_fine.sol new file mode 100644 index 000000000..40ddc3777 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_declaration_fine.sol @@ -0,0 +1,44 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + do {} while((c = s).f); + c; + } + function g() internal view { + S storage c; + do { c = s; } while(false); + c; + } + function h() internal view { + S storage c; + c = s; + do {} while(false); + c; + } + function i() internal view { + S storage c; + do {} while(false); + c = s; + c; + } + function j() internal view { + S storage c; + do { + c = s; + break; + } while(false); + c; + } + function k() internal view { + S storage c; + do { + c = s; + continue; + } while(false); + c; + } +} +// ---- +// Warning: (606-611): Unreachable code. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_declaration_err.sol new file mode 100644 index 000000000..e93d9798f --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_declaration_err.sol @@ -0,0 +1,20 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + for(;; c = s) { + } + c; + } + function g() internal view { + S storage c; + for(;;) { + c = s; + } + c; + } +} +// ---- +//TypeError: (143-144): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (261-262): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_declaration_fine.sol new file mode 100644 index 000000000..b3e9f46d1 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_declaration_fine.sol @@ -0,0 +1,17 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + for(c = s;;) { + } + c; + } + function g() internal view { + S storage c; + for(; (c = s).f;) { + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_declaration_err.sol new file mode 100644 index 000000000..7da6bef92 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_declaration_err.sol @@ -0,0 +1,22 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal { + S storage c; + if (flag) c = s; + c; + } + function g(bool flag) internal { + S storage c; + if (flag) c = s; + else + { + if (!flag) c = s; + else s.f = true; + } + c; + } +} +// ---- +// TypeError: (138-139): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (330-331): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_declaration_fine.sol new file mode 100644 index 000000000..5d3a6d5bd --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_declaration_fine.sol @@ -0,0 +1,39 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal view { + S storage c; + if (flag) c = s; + else c = s; + c; + } + function g(bool flag) internal view { + S storage c; + if (flag) c = s; + else { c = s; } + c; + } + function h(bool flag) internal view { + S storage c; + if (flag) c = s; + else + { + if (!flag) c = s; + else c = s; + } + c; + } + function i() internal view { + S storage c; + if ((c = s).f) { + } + c; + } + function j() internal view { + S storage c; + if ((c = s).f && !(c = s).f) { + } + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_declaration_fine.sol new file mode 100644 index 000000000..6df4939cf --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_declaration_fine.sol @@ -0,0 +1,20 @@ +contract C { + modifier revertIfNoReturn() { + _; + revert(); + } + modifier ifFlag(bool flag) { + if (flag) + _; + } + struct S { uint a; } + S s; + function f(bool flag) revertIfNoReturn() internal view { + if (flag) s; + } + function g(bool flag) revertIfNoReturn() ifFlag(flag) internal view { + s; + } + +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/revert_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/revert_declaration_fine.sol new file mode 100644 index 000000000..5cb580528 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/revert_declaration_fine.sol @@ -0,0 +1,11 @@ +contract C { + struct S { bool f; } + S s; + function g(bool flag) internal view { + S storage c; + if (flag) c = s; + else revert(); + s; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_declaration_err.sol new file mode 100644 index 000000000..a6dd300ce --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_declaration_err.sol @@ -0,0 +1,24 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + false && (c = s).f; + c; + } + function g() internal view { + S storage c; + true || (c = s).f; + c; + } + function h() internal view { + S storage c; + // expect error, although this is always fine + true && (false || (c = s).f); + c; + } +} +// ---- +// TypeError: (137-138): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (235-236): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (398-399): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_declaration_fine.sol new file mode 100644 index 000000000..e8ad5e485 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_declaration_fine.sol @@ -0,0 +1,15 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + (c = s).f && false; + c; + } + function g() internal view { + S storage c; + (c = s).f || true; + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/smoke_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/smoke_declaration.sol new file mode 100644 index 000000000..f820456e1 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/smoke_declaration.sol @@ -0,0 +1,17 @@ +contract C { + struct S { bool f; } + S s; + function f() internal pure {} + function g() internal view { s; } + function h() internal view { + S storage c; + c = s; + c; + } + function i() internal view { + S storage c; + (c) = s; + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_declaration_err.sol new file mode 100644 index 000000000..3b4552f86 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_declaration_err.sol @@ -0,0 +1,17 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal view { + S storage c; + flag ? (c = s).f : false; + c; + } + function g(bool flag) internal view { + S storage c; + flag ? false : (c = s).f; + c; + } +} +// ---- +// TypeError: (152-153): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (266-267): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_declaration_fine.sol new file mode 100644 index 000000000..1dd8d2f57 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_declaration_fine.sol @@ -0,0 +1,20 @@ +contract C { + struct S { bool f; } + S s; + function f(bool flag) internal view { + S storage c; + flag ? c = s : c = s; + c; + } + function g(bool flag) internal view { + S storage c; + flag ? c = s : (c = s); + c; + } + function h(bool flag) internal view { + S storage c; + flag ? (c = s).f : (c = s).f; + c; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/try_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/try_declaration_err.sol new file mode 100644 index 000000000..ca43dc346 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/try_declaration_err.sol @@ -0,0 +1,42 @@ +contract C { + struct S { bool f; } + S s; + function ext() external {} + function f() internal + { + S storage r; + try this.ext() { } + catch (bytes memory) { r = s; } + r; + } + function g() internal + { + S storage r; + try this.ext() { r = s; } + catch (bytes memory) { } + r; + } + function h() internal + { + S storage r; + try this.ext() {} + catch Error (string memory) { r = s; } + catch (bytes memory) { r = s; } + r; + } + function i() internal + { + S storage r; + try this.ext() { r = s; } + catch (bytes memory) { r; } + r = s; + r; + } +} +// ==== +// EVMVersion: >=byzantium +// ---- +// TypeError: (206-207): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (343-344): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (526-527): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. +// TypeError: (653-654): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/try_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/try_declaration_fine.sol new file mode 100644 index 000000000..6834525d5 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/try_declaration_fine.sol @@ -0,0 +1,30 @@ +contract C { + struct S { bool f; } + S s; + function ext() external { } + function f() internal + { + S storage r; + try this.ext() { r = s; } + catch (bytes memory) { r = s; } + r; + } + function g() internal + { + S storage r; + try this.ext() { r = s; } + catch Error (string memory) { r = s; } + catch (bytes memory) { r = s; } + r; + } + function h() internal + { + S storage r; + try this.ext() { } + catch (bytes memory) { } + r = s; + r; + } +} +// ==== +// EVMVersion: >=byzantium diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/tuple_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/tuple_declaration_fine.sol new file mode 100644 index 000000000..e2e274196 --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/tuple_declaration_fine.sol @@ -0,0 +1,17 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view returns (S storage, uint) { + return (s,2); + } + function g() internal view { + uint a; + S storage c; + (c, a) = f(); + c; + } + function h() internal view { + (s, s); + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_declaration_err.sol new file mode 100644 index 000000000..cd6fee8db --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_declaration_err.sol @@ -0,0 +1,13 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + while(false) { + c = s; + } + c; + } +} +// ---- +// TypeError: (161-162): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_declaration_fine.sol new file mode 100644 index 000000000..20e72e5db --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_declaration_fine.sol @@ -0,0 +1,25 @@ +contract C { + struct S { bool f; } + S s; + function f() internal view { + S storage c; + while((c = s).f) { + } + c; + } + function g() internal view { + S storage c; + c = s; + while(false) { + } + c; + } + function h() internal view { + S storage c; + while(false) { + } + c = s; + c; + } +} +// ---- From 8e6567e39926414d543f89bd6652019db60f1a51 Mon Sep 17 00:00:00 2001 From: hrkrshnn Date: Thu, 2 Apr 2020 16:20:42 +0530 Subject: [PATCH 50/76] New folder localStorageVariable for new testcases; added two new tests --- .../assembly/for_declaration_err.sol | 0 .../assembly/for_declaration_fine.sol | 0 .../assembly/if_declaration_err.sol | 0 .../assembly/returning_function_declaration.sol | 0 .../assembly/reverting_function_declaration.sol | 0 .../assembly/stub_declaration.sol | 0 .../assembly/switch_declaration_err.sol | 0 .../assembly/switch_declaration_fine.sol | 0 .../dowhile_declaration_err.sol | 0 .../dowhile_declaration_fine.sol | 0 .../for_declaration_err.sol | 0 .../for_declaration_fine.sol | 0 .../if_declaration_err.sol | 0 .../if_declaration_fine.sol | 0 .../modifier_declaration_fine.sol | 0 .../revert_declaration_fine.sol | 0 .../short_circuit_declaration_err.sol | 0 .../short_circuit_declaration_fine.sol | 0 .../smoke_declaration.sol | 0 .../localStorageVariables/ternary_assignment_err.sol | 11 +++++++++++ .../localStorageVariables/ternary_assignment_fine.sol | 9 +++++++++ .../ternary_declaration_err.sol | 0 .../ternary_declaration_fine.sol | 0 .../try_declaration_err.sol | 0 .../try_declaration_fine.sol | 0 .../tuple_declaration_fine.sol | 0 .../while_declaration_err.sol | 0 .../while_declaration_fine.sol | 0 28 files changed, 20 insertions(+) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/assembly/for_declaration_err.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/assembly/for_declaration_fine.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/assembly/if_declaration_err.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/assembly/returning_function_declaration.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/assembly/reverting_function_declaration.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/assembly/stub_declaration.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/assembly/switch_declaration_err.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/assembly/switch_declaration_fine.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/dowhile_declaration_err.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/dowhile_declaration_fine.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/for_declaration_err.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/for_declaration_fine.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/if_declaration_err.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/if_declaration_fine.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/modifier_declaration_fine.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/revert_declaration_fine.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/short_circuit_declaration_err.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/short_circuit_declaration_fine.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/smoke_declaration.sol (100%) create mode 100644 test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_err.sol create mode 100644 test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_fine.sol rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/ternary_declaration_err.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/ternary_declaration_fine.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/try_declaration_err.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/try_declaration_fine.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/tuple_declaration_fine.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/while_declaration_err.sol (100%) rename test/libsolidity/syntaxTests/controlFlow/{storageReturn => localStorageVariables}/while_declaration_fine.sol (100%) diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_err.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_declaration_err.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_err.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_fine.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/for_declaration_fine.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/for_declaration_fine.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/if_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/if_declaration_err.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/if_declaration_err.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/if_declaration_err.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/returning_function_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/returning_function_declaration.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/returning_function_declaration.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/returning_function_declaration.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/reverting_function_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/reverting_function_declaration.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/reverting_function_declaration.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/reverting_function_declaration.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/stub_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/stub_declaration.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/stub_declaration.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/stub_declaration.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_err.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_declaration_err.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_err.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_fine.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/assembly/switch_declaration_fine.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/assembly/switch_declaration_fine.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_err.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_declaration_err.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_err.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_fine.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/dowhile_declaration_fine.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/dowhile_declaration_fine.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_err.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/for_declaration_err.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_err.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/for_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_fine.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/for_declaration_fine.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/for_declaration_fine.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_err.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/if_declaration_err.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_err.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/if_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_fine.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/if_declaration_fine.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/if_declaration_fine.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/modifier_declaration_fine.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/modifier_declaration_fine.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/modifier_declaration_fine.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/revert_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/revert_declaration_fine.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/revert_declaration_fine.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/revert_declaration_fine.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_err.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_declaration_err.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_err.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_fine.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/short_circuit_declaration_fine.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/short_circuit_declaration_fine.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/smoke_declaration.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/smoke_declaration.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/smoke_declaration.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/smoke_declaration.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_err.sol new file mode 100644 index 000000000..2c47731aa --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_err.sol @@ -0,0 +1,11 @@ +contract C { + uint256[] s; + function f() public { + bool d; + uint256[] storage x; + uint256[] storage y = d ? (x = s) : x; + y; + } +} +// ---- +// TypeError: (145-146): This variable is of storage pointer type and can be accessed without prior assignment, which would lead to undefined behaviour. diff --git a/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_fine.sol new file mode 100644 index 000000000..a510bd40d --- /dev/null +++ b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_assignment_fine.sol @@ -0,0 +1,9 @@ +contract C { + uint256[] s; + function f() public view { + uint256[] storage x; + uint256[] storage y = (x = s)[0] > 0 ? x : x; + y; + } +} +// --- diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_err.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_declaration_err.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_err.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_fine.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/ternary_declaration_fine.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/ternary_declaration_fine.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/try_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_err.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/try_declaration_err.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_err.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/try_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_fine.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/try_declaration_fine.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/try_declaration_fine.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/tuple_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/tuple_declaration_fine.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/tuple_declaration_fine.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/tuple_declaration_fine.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_declaration_err.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_err.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/while_declaration_err.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_err.sol diff --git a/test/libsolidity/syntaxTests/controlFlow/storageReturn/while_declaration_fine.sol b/test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_fine.sol similarity index 100% rename from test/libsolidity/syntaxTests/controlFlow/storageReturn/while_declaration_fine.sol rename to test/libsolidity/syntaxTests/controlFlow/localStorageVariables/while_declaration_fine.sol From 9a8ca6ca33d9c9910f57c03690fd004e5ff95c1a Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Wed, 1 Apr 2020 12:24:08 +0200 Subject: [PATCH 51/76] Always return `this` for `EventDefinition::resolveVirtual` Instead of asserting. --- libsolidity/ast/AST.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4bbacbdf3..3d64c828f 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -1039,7 +1039,7 @@ public: ContractDefinition const* ) const override { - solAssert(false, "Tried to resolve virtual event."); + return *this; } private: From ac7b31e5590fd4676b76afec878b838fe3c81abe Mon Sep 17 00:00:00 2001 From: Mathias Baumann Date: Tue, 10 Mar 2020 18:15:50 +0100 Subject: [PATCH 52/76] Validate immutable variables --- Changelog.md | 1 + libsolidity/CMakeLists.txt | 2 + libsolidity/analysis/ImmutableValidator.cpp | 214 ++++++++++++++++++ libsolidity/analysis/ImmutableValidator.h | 78 +++++++ libsolidity/analysis/TypeChecker.cpp | 15 +- libsolidity/analysis/TypeChecker.h | 2 +- libsolidity/ast/AST.h | 18 ++ libsolidity/ast/ASTAnnotations.h | 3 + libsolidity/interface/CompilerStack.cpp | 10 + .../conditional_return_uninitialized.sol | 11 + .../immutable/conditionally_initialized.sol | 9 + .../ctor_indirect_initialization.sol | 12 + .../ctor_initialization_indirect_reading.sol | 10 + .../immutable/ctor_initialization_reading.sol | 8 + .../immutable/ctor_initialization_tuple.sol | 13 ++ .../immutable/ctor_modifier_args.sol | 12 + .../ctor_modifier_initialization.sol | 11 + .../immutable/ctor_modifier_reading.sol | 14 ++ .../syntaxTests/immutable/decrement.sol | 8 + .../syntaxTests/immutable/delete.sol | 8 + .../immutable/function_initialization.sol | 5 + .../function_initialization_reading.sol | 7 + .../function_pointer_initializing.sol | 14 ++ .../immutable/function_pointer_reading.sol | 13 ++ .../syntaxTests/immutable/getter.sol | 5 - .../syntaxTests/immutable/immutable_basic.sol | 4 +- .../syntaxTests/immutable/increment.sol | 8 + .../indirect_reading_during_statevar_init.sol | 8 + .../immutable/inheritance_ctor.sol | 15 ++ .../immutable/inheritance_ctor_argument.sol | 14 ++ ...e_ctor_inherit_specifier_argument_init.sol | 13 ++ ...tor_inherit_specifier_argument_reading.sol | 16 ++ .../inheritance_virtual_functions.sol | 19 ++ ...eritance_virtual_functions_direct_call.sol | 19 ++ .../inheritance_virtual_functions_super.sol | 19 ++ .../inheritance_virtual_modifiers.sol | 21 ++ .../immutable/inheritance_wrong_ctor.sol | 12 + .../immutable/initialized_after_ctor.sol | 8 + .../immutable/loop_initialized.sol | 9 + ...multiple_inheritance_virtual_functions.sol | 29 +++ ...heritance_virtual_functions_with_super.sol | 29 +++ .../immutable/multiple_initializations.sol | 9 + .../immutable/private_state_var.sol | 20 ++ .../reading_after_initialization.sol | 8 + .../reading_after_initialization_modifier.sol | 12 + .../reading_during_statevar_init.sol | 6 + .../immutable/return_uninitialized.sol | 10 + .../syntaxTests/immutable/selector.sol | 12 + .../immutable/selector_function_name.sol | 13 ++ .../immutable/selector_function_pointer.sol | 16 ++ .../syntaxTests/immutable/uninitialized.sol | 5 + .../uninitialized_private_state_var.sol | 21 ++ .../immutable/unrelated_reading.sol | 8 + .../writing_after_initialization.sol | 10 + .../writing_after_initialization_modifier.sol | 12 + .../syntaxTests/viewPureChecker/immutable.sol | 4 +- 56 files changed, 898 insertions(+), 14 deletions(-) create mode 100644 libsolidity/analysis/ImmutableValidator.cpp create mode 100644 libsolidity/analysis/ImmutableValidator.h create mode 100644 test/libsolidity/syntaxTests/immutable/conditional_return_uninitialized.sol create mode 100644 test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol create mode 100644 test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol create mode 100644 test/libsolidity/syntaxTests/immutable/ctor_initialization_indirect_reading.sol create mode 100644 test/libsolidity/syntaxTests/immutable/ctor_initialization_reading.sol create mode 100644 test/libsolidity/syntaxTests/immutable/ctor_initialization_tuple.sol create mode 100644 test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol create mode 100644 test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol create mode 100644 test/libsolidity/syntaxTests/immutable/ctor_modifier_reading.sol create mode 100644 test/libsolidity/syntaxTests/immutable/decrement.sol create mode 100644 test/libsolidity/syntaxTests/immutable/delete.sol create mode 100644 test/libsolidity/syntaxTests/immutable/function_initialization.sol create mode 100644 test/libsolidity/syntaxTests/immutable/function_initialization_reading.sol create mode 100644 test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol create mode 100644 test/libsolidity/syntaxTests/immutable/function_pointer_reading.sol delete mode 100644 test/libsolidity/syntaxTests/immutable/getter.sol create mode 100644 test/libsolidity/syntaxTests/immutable/increment.sol create mode 100644 test/libsolidity/syntaxTests/immutable/indirect_reading_during_statevar_init.sol create mode 100644 test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol create mode 100644 test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol create mode 100644 test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol create mode 100644 test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_reading.sol create mode 100644 test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions.sol create mode 100644 test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_direct_call.sol create mode 100644 test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_super.sol create mode 100644 test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol create mode 100644 test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol create mode 100644 test/libsolidity/syntaxTests/immutable/initialized_after_ctor.sol create mode 100644 test/libsolidity/syntaxTests/immutable/loop_initialized.sol create mode 100644 test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions.sol create mode 100644 test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions_with_super.sol create mode 100644 test/libsolidity/syntaxTests/immutable/multiple_initializations.sol create mode 100644 test/libsolidity/syntaxTests/immutable/private_state_var.sol create mode 100644 test/libsolidity/syntaxTests/immutable/reading_after_initialization.sol create mode 100644 test/libsolidity/syntaxTests/immutable/reading_after_initialization_modifier.sol create mode 100644 test/libsolidity/syntaxTests/immutable/reading_during_statevar_init.sol create mode 100644 test/libsolidity/syntaxTests/immutable/return_uninitialized.sol create mode 100644 test/libsolidity/syntaxTests/immutable/selector.sol create mode 100644 test/libsolidity/syntaxTests/immutable/selector_function_name.sol create mode 100644 test/libsolidity/syntaxTests/immutable/selector_function_pointer.sol create mode 100644 test/libsolidity/syntaxTests/immutable/uninitialized.sol create mode 100644 test/libsolidity/syntaxTests/immutable/uninitialized_private_state_var.sol create mode 100644 test/libsolidity/syntaxTests/immutable/unrelated_reading.sol create mode 100644 test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol create mode 100644 test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol diff --git a/Changelog.md b/Changelog.md index f20b05aa5..6ca68ea85 100644 --- a/Changelog.md +++ b/Changelog.md @@ -11,6 +11,7 @@ Compiler Features: * Metadata: Added support for IPFS hashes of large files that need to be split in multiple chunks. * Commandline Interface: Enable output of storage layout with `--storage-layout`. + Bugfixes: * Inline Assembly: Fix internal error when accessing invalid constant variables. * Inline Assembly: Fix internal error when accessing functions. diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index ba2651e94..c7a517a0b 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -14,6 +14,8 @@ set(sources analysis/DeclarationContainer.h analysis/DocStringAnalyser.cpp analysis/DocStringAnalyser.h + analysis/ImmutableValidator.cpp + analysis/ImmutableValidator.h analysis/GlobalContext.cpp analysis/GlobalContext.h analysis/NameAndTypeResolver.cpp diff --git a/libsolidity/analysis/ImmutableValidator.cpp b/libsolidity/analysis/ImmutableValidator.cpp new file mode 100644 index 000000000..e80902009 --- /dev/null +++ b/libsolidity/analysis/ImmutableValidator.cpp @@ -0,0 +1,214 @@ +/* + 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 . +*/ + +#include + +#include + +#include + +using namespace solidity::frontend; + +void ImmutableValidator::analyze() +{ + m_inConstructionContext = true; + + auto linearizedContracts = m_currentContract.annotation().linearizedBaseContracts | boost::adaptors::reversed; + + for (ContractDefinition const* contract: linearizedContracts) + for (VariableDeclaration const* stateVar: contract->stateVariables()) + if (stateVar->value()) + { + stateVar->value()->accept(*this); + solAssert(m_initializedStateVariables.emplace(stateVar).second, ""); + } + + for (ContractDefinition const* contract: linearizedContracts) + if (contract->constructor()) + visitCallableIfNew(*contract->constructor()); + + for (ContractDefinition const* contract: linearizedContracts) + for (std::shared_ptr const inheritSpec: contract->baseContracts()) + if (auto args = inheritSpec->arguments()) + ASTNode::listAccept(*args, *this); + + m_inConstructionContext = false; + + for (ContractDefinition const* contract: linearizedContracts) + { + for (auto funcDef: contract->definedFunctions()) + visitCallableIfNew(*funcDef); + + for (auto modDef: contract->functionModifiers()) + visitCallableIfNew(*modDef); + } + + checkAllVariablesInitialized(m_currentContract.location()); +} + +bool ImmutableValidator::visit(FunctionDefinition const& _functionDefinition) +{ + return analyseCallable(_functionDefinition); +} + +bool ImmutableValidator::visit(ModifierDefinition const& _modifierDefinition) +{ + return analyseCallable(_modifierDefinition); +} + +bool ImmutableValidator::visit(MemberAccess const& _memberAccess) +{ + _memberAccess.expression().accept(*this); + + if (auto varDecl = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + analyseVariableReference(*varDecl, _memberAccess); + else if (auto funcType = dynamic_cast(_memberAccess.annotation().type)) + if (funcType->kind() == FunctionType::Kind::Internal && funcType->hasDeclaration()) + visitCallableIfNew(funcType->declaration()); + + return false; +} + +bool ImmutableValidator::visit(IfStatement const& _ifStatement) +{ + bool prevInBranch = m_inBranch; + + _ifStatement.condition().accept(*this); + + m_inBranch = true; + _ifStatement.trueStatement().accept(*this); + + if (auto falseStatement = _ifStatement.falseStatement()) + falseStatement->accept(*this); + + m_inBranch = prevInBranch; + + return false; +} + +bool ImmutableValidator::visit(WhileStatement const& _whileStatement) +{ + bool prevInLoop = m_inLoop; + m_inLoop = true; + + _whileStatement.condition().accept(*this); + _whileStatement.body().accept(*this); + + m_inLoop = prevInLoop; + + return false; +} + +void ImmutableValidator::endVisit(Identifier const& _identifier) +{ + if (auto const callableDef = dynamic_cast(_identifier.annotation().referencedDeclaration)) + visitCallableIfNew(callableDef->resolveVirtual(m_currentContract)); + if (auto const varDecl = dynamic_cast(_identifier.annotation().referencedDeclaration)) + analyseVariableReference(*varDecl, _identifier); +} + +void ImmutableValidator::endVisit(Return const& _return) +{ + if (m_currentConstructor != nullptr) + checkAllVariablesInitialized(_return.location()); +} + +bool ImmutableValidator::analyseCallable(CallableDeclaration const& _callableDeclaration) +{ + FunctionDefinition const* prevConstructor = m_currentConstructor; + m_currentConstructor = nullptr; + + if (FunctionDefinition const* funcDef = dynamic_cast(&_callableDeclaration)) + { + ASTNode::listAccept(funcDef->modifiers(), *this); + + if (funcDef->isConstructor()) + m_currentConstructor = funcDef; + + if (funcDef->isImplemented()) + funcDef->body().accept(*this); + } + else if (ModifierDefinition const* modDef = dynamic_cast(&_callableDeclaration)) + modDef->body().accept(*this); + + m_currentConstructor = prevConstructor; + + return false; +} + +void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _variableReference, Expression const& _expression) +{ + if (!_variableReference.isStateVariable() || !_variableReference.immutable()) + return; + + if (_expression.annotation().lValueRequested && _expression.annotation().lValueOfOrdinaryAssignment) + { + if (!m_currentConstructor) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables can only be initialized inline or assigned directly in the constructor." + ); + else if (m_currentConstructor->annotation().contract->id() != _variableReference.annotation().contract->id()) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables must be initialized in the constructor of the contract they are defined in." + ); + else if (m_inLoop) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables can only be initialized once, not in a while statement." + ); + else if (m_inBranch) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables must be initialized unconditionally, not in an if statement." + ); + + if (!m_initializedStateVariables.emplace(&_variableReference).second) + m_errorReporter.typeError( + _expression.location(), + "Immutable state variable already initialized." + ); + } + else if (m_inConstructionContext) + m_errorReporter.typeError( + _expression.location(), + "Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it." + ); +} + +void ImmutableValidator::checkAllVariablesInitialized(solidity::langutil::SourceLocation const& _location) +{ + for (ContractDefinition const* contract: m_currentContract.annotation().linearizedBaseContracts) + for (VariableDeclaration const* varDecl: contract->stateVariables()) + if (varDecl->immutable()) + if (!util::contains(m_initializedStateVariables, varDecl)) + m_errorReporter.typeError( + _location, + solidity::langutil::SecondarySourceLocation().append("Not initialized: ", varDecl->location()), + "Construction control flow ends without initializing all immutable state variables." + ); +} + +void ImmutableValidator::visitCallableIfNew(Declaration const& _declaration) +{ + CallableDeclaration const* _callable = dynamic_cast(&_declaration); + solAssert(_callable != nullptr, ""); + + if (m_visitedCallables.emplace(_callable).second) + _declaration.accept(*this); +} diff --git a/libsolidity/analysis/ImmutableValidator.h b/libsolidity/analysis/ImmutableValidator.h new file mode 100644 index 000000000..5845ba011 --- /dev/null +++ b/libsolidity/analysis/ImmutableValidator.h @@ -0,0 +1,78 @@ +/* + 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 . +*/ + +#pragma once + +#include +#include + +#include + +namespace solidity::frontend +{ + +/** + * Validates access and initialization of immutable variables: + * must be directly initialized in their respective c'tor + * can not be read by any function/modifier called by the c'tor (or the c'tor itself) + * must be initialized outside loops (only one initialization) + * must be initialized outside ifs (must be initialized unconditionally) + * must be initialized exactly once (no multiple statements) + * must be initialized exactly once (no early return to skip initialization) +*/ +class ImmutableValidator: private ASTConstVisitor +{ + using CallableDeclarationSet = std::set; + +public: + ImmutableValidator(langutil::ErrorReporter& _errorReporter, ContractDefinition const& _contractDefinition): + m_currentContract(_contractDefinition), + m_errorReporter(_errorReporter) + { } + + void analyze(); + +private: + bool visit(FunctionDefinition const& _functionDefinition); + bool visit(ModifierDefinition const& _modifierDefinition); + bool visit(MemberAccess const& _memberAccess); + bool visit(IfStatement const& _ifStatement); + bool visit(WhileStatement const& _whileStatement); + void endVisit(Identifier const& _identifier); + void endVisit(Return const& _return); + + bool analyseCallable(CallableDeclaration const& _callableDeclaration); + void analyseVariableReference(VariableDeclaration const& _variableReference, Expression const& _expression); + + void checkAllVariablesInitialized(langutil::SourceLocation const& _location); + + void visitCallableIfNew(Declaration const& _declaration); + + ContractDefinition const& m_currentContract; + + CallableDeclarationSet m_visitedCallables; + + std::set m_initializedStateVariables; + langutil::ErrorReporter& m_errorReporter; + + FunctionDefinition const* m_currentConstructor = nullptr; + bool m_inLoop = false; + bool m_inBranch = false; + bool m_inConstructionContext = false; +}; + +} diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index e6a2408f1..8e7bef338 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1337,7 +1337,10 @@ void TypeChecker::checkExpressionAssignment(Type const& _type, Expression const& bool TypeChecker::visit(Assignment const& _assignment) { - requireLValue(_assignment.leftHandSide()); + requireLValue( + _assignment.leftHandSide(), + _assignment.assignmentOperator() == Token::Assign + ); TypePointer t = type(_assignment.leftHandSide()); _assignment.annotation().type = t; @@ -1395,7 +1398,10 @@ bool TypeChecker::visit(TupleExpression const& _tuple) for (auto const& component: components) if (component) { - requireLValue(*component); + requireLValue( + *component, + _tuple.annotation().lValueOfOrdinaryAssignment + ); types.push_back(type(*component)); } else @@ -1480,7 +1486,7 @@ bool TypeChecker::visit(UnaryOperation const& _operation) Token op = _operation.getOperator(); bool const modifying = (op == Token::Inc || op == Token::Dec || op == Token::Delete); if (modifying) - requireLValue(_operation.subExpression()); + requireLValue(_operation.subExpression(), false); else _operation.subExpression().accept(*this); TypePointer const& subExprType = type(_operation.subExpression()); @@ -2988,9 +2994,10 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte return true; } -void TypeChecker::requireLValue(Expression const& _expression) +void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAssignment) { _expression.annotation().lValueRequested = true; + _expression.annotation().lValueOfOrdinaryAssignment = _ordinaryAssignment; _expression.accept(*this); if (_expression.annotation().isLValue) diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h index d428a6ac9..a26ab81bd 100644 --- a/libsolidity/analysis/TypeChecker.h +++ b/libsolidity/analysis/TypeChecker.h @@ -158,7 +158,7 @@ private: /// convertible to @a _expectedType. bool expectType(Expression const& _expression, Type const& _expectedType); /// Runs type checks on @a _expression to infer its type and then checks that it is an LValue. - void requireLValue(Expression const& _expression); + void requireLValue(Expression const& _expression, bool _ordinaryAssignment); ContractDefinition const* m_scope = nullptr; diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 3d64c828f..4979d5d4e 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -62,6 +62,24 @@ class ASTConstVisitor; class ASTNode: private boost::noncopyable { public: + struct CompareByID + { + using is_transparent = void; + + bool operator()(ASTNode const* _lhs, ASTNode const* _rhs) const + { + return _lhs->id() < _rhs->id(); + } + bool operator()(ASTNode const* _lhs, int64_t _rhs) const + { + return _lhs->id() < _rhs; + } + bool operator()(int64_t _lhs, ASTNode const* _rhs) const + { + return _lhs < _rhs->id(); + } + }; + using SourceLocation = langutil::SourceLocation; explicit ASTNode(int64_t _id, SourceLocation const& _location); diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 86636684c..a724472d0 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -208,6 +208,9 @@ struct ExpressionAnnotation: ASTAnnotation bool isLValue = false; /// Whether the expression is used in a context where the LValue is actually required. bool lValueRequested = false; + /// Whether the expression is an lvalue that is only assigned. + /// Would be false for --, ++, delete, +=, -=, .... + bool lValueOfOrdinaryAssignment = false; /// Types and - if given - names of arguments if the expr. is a function /// that is called, used for overload resoultion diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 781f68d6b..fe0268fb1 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -383,6 +384,15 @@ bool CompilerStack::analyze() noErrors = false; } + // Check that immutable variables are never read in c'tors and assigned + // exactly once + if (noErrors) + for (Source const* source: m_sourceOrder) + if (source->ast) + for (ASTPointer const& node: source->ast->nodes()) + if (ContractDefinition* contract = dynamic_cast(node.get())) + ImmutableValidator(m_errorReporter, *contract).analyze(); + if (noErrors) { // Control flow graph generator and analyzer. It can check for issues such as diff --git a/test/libsolidity/syntaxTests/immutable/conditional_return_uninitialized.sol b/test/libsolidity/syntaxTests/immutable/conditional_return_uninitialized.sol new file mode 100644 index 000000000..702f03079 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/conditional_return_uninitialized.sol @@ -0,0 +1,11 @@ +contract C { + uint immutable x; + constructor() public { + if (false) + return; + + x = 1; + } +} +// ---- +// TypeError: (93-100): Construction control flow ends without initializing all immutable state variables. diff --git a/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol b/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol new file mode 100644 index 000000000..b8557321b --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/conditionally_initialized.sol @@ -0,0 +1,9 @@ +contract C { + uint immutable x; + constructor() public { + if (false) + x = 1; + } +} +// ---- +// TypeError: (93-94): Immutable variables must be initialized unconditionally, not in an if statement. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol b/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol new file mode 100644 index 000000000..1423659c8 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_indirect_initialization.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x; + constructor() public { + initX(); + } + + function initX() internal { + x = 3; + } +} +// ---- +// TypeError: (126-127): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_initialization_indirect_reading.sol b/test/libsolidity/syntaxTests/immutable/ctor_initialization_indirect_reading.sol new file mode 100644 index 000000000..ad17635c4 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_initialization_indirect_reading.sol @@ -0,0 +1,10 @@ +contract C { + uint immutable x; + constructor() public { + x = f(); + } + + function f() public pure returns (uint) { return 3 + x; } +} +// ---- +// TypeError: (143-144): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_initialization_reading.sol b/test/libsolidity/syntaxTests/immutable/ctor_initialization_reading.sol new file mode 100644 index 000000000..0b2207808 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_initialization_reading.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x; + constructor() public { + x = 3 + x; + } +} +// ---- +// TypeError: (78-79): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_initialization_tuple.sol b/test/libsolidity/syntaxTests/immutable/ctor_initialization_tuple.sol new file mode 100644 index 000000000..f3b724f03 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_initialization_tuple.sol @@ -0,0 +1,13 @@ +contract C { + uint immutable x; + uint immutable y; + constructor() public { + (x, y) = f(); + } + + function f() internal pure returns(uint _x, uint _y) { + _x = 3; + _y = 4; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol new file mode 100644 index 000000000..2c1c37c79 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_args.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x; + constructor() readX(x = 3) public { } + + modifier readX(uint _x) { + _; f(_x); + } + + function f(uint a) internal pure {} +} +// ---- +// TypeError: (59-60): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol new file mode 100644 index 000000000..b26666530 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_initialization.sol @@ -0,0 +1,11 @@ +contract C { + uint immutable x; + constructor() initX public { + } + + modifier initX() { + _; x = 23; + } +} +// ---- +// TypeError: (109-110): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/ctor_modifier_reading.sol b/test/libsolidity/syntaxTests/immutable/ctor_modifier_reading.sol new file mode 100644 index 000000000..a9a1b3a22 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/ctor_modifier_reading.sol @@ -0,0 +1,14 @@ +contract C { + uint immutable x; + constructor() readX public { + x = 3; + } + + modifier readX() { + _; f(x); + } + + function f(uint a) internal pure {} +} +// ---- +// TypeError: (126-127): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/decrement.sol b/test/libsolidity/syntaxTests/immutable/decrement.sol new file mode 100644 index 000000000..54ac9f615 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/decrement.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 3; + constructor() public { + x--; + } +} +// ---- +// TypeError: (74-75): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/delete.sol b/test/libsolidity/syntaxTests/immutable/delete.sol new file mode 100644 index 000000000..def28fad2 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/delete.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 3; + constructor() public { + delete x; + } +} +// ---- +// TypeError: (81-82): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/function_initialization.sol b/test/libsolidity/syntaxTests/immutable/function_initialization.sol new file mode 100644 index 000000000..c836beb27 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/function_initialization.sol @@ -0,0 +1,5 @@ +contract C { + uint immutable x = f(); + + function f() public pure returns (uint) { return 3; } +} diff --git a/test/libsolidity/syntaxTests/immutable/function_initialization_reading.sol b/test/libsolidity/syntaxTests/immutable/function_initialization_reading.sol new file mode 100644 index 000000000..2a4365e69 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/function_initialization_reading.sol @@ -0,0 +1,7 @@ +contract C { + uint immutable x = f(); + + function f() public pure returns (uint) { return 3 + x; } +} +// ---- +// TypeError: (99-100): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol b/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol new file mode 100644 index 000000000..04eaa621d --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/function_pointer_initializing.sol @@ -0,0 +1,14 @@ +contract B { + uint immutable x; + + constructor(function() internal returns(uint) fp) internal { + x = fp(); + } +} + +contract C is B(C.f) { + function f() internal returns(uint) { return x = 2; } +} +// ---- +// TypeError: (200-201): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError: (200-201): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/immutable/function_pointer_reading.sol b/test/libsolidity/syntaxTests/immutable/function_pointer_reading.sol new file mode 100644 index 000000000..2ec62e931 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/function_pointer_reading.sol @@ -0,0 +1,13 @@ +contract B { + uint immutable x; + + constructor(function() internal returns(uint) fp) internal { + x = fp(); + } +} + +contract C is B(C.f) { + function f() internal returns(uint) { return x + 2; } +} +// ---- +// TypeError: (200-201): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/getter.sol b/test/libsolidity/syntaxTests/immutable/getter.sol deleted file mode 100644 index 7740f8643..000000000 --- a/test/libsolidity/syntaxTests/immutable/getter.sol +++ /dev/null @@ -1,5 +0,0 @@ -contract C { - uint immutable public x; -} -// ---- -// UnimplementedFeatureError: NONE diff --git a/test/libsolidity/syntaxTests/immutable/immutable_basic.sol b/test/libsolidity/syntaxTests/immutable/immutable_basic.sol index b4960f25b..3d8e04636 100644 --- a/test/libsolidity/syntaxTests/immutable/immutable_basic.sol +++ b/test/libsolidity/syntaxTests/immutable/immutable_basic.sol @@ -1,3 +1,3 @@ contract C { - uint immutable x; -} \ No newline at end of file + uint immutable x = 0; +} diff --git a/test/libsolidity/syntaxTests/immutable/increment.sol b/test/libsolidity/syntaxTests/immutable/increment.sol new file mode 100644 index 000000000..0068bf87d --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/increment.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 3; + constructor() public { + x++; + } +} +// ---- +// TypeError: (74-75): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/indirect_reading_during_statevar_init.sol b/test/libsolidity/syntaxTests/immutable/indirect_reading_during_statevar_init.sol new file mode 100644 index 000000000..5f659bff6 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/indirect_reading_during_statevar_init.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 0; + uint y = f(); + + function f() internal returns(uint) { return x; } +} +// ---- +// TypeError: (107-108): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol new file mode 100644 index 000000000..ee794fd37 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor.sol @@ -0,0 +1,15 @@ +contract B { + uint immutable x; + + constructor() public { + x = 3; + } +} + +contract C is B { + uint immutable y; + constructor() public { + y = 3; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol new file mode 100644 index 000000000..0d98f8b40 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_argument.sol @@ -0,0 +1,14 @@ +contract B { + uint immutable x; + + constructor(uint _x) public { + x = _x; + } +} + +contract C is B { + uint immutable y; + constructor() B(y = 3) public { } +} +// ---- +// TypeError: (155-156): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol new file mode 100644 index 000000000..16ed52f36 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_init.sol @@ -0,0 +1,13 @@ +contract B { + uint immutable x; + + constructor(uint _x) public { + x = _x; + } +} + +contract C is B(C.y = 3) { + uint immutable y; +} +// ---- +// TypeError: (111-114): Immutable variables can only be initialized inline or assigned directly in the constructor. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_reading.sol b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_reading.sol new file mode 100644 index 000000000..ed352746f --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_ctor_inherit_specifier_argument_reading.sol @@ -0,0 +1,16 @@ +contract B { + uint immutable x; + + constructor(uint _x) public { + x = _x; + } +} + +contract C is B(C.y) { + uint immutable y; + constructor() public { + y = 3; + } +} +// ---- +// TypeError: (111-114): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions.sol new file mode 100644 index 000000000..d56fb487a --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions.sol @@ -0,0 +1,19 @@ +contract B { + uint immutable x; + + constructor() public { + x = xInit(); + } + + function xInit() internal virtual returns(uint) { + return 3; + } +} + +contract C is B { + function xInit() internal override returns(uint) { + return x; + } +} +// ---- +// TypeError: (260-261): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_direct_call.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_direct_call.sol new file mode 100644 index 000000000..5dda6bfb0 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_direct_call.sol @@ -0,0 +1,19 @@ +contract B { + uint immutable x = 3; + + function readX() internal virtual returns(uint) { + return x; + } +} + +contract C is B { + constructor() public { + B.readX; + } + + function readX() internal override returns(uint) { + return 3; + } +} +// ---- +// TypeError: (109-110): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_super.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_super.sol new file mode 100644 index 000000000..278efe55f --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_functions_super.sol @@ -0,0 +1,19 @@ +contract B { + uint immutable x = 3; + + function readX() internal view virtual returns(uint) { + return x; + } +} + +contract C is B { + constructor() public { + super.readX(); + } + + function readX() internal view override returns(uint) { + return 1; + } +} +// ---- +// TypeError: (114-115): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol new file mode 100644 index 000000000..f1e60dbd8 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_virtual_modifiers.sol @@ -0,0 +1,21 @@ +contract B { + uint immutable x; + + constructor() readX public { + x = 3; + } + + modifier readX() virtual { + _; f(3); + } + + function f(uint a) internal pure {} +} + +contract C is B { + modifier readX() override { + _; f(x); + } +} +// ---- +// TypeError: (252-253): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol b/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol new file mode 100644 index 000000000..24a5239ed --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/inheritance_wrong_ctor.sol @@ -0,0 +1,12 @@ +contract B { + uint immutable x = 4; +} + +contract C is B { + constructor() public { + x = 3; + } +} +// ---- +// TypeError: (95-96): Immutable variables must be initialized in the constructor of the contract they are defined in. +// TypeError: (95-96): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/immutable/initialized_after_ctor.sol b/test/libsolidity/syntaxTests/immutable/initialized_after_ctor.sol new file mode 100644 index 000000000..a6db6a665 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/initialized_after_ctor.sol @@ -0,0 +1,8 @@ +contract C { + constructor() public { + return; + } + + uint immutable x = 3; +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/loop_initialized.sol b/test/libsolidity/syntaxTests/immutable/loop_initialized.sol new file mode 100644 index 000000000..2827ab389 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/loop_initialized.sol @@ -0,0 +1,9 @@ +contract C { + uint immutable x; + constructor() public { + while (true) + x = 1; + } +} +// ---- +// TypeError: (95-96): Immutable variables can only be initialized once, not in a while statement. diff --git a/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions.sol b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions.sol new file mode 100644 index 000000000..c71335b83 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions.sol @@ -0,0 +1,29 @@ +contract A { + function f() internal virtual returns(uint) { return 3; } +} + +contract B { + uint immutable x; + + constructor() public { + x = xInit(); + } + + function xInit() internal virtual returns(uint) { + return f(); + } + + function f() internal virtual returns(uint) { return 3; } +} + +contract C is A, B { + function xInit() internal override returns(uint) { + return B.xInit(); + } + + function f() internal override(A, B) returns(uint) { + return x; + } +} +// ---- +// TypeError: (496-497): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions_with_super.sol b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions_with_super.sol new file mode 100644 index 000000000..eaabbb71a --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/multiple_inheritance_virtual_functions_with_super.sol @@ -0,0 +1,29 @@ +contract A { + function f() internal virtual returns(uint) { return 3; } +} + +contract B { + uint immutable x; + + constructor() public { + x = xInit(); + } + + function xInit() internal virtual returns(uint) { + return f(); + } + + function f() internal virtual returns(uint) { return 3; } +} + +contract C is A, B { + function xInit() internal override returns(uint) { + return super.xInit(); + } + + function f() internal override(A, B) returns(uint) { + return x; + } +} +// ---- +// TypeError: (500-501): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/multiple_initializations.sol b/test/libsolidity/syntaxTests/immutable/multiple_initializations.sol new file mode 100644 index 000000000..8ff940fef --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/multiple_initializations.sol @@ -0,0 +1,9 @@ +contract C { + uint immutable x; + constructor() public { + x = 1; + x = 4; + } +} +// ---- +// TypeError: (85-86): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/immutable/private_state_var.sol b/test/libsolidity/syntaxTests/immutable/private_state_var.sol new file mode 100644 index 000000000..2cfc22a85 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/private_state_var.sol @@ -0,0 +1,20 @@ +contract B { + uint immutable private x = f(); + + constructor() public { + } + + function f() internal view virtual returns(uint) { return 1; } + function readX() internal view returns(uint) { return x; } +} + +contract C is B { + uint immutable y; + constructor() public { + y = 3; + } + function f() internal view override returns(uint) { return readX(); } + +} +// ---- +// TypeError: (209-210): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/reading_after_initialization.sol b/test/libsolidity/syntaxTests/immutable/reading_after_initialization.sol new file mode 100644 index 000000000..b64f236a9 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/reading_after_initialization.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 0; + uint y = 0; + + function f() internal { + y = x + 1; + } +} diff --git a/test/libsolidity/syntaxTests/immutable/reading_after_initialization_modifier.sol b/test/libsolidity/syntaxTests/immutable/reading_after_initialization_modifier.sol new file mode 100644 index 000000000..4de778ba3 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/reading_after_initialization_modifier.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x = 0; + uint y = 0; + + function f() readX internal { + } + + modifier readX() { + _; + y = x + 1; + } +} diff --git a/test/libsolidity/syntaxTests/immutable/reading_during_statevar_init.sol b/test/libsolidity/syntaxTests/immutable/reading_during_statevar_init.sol new file mode 100644 index 000000000..d35c68d8a --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/reading_during_statevar_init.sol @@ -0,0 +1,6 @@ +contract C { + uint immutable x = 0; + uint y = x; +} +// ---- +// TypeError: (52-53): Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it. diff --git a/test/libsolidity/syntaxTests/immutable/return_uninitialized.sol b/test/libsolidity/syntaxTests/immutable/return_uninitialized.sol new file mode 100644 index 000000000..0fd5461d4 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/return_uninitialized.sol @@ -0,0 +1,10 @@ +contract C { + uint immutable x; + constructor() public { + return; + + x = 1; + } +} +// ---- +// TypeError: (70-77): Construction control flow ends without initializing all immutable state variables. diff --git a/test/libsolidity/syntaxTests/immutable/selector.sol b/test/libsolidity/syntaxTests/immutable/selector.sol new file mode 100644 index 000000000..4b880c8e8 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/selector.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x; + constructor() public { + x = 3; + this.readX.selector; + } + + function readX() external view returns(uint) { return x; } +} +// ---- +// Warning: (85-104): Statement has no effect. +// Warning: (85-89): "this" used in constructor. Note that external functions of a contract cannot be called while it is being constructed. diff --git a/test/libsolidity/syntaxTests/immutable/selector_function_name.sol b/test/libsolidity/syntaxTests/immutable/selector_function_name.sol new file mode 100644 index 000000000..c84f72408 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/selector_function_name.sol @@ -0,0 +1,13 @@ +contract C { + uint immutable x; + constructor() public { + x = 3; + C.selector.selector; + C.selector; + } + + function selector() external view returns(uint) { return x; } +} +// ---- +// Warning: (85-104): Statement has no effect. +// Warning: (114-124): Statement has no effect. diff --git a/test/libsolidity/syntaxTests/immutable/selector_function_pointer.sol b/test/libsolidity/syntaxTests/immutable/selector_function_pointer.sol new file mode 100644 index 000000000..a8b91a639 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/selector_function_pointer.sol @@ -0,0 +1,16 @@ +contract C { + uint immutable x; + constructor() public { + x = 3; + readX().selector; + } + + function f() external view returns(uint) { + return x; + } + + function readX() public view returns(function() external view returns(uint) _f) { + _f = this.f; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/uninitialized.sol b/test/libsolidity/syntaxTests/immutable/uninitialized.sol new file mode 100644 index 000000000..18c60ea9d --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/uninitialized.sol @@ -0,0 +1,5 @@ +contract C { + uint immutable x; +} +// ---- +// TypeError: (0-36): Construction control flow ends without initializing all immutable state variables. diff --git a/test/libsolidity/syntaxTests/immutable/uninitialized_private_state_var.sol b/test/libsolidity/syntaxTests/immutable/uninitialized_private_state_var.sol new file mode 100644 index 000000000..69aa5448f --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/uninitialized_private_state_var.sol @@ -0,0 +1,21 @@ +contract B { + uint immutable private x; + + constructor() public { + } + + function f() internal view virtual returns(uint) { return 1; } + function readX() internal view returns(uint) { return x; } +} + +contract C is B { + uint immutable y; + constructor() public { + y = 3; + } + function f() internal view override returns(uint) { return readX(); } + +} +// ---- +// TypeError: (0-209): Construction control flow ends without initializing all immutable state variables. +// TypeError: (211-375): Construction control flow ends without initializing all immutable state variables. diff --git a/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol b/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol new file mode 100644 index 000000000..2b5614650 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/unrelated_reading.sol @@ -0,0 +1,8 @@ +contract C { + uint immutable x = 1; + + function readX() internal view returns(uint) { + return x + 3; + } +} +// ---- diff --git a/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol b/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol new file mode 100644 index 000000000..e844de080 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/writing_after_initialization.sol @@ -0,0 +1,10 @@ +contract C { + uint immutable x = 0; + + function f() internal { + x = 1; + } +} +// ---- +// TypeError: (76-77): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError: (76-77): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol b/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol new file mode 100644 index 000000000..592633379 --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/writing_after_initialization_modifier.sol @@ -0,0 +1,12 @@ +contract C { + uint immutable x = 0; + + function f() readX internal { } + + modifier readX() { + _; x = 1; + } +} +// ---- +// TypeError: (111-112): Immutable variables can only be initialized inline or assigned directly in the constructor. +// TypeError: (111-112): Immutable state variable already initialized. diff --git a/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol b/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol index 1c0e7a46f..9028c7ccc 100644 --- a/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol +++ b/test/libsolidity/syntaxTests/viewPureChecker/immutable.sol @@ -1,8 +1,8 @@ contract B { - uint immutable x; + uint immutable x = 1; function f() public pure returns (uint) { return x; } } // ---- -// TypeError: (96-97): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". +// TypeError: (100-101): Function declared as pure, but this expression (potentially) reads from the environment or state and thus requires "view". From cb66274827d83236632ba648816323299bbdc8ec Mon Sep 17 00:00:00 2001 From: hrkrshnn Date: Thu, 2 Apr 2020 17:33:35 +0530 Subject: [PATCH 53/76] Added changelog --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index f20b05aa5..05b3fa8c1 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Important Bugfixes: Language Features: + * Allow local storage variables to be declared without initialization, as long as they are assigned before they are accessed. Compiler Features: From a7e1ef6a5060d1963d67db4a47e9c17579b7338a Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Wed, 1 Apr 2020 19:11:03 -0500 Subject: [PATCH 54/76] [build-system] Add -Wpessimizing-move & -Wredundant-move warnings. -Wpessimizing-move warns when a call to std::move would prevent copy elision if the argument was not wrapped in a call. This happens when moving a local variable in a return statement when the variable is the same type as the return type or using a move to create a new object from a temporary object. -Wredundant-move warns when an implicit move would already be made, so the std::move call is not needed, such as when moving a local variable in a return that is different from the return type. --- cmake/EthCompilerSettings.cmake | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cmake/EthCompilerSettings.cmake b/cmake/EthCompilerSettings.cmake index cc4489e9f..b36af7e3f 100644 --- a/cmake/EthCompilerSettings.cmake +++ b/cmake/EthCompilerSettings.cmake @@ -26,6 +26,17 @@ eth_add_cxx_compiler_flag_if_supported(-Wimplicit-fallthrough) # Prevent the path of the source directory from ending up in the binary via __FILE__ macros. eth_add_cxx_compiler_flag_if_supported("-fmacro-prefix-map=${CMAKE_SOURCE_DIR}=/solidity") +# -Wpessimizing-move warns when a call to std::move would prevent copy elision +# if the argument was not wrapped in a call. This happens when moving a local +# variable in a return statement when the variable is the same type as the +# return type or using a move to create a new object from a temporary object. +eth_add_cxx_compiler_flag_if_supported(-Wpessimizing-move) + +# -Wredundant-move warns when an implicit move would already be made, so the +# std::move call is not needed, such as when moving a local variable in a return +# that is different from the return type. +eth_add_cxx_compiler_flag_if_supported(-Wredundant-move) + if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) # Enables all the warnings about constructions that some users consider questionable, # and that are easy to avoid. Also enable some extra warning flags that are not From 39ff0deb051a5dfc2c2d2403d4f580d31d9bcea9 Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 2 Mar 2020 21:42:46 +0100 Subject: [PATCH 55/76] Zero initialize memory arrays --- libsolidity/ast/Types.cpp | 2 +- libsolidity/codegen/YulUtilFunctions.cpp | 170 +++++++++++++++--- libsolidity/codegen/YulUtilFunctions.h | 28 ++- .../codegen/ir/IRGenerationContext.cpp | 6 +- libsolidity/codegen/ir/IRGenerator.cpp | 13 ++ libsolidity/codegen/ir/IRGenerator.h | 3 + .../codegen/ir/IRGeneratorForStatements.cpp | 35 +++- .../codegen/ir/IRGeneratorForStatements.h | 7 + .../standard_ir_requested/output.json | 2 + .../yul_string_format_ascii/output.json | 14 ++ .../output.json | 14 ++ .../output.json | 14 ++ .../yul_string_format_ascii_long/output.json | 14 ++ .../yul_string_format_hex/output.json | 14 ++ .../viaYul/array_function_pointers.sol | 29 +++ .../array_2d_zeroed_memory_index_access.sol | 24 +++ .../array_array_static.sol | 22 +++ ...eturn_param_zeroed_memory_index_access.sol | 17 ++ ...rray_static_zeroed_memory_index_access.sol | 19 ++ .../array_zeroed_memory_index_access.sol | 22 +++ .../viaYul/function_pointers.sol | 25 +++ .../viaYul/return_storage_pointers.sol | 16 ++ 22 files changed, 475 insertions(+), 35 deletions(-) create mode 100644 test/libsolidity/semanticTests/viaYul/array_function_pointers.sol create mode 100644 test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_2d_zeroed_memory_index_access.sol create mode 100644 test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_array_static.sol create mode 100644 test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_return_param_zeroed_memory_index_access.sol create mode 100644 test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_zeroed_memory_index_access.sol create mode 100644 test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_zeroed_memory_index_access.sol create mode 100644 test/libsolidity/semanticTests/viaYul/function_pointers.sol create mode 100644 test/libsolidity/semanticTests/viaYul/return_storage_pointers.sol diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 76eeb4fce..3fc649caf 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2338,7 +2338,7 @@ TypePointers StructType::memoryMemberTypes() const TypePointers types; for (ASTPointer const& variable: m_struct.members()) if (variable->annotation().type->canLiveOutsideStorage()) - types.push_back(variable->annotation().type); + types.push_back(TypeProvider::withLocationIfReference(DataLocation::Memory, variable->annotation().type)); return types; } diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index ba664dbb6..28a4f69fb 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -658,6 +658,8 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type) solUnimplementedAssert(!_type.isByteArray(), "Byte Arrays not yet implemented!"); solUnimplementedAssert(_type.baseType()->storageBytes() <= 32, "Base type is not yet implemented."); + solAssert(_type.baseType()->isValueType(), ""); + string functionName = "array_push_zero_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( @@ -794,6 +796,7 @@ string YulUtilFunctions::arrayConvertLengthToSize(ArrayType const& _type) }); } + string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type) { solAssert(_type.dataStoredIn(DataLocation::Memory), ""); @@ -1178,7 +1181,7 @@ string YulUtilFunctions::writeToMemoryFunction(Type const& _type) return Whiskers(R"( function (memPtr, value) { mstore(memPtr, value) - } + } )") ("functionName", functionName) .render(); @@ -1202,7 +1205,7 @@ string YulUtilFunctions::writeToMemoryFunction(Type const& _type) return Whiskers(R"( function (memPtr, value) { mstore(memPtr, (value)) - } + } )") ("functionName", functionName) ("cleanup", cleanupFunction(_type)) @@ -1334,28 +1337,112 @@ string YulUtilFunctions::allocationFunction() }); } -string YulUtilFunctions::allocateMemoryArrayFunction(ArrayType const& _type) +string YulUtilFunctions::zeroMemoryArrayFunction(ArrayType const& _type) { - solUnimplementedAssert(!_type.isByteArray(), ""); + if (_type.baseType()->hasSimpleZeroValueInMemory()) + return zeroMemoryFunction(*_type.baseType()); + return zeroComplexMemoryArrayFunction(_type); +} - string functionName = "allocate_memory_array_" + _type.identifier(); +string YulUtilFunctions::zeroMemoryFunction(Type const& _type) +{ + solAssert(_type.hasSimpleZeroValueInMemory(), ""); + + string functionName = "zero_memory_chunk_" + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( - function (length) -> memPtr { - memPtr := ((length)) - - mstore(memPtr, length) - + function (dataStart, dataSizeInBytes) { + calldatacopy(dataStart, calldatasize(), dataSizeInBytes) } )") ("functionName", functionName) - ("alloc", allocationFunction()) - ("allocSize", arrayAllocationSizeFunction(_type)) - ("dynamic", _type.isDynamicallySized()) .render(); }); } +string YulUtilFunctions::zeroComplexMemoryArrayFunction(ArrayType const& _type) +{ + solAssert(!_type.baseType()->hasSimpleZeroValueInMemory(), ""); + + string functionName = "zero_complex_memory_array_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + solAssert(_type.memoryStride() == 32, ""); + return Whiskers(R"( + function (dataStart, dataSizeInBytes) { + for {let i := 0} lt(i, dataSizeInBytes) { i := add(i, ) } { + mstore(add(dataStart, i), ()) + } + } + )") + ("functionName", functionName) + ("stride", to_string(_type.memoryStride())) + ("zeroValue", zeroValueFunction(*_type.baseType(), false)) + .render(); + }); +} + +string YulUtilFunctions::allocateAndInitializeMemoryArrayFunction(ArrayType const& _type) +{ + solUnimplementedAssert(!_type.isByteArray(), ""); + + string functionName = "allocate_and_zero_memory_array_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function (length) -> memPtr { + let allocSize := (length) + memPtr := (allocSize) + let dataStart := memPtr + let dataSize := allocSize + + dataStart := add(dataStart, 32) + dataSize := sub(dataSize, 32) + mstore(memPtr, length) + + (dataStart, dataSize) + } + )") + ("functionName", functionName) + ("alloc", allocationFunction()) + ("allocSize", arrayAllocationSizeFunction(_type)) + ("zeroArrayFunction", zeroMemoryArrayFunction(_type)) + ("dynamic", _type.isDynamicallySized()) + .render(); + }); +} + +string YulUtilFunctions::allocateAndInitializeMemoryStructFunction(StructType const& _type) +{ + string functionName = "allocate_and_initialize_memory_struct_" + _type.identifier(); + return m_functionCollector.createFunction(functionName, [&]() { + Whiskers templ(R"( + function () -> memPtr { + let allocSize := () + memPtr := (allocSize) + let offset := memPtr + <#member> + mstore(offset, ()) + offset := add(offset, 32) + + } + )"); + templ("functionName", functionName); + templ("alloc", allocationFunction()); + + TypePointers const& members = _type.memoryMemberTypes(); + templ("allocSize", _type.memoryDataSize().str()); + + vector> memberParams(members.size()); + for (size_t i = 0; i < members.size(); ++i) + { + solAssert(members[i]->memoryHeadSize() == 32, ""); + solAssert(members[i]->dataStoredIn(DataLocation::Memory), ""); + memberParams[i]["zeroValue"] = zeroValueFunction(*members[i], false); + } + templ("member", memberParams); + return templ.render(); + }); +} + string YulUtilFunctions::conversionFunction(Type const& _from, Type const& _to) { if (_from.category() == Type::Category::Function) @@ -1884,23 +1971,58 @@ string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type) }); } -string YulUtilFunctions::zeroValueFunction(Type const& _type) +string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes) { - solUnimplementedAssert(_type.sizeOnStack() == 1, "Stacksize not yet implemented!"); - solUnimplementedAssert(_type.isValueType(), "Zero value for non-value types not yet implemented"); + solAssert(_type.category() != Type::Category::Mapping, ""); - string const functionName = "zero_value_for_" + _type.identifier(); + string const functionName = "zero_value_for_" + string(_splitFunctionTypes ? "split_" : "") + _type.identifier(); return m_functionCollector.createFunction(functionName, [&]() { - return Whiskers(R"( - function () -> ret { - - } - )") + FunctionType const* fType = dynamic_cast(&_type); + if (fType && fType->kind() == FunctionType::Kind::External && _splitFunctionTypes) + return Whiskers(R"( + function () -> retAddress, retFunction { + retAddress := 0 + retFunction := 0 + } + )") ("functionName", functionName) - ("body", "ret := 0x0") .render(); - }); + + Whiskers templ(R"( + function () -> ret { + ret := + } + )"); + templ("functionName", functionName); + + if (_type.isValueType()) + { + solAssert(( + _type.hasSimpleZeroValueInMemory() || + (fType && (fType->kind() == FunctionType::Kind::Internal || fType->kind() == FunctionType::Kind::External)) + ), ""); + templ("zeroValue", "0"); + } + else + { + solAssert(_type.dataStoredIn(DataLocation::Memory), ""); + if (auto const* arrayType = dynamic_cast(&_type)) + { + if (_type.isDynamicallySized()) + templ("zeroValue", to_string(CompilerUtils::zeroPointer)); + else + templ("zeroValue", allocateAndInitializeMemoryArrayFunction(*arrayType) + "(" + to_string(unsigned(arrayType->length())) + ")"); + + } + else if (auto const* structType = dynamic_cast(&_type)) + templ("zeroValue", allocateAndInitializeMemoryStructFunction(*structType) + "()"); + else + solUnimplementedAssert(false, ""); + } + + return templ.render(); + }); } string YulUtilFunctions::storageSetToZeroFunction(Type const& _type) diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 8841e98ad..b902f9f64 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -37,6 +37,7 @@ class Type; class ArrayType; class MappingType; class IntegerType; +class StructType; /** * Component that can generate various useful Yul functions. @@ -154,6 +155,7 @@ public: /// to store an array in memory given its length (internally encoded, not ABI encoded). /// The function reverts for too large lengths. std::string arrayAllocationSizeFunction(ArrayType const& _type); + /// @returns the name of a function that converts a storage slot number /// a memory pointer or a calldata pointer to the slot number / memory pointer / calldata pointer /// for the data position of an array which is stored in that slot / memory area / calldata area. @@ -250,10 +252,27 @@ public: /// Return value: pointer std::string allocationFunction(); - /// @returns the name of a function that allocates a memory array. + /// @returns the name of a function that zeroes an array. + /// signature: (dataStart, dataSizeInBytes) -> + std::string zeroMemoryArrayFunction(ArrayType const& _type); + + /// @returns the name of a function that zeroes a chunk of memory. + /// signature: (dataStart, dataSizeInBytes) -> + std::string zeroMemoryFunction(Type const& _type); + + /// @returns the name of a function that zeroes an array + /// where the base does not have simple zero value in memory. + /// signature: (dataStart, dataSizeInBytes) -> + std::string zeroComplexMemoryArrayFunction(ArrayType const& _type); + + /// @returns the name of a function that allocates and zeroes a memory array. /// For dynamic arrays it adds space for length and stores it. /// signature: (length) -> memPtr - std::string allocateMemoryArrayFunction(ArrayType const& _type); + std::string allocateAndInitializeMemoryArrayFunction(ArrayType const& _type); + + /// @returns the name of a function that allocates and zeroes a memory struct. + /// signature: (members) -> memPtr + std::string allocateAndInitializeMemoryStructFunction(StructType const& _type); /// @returns the name of the function that converts a value of type @a _from /// to a value of type @a _to. The resulting vale is guaranteed to be in range @@ -288,8 +307,9 @@ public: std::string negateNumberCheckedFunction(Type const& _type); /// @returns the name of a function that returns the zero value for the - /// provided type - std::string zeroValueFunction(Type const& _type); + /// provided type. + /// @param _splitFunctionTypes if false, returns two zeroes + std::string zeroValueFunction(Type const& _type, bool _splitFunctionTypes = true); /// @returns the name of a function that will set the given storage item to /// zero diff --git a/libsolidity/codegen/ir/IRGenerationContext.cpp b/libsolidity/codegen/ir/IRGenerationContext.cpp index 288da5937..26f6f0309 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.cpp +++ b/libsolidity/codegen/ir/IRGenerationContext.cpp @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -112,9 +113,10 @@ string IRGenerationContext::internalDispatch(size_t _in, size_t _out) for (auto const& contract: mostDerivedContract().annotation().linearizedBaseContracts) for (FunctionDefinition const* function: contract->definedFunctions()) if ( + FunctionType const* functionType = TypeProvider::function(*function)->asCallableFunction(false); !function->isConstructor() && - function->parameters().size() == _in && - function->returnParameters().size() == _out + TupleType(functionType->parameterTypes()).sizeOnStack() == _in && + TupleType(functionType->returnParameterTypes()).sizeOnStack() == _out ) { // 0 is reserved for uninitialized function pointers diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index 2aff9c3f8..5ae4b71d7 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -133,6 +133,7 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) return m_context.functionCollector().createFunction(functionName, [&]() { Whiskers t(R"( function () { + } )"); @@ -142,9 +143,14 @@ string IRGenerator::generateFunction(FunctionDefinition const& _function) params += (params.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList(); t("params", params); string retParams; + string retInit; for (auto const& varDecl: _function.returnParameters()) + { retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl).commaSeparatedList(); + retInit += generateInitialAssignment(*varDecl); + } t("returns", retParams.empty() ? "" : " -> " + retParams); + t("initReturnVariables", retInit); t("body", generate(_function.body())); return t.render(); }); @@ -226,6 +232,13 @@ string IRGenerator::generateGetter(VariableDeclaration const& _varDecl) } } +string IRGenerator::generateInitialAssignment(VariableDeclaration const& _varDecl) +{ + IRGeneratorForStatements generator(m_context, m_utils); + generator.initializeLocalVar(_varDecl); + return generator.code(); +} + string IRGenerator::constructorCode(ContractDefinition const& _contract) { // Initialization of state variables in base-to-derived order. diff --git a/libsolidity/codegen/ir/IRGenerator.h b/libsolidity/codegen/ir/IRGenerator.h index d1ec580f6..e0c1dcd4f 100644 --- a/libsolidity/codegen/ir/IRGenerator.h +++ b/libsolidity/codegen/ir/IRGenerator.h @@ -61,6 +61,9 @@ private: /// Generates a getter for the given declaration and returns its name std::string generateGetter(VariableDeclaration const& _varDecl); + /// Generates code that assigns the initial value of the respective type. + std::string generateInitialAssignment(VariableDeclaration const& _varDecl); + std::string constructorCode(ContractDefinition const& _contract); std::string deployCode(ContractDefinition const& _contract); std::string callValueCheck(); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 8a98e8d36..57c8cb152 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -154,6 +154,19 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va } } +void IRGeneratorForStatements::initializeLocalVar(VariableDeclaration const& _varDecl) +{ + solAssert(m_context.isLocalVariable(_varDecl), "Must be a local variable."); + + auto const* type = _varDecl.type(); + if (auto const* refType = dynamic_cast(type)) + if (refType->dataStoredIn(DataLocation::Storage) && refType->isPointer()) + return; + + IRVariable zero = zeroValue(*type); + assign(m_context.localVariable(_varDecl), zero); +} + void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement) { if (Expression const* expression = _varDeclStatement.initialValue()) @@ -179,7 +192,10 @@ void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _var else for (auto const& decl: _varDeclStatement.declarations()) if (decl) + { declare(m_context.addLocalVariable(*decl)); + initializeLocalVar(*decl); + } } bool IRGeneratorForStatements::visit(Conditional const& _conditional) @@ -670,7 +686,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) IRVariable value = convert(*arguments[0], *TypeProvider::uint256()); define(_functionCall) << - m_utils.allocateMemoryArrayFunction(arrayType) << + m_utils.allocateAndInitializeMemoryArrayFunction(arrayType) << "(" << value.commaSeparatedList() << ")\n"; @@ -1439,6 +1455,12 @@ std::ostream& IRGeneratorForStatements::define(IRVariable const& _var) return m_code; } +void IRGeneratorForStatements::declare(IRVariable const& _var) +{ + if (_var.type().sizeOnStack() > 0) + m_code << "let " << _var.commaSeparatedList() << "\n"; +} + void IRGeneratorForStatements::declareAssign(IRVariable const& _lhs, IRVariable const& _rhs, bool _declare) { string output; @@ -1458,10 +1480,15 @@ void IRGeneratorForStatements::declareAssign(IRVariable const& _lhs, IRVariable _rhs.commaSeparatedList() << ")\n"; } -void IRGeneratorForStatements::declare(IRVariable const& _var) + +IRVariable IRGeneratorForStatements::zeroValue(Type const& _type, bool _splitFunctionTypes) { - if (_var.type().sizeOnStack() > 0) - m_code << "let " << _var.commaSeparatedList() << "\n"; + IRVariable irVar{ + "zero_value_for_type_" + _type.identifier() + m_context.newYulVariable(), + _type + }; + define(irVar) << m_utils.zeroValueFunction(_type, _splitFunctionTypes) << "()\n"; + return irVar; } void IRGeneratorForStatements::appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr) diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.h b/libsolidity/codegen/ir/IRGeneratorForStatements.h index 39dbb67d7..710961cd7 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.h +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.h @@ -46,6 +46,8 @@ public: /// Generates code to initialize the given state variable. void initializeStateVar(VariableDeclaration const& _varDecl); + /// Generates code to initialize the given local variable. + void initializeLocalVar(VariableDeclaration const& _varDecl); void endVisit(VariableDeclarationStatement const& _variableDeclaration) override; bool visit(Conditional const& _conditional) override; @@ -100,6 +102,11 @@ private: void declareAssign(IRVariable const& _var, IRVariable const& _value, bool _define); + /// @returns an IRVariable with the zero + /// value of @a _type. + /// @param _splitFunctionTypes if false, returns two zeroes + IRVariable zeroValue(Type const& _type, bool _splitFunctionTypes = true); + void appendAndOrOperatorCode(BinaryOperation const& _binOp); void appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr); diff --git a/test/cmdlineTests/standard_ir_requested/output.json b/test/cmdlineTests/standard_ir_requested/output.json index 82e974025..cb89a6911 100644 --- a/test/cmdlineTests/standard_ir_requested/output.json +++ b/test/cmdlineTests/standard_ir_requested/output.json @@ -20,6 +20,7 @@ object \"C_6\" { function fun_f_5() { + } } @@ -69,6 +70,7 @@ object \"C_6\" { function fun_f_5() { + } function shift_right_224_unsigned(value) -> newValue { diff --git a/test/cmdlineTests/yul_string_format_ascii/output.json b/test/cmdlineTests/yul_string_format_ascii/output.json index fc88acf50..24a2a10aa 100644 --- a/test/cmdlineTests/yul_string_format_ascii/output.json +++ b/test/cmdlineTests/yul_string_format_ascii/output.json @@ -35,11 +35,18 @@ object \"C_10\" { } function fun_f_9() -> vloc__4_mpos { + let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos + vloc__4_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() leave } + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + } object \"C_10_deployed\" { code { @@ -131,6 +138,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4_mpos { + let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos + vloc__4_mpos := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_string_memory_ptr() leave @@ -147,6 +157,10 @@ object \"C_10\" { } + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + } } } diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json index 428c7911e..8028e5dd6 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32/output.json @@ -23,11 +23,18 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes32_1 := zero_value_for_split_t_bytes32() + vloc__4 := zero_value_for_type_t_bytes32_1 + vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() leave } + function zero_value_for_split_t_bytes32() -> ret { + ret := 0 + } + } object \"C_10_deployed\" { code { @@ -88,6 +95,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes32_1 := zero_value_for_split_t_bytes32() + vloc__4 := zero_value_for_type_t_bytes32_1 + vloc__4 := convert_t_stringliteral_9f0adad0a59b05d2e04a1373342b10b9eb16c57c164c8a3bfcbf46dccee39a21_to_t_bytes32() leave @@ -100,6 +110,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes32() -> ret { + ret := 0 + } + } } } diff --git a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json index 4a348343b..a9588d145 100644 --- a/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_bytes32_from_number/output.json @@ -27,6 +27,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() + vloc__4 := zero_value_for_type_t_bytes4_1 + let expr_6 := 0x61626364 vloc__4 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_6) leave @@ -40,6 +43,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes4() -> ret { + ret := 0 + } + } object \"C_10_deployed\" { code { @@ -104,6 +111,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() + vloc__4 := zero_value_for_type_t_bytes4_1 + let expr_6 := 0x61626364 vloc__4 := convert_t_rational_1633837924_by_1_to_t_bytes4(expr_6) leave @@ -124,6 +134,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes4() -> ret { + ret := 0 + } + } } } diff --git a/test/cmdlineTests/yul_string_format_ascii_long/output.json b/test/cmdlineTests/yul_string_format_ascii_long/output.json index 4302701bd..552712154 100644 --- a/test/cmdlineTests/yul_string_format_ascii_long/output.json +++ b/test/cmdlineTests/yul_string_format_ascii_long/output.json @@ -39,11 +39,18 @@ object \"C_10\" { } function fun_f_9() -> vloc__4_mpos { + let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos + vloc__4_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() leave } + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + } object \"C_10_deployed\" { code { @@ -139,6 +146,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4_mpos { + let zero_value_for_type_t_string_memory_ptr_1_mpos := zero_value_for_split_t_string_memory_ptr() + vloc__4_mpos := zero_value_for_type_t_string_memory_ptr_1_mpos + vloc__4_mpos := convert_t_stringliteral_d6604f85ac07e2b33103a620b3d3d75b0473c7214912beded67b9b624d41c571_to_t_string_memory_ptr() leave @@ -155,6 +165,10 @@ object \"C_10\" { } + function zero_value_for_split_t_string_memory_ptr() -> ret { + ret := 96 + } + } } } diff --git a/test/cmdlineTests/yul_string_format_hex/output.json b/test/cmdlineTests/yul_string_format_hex/output.json index 0b92fceea..48c1146fa 100644 --- a/test/cmdlineTests/yul_string_format_hex/output.json +++ b/test/cmdlineTests/yul_string_format_hex/output.json @@ -27,6 +27,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() + vloc__4 := zero_value_for_type_t_bytes4_1 + let expr_6 := 0xaabbccdd vloc__4 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_6) leave @@ -40,6 +43,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes4() -> ret { + ret := 0 + } + } object \"C_10_deployed\" { code { @@ -104,6 +111,9 @@ object \"C_10\" { } function fun_f_9() -> vloc__4 { + let zero_value_for_type_t_bytes4_1 := zero_value_for_split_t_bytes4() + vloc__4 := zero_value_for_type_t_bytes4_1 + let expr_6 := 0xaabbccdd vloc__4 := convert_t_rational_2864434397_by_1_to_t_bytes4(expr_6) leave @@ -124,6 +134,10 @@ object \"C_10\" { } + function zero_value_for_split_t_bytes4() -> ret { + ret := 0 + } + } } } diff --git a/test/libsolidity/semanticTests/viaYul/array_function_pointers.sol b/test/libsolidity/semanticTests/viaYul/array_function_pointers.sol new file mode 100644 index 000000000..5344ae67f --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_function_pointers.sol @@ -0,0 +1,29 @@ +contract C { + function f(uint n, uint m) public { + function() internal returns (uint)[] memory arr = new function() internal returns (uint)[](n); + arr[m](); + } + function f2(uint n, uint m, uint a, uint b) public { + function() internal returns (uint)[][] memory arr = new function() internal returns (uint)[][](n); + for (uint i = 0; i < n; ++i) + arr[i] = new function() internal returns (uint)[](m); + arr[a][b](); + } + function g(uint n, uint m) public { + function() external returns (uint)[] memory arr = new function() external returns (uint)[](n); + arr[m](); + } + function g2(uint n, uint m, uint a, uint b) public { + function() external returns (uint)[][] memory arr = new function() external returns (uint)[][](n); + for (uint i = 0; i < n; ++i) + arr[i] = new function() external returns (uint)[](m); + arr[a][b](); + } +} +// ==== +// compileViaYul: also +// ---- +// f(uint256,uint256): 1823621, 12323 -> FAILURE +// f2(uint256,uint256,uint256,uint256): 18723921, 1823621, 123, 12323 -> FAILURE +// g(uint256,uint256): 1823621, 12323 -> FAILURE +// g2(uint256,uint256,uint256,uint256): 18723921, 1823621, 123, 12323 -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_2d_zeroed_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_2d_zeroed_memory_index_access.sol new file mode 100644 index 000000000..aa1349cb8 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_2d_zeroed_memory_index_access.sol @@ -0,0 +1,24 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s, uint n, uint m, uint a, uint b) public returns (uint) { + map[s] = 0; + uint[][] memory x = new uint[][](n); + for (uint i = 0; i < n; ++i) + x[i] = new uint[](m); + return x[a][b]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 0, 0, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 1, 3, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 3, 3, 32, "01234567890123456789012345678901" -> FAILURE +// set(string,uint256,uint256,uint256,uint256): 0xa0, 2, 4, 1, 5, 32, "01234567890123456789012345678901" -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_array_static.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_array_static.sol new file mode 100644 index 000000000..1f490ba59 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_array_static.sol @@ -0,0 +1,22 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s, uint n, uint m) public returns (uint) { + map[s] = 0; + uint[4][] memory x = new uint[4][](n); + return x[m][0]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string,uint256,uint256): 0x60, 2, 0, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 2, 1, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 2, 2, 32, "01234567890123456789012345678901" -> FAILURE +// set(string,uint256,uint256): 0x60, 200, 199, 32, "01234567890123456789012345678901" -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_return_param_zeroed_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_return_param_zeroed_memory_index_access.sol new file mode 100644 index 000000000..31dd0a66a --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_return_param_zeroed_memory_index_access.sol @@ -0,0 +1,17 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s) public returns (uint[3] memory x, uint[2] memory y, uint[] memory z, uint t) { + map[s] = 0; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string): 0x20, 32, "01234567890123456789012345678901" -> 0, 0, 0, 0, 0, 0xe0, 0, 0 diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_zeroed_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_zeroed_memory_index_access.sol new file mode 100644 index 000000000..6cf73bd8e --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_static_zeroed_memory_index_access.sol @@ -0,0 +1,19 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s) public returns (uint) { + map[s] = 0; + uint[3] memory x; + return x[2]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string): 0x20, 32, "01234567890123456789012345678901" -> 0 diff --git a/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_zeroed_memory_index_access.sol b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_zeroed_memory_index_access.sol new file mode 100644 index 000000000..1f9e70db7 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/array_memory_allocation/array_zeroed_memory_index_access.sol @@ -0,0 +1,22 @@ +contract C { + uint test1; + uint test2; + uint test3; + uint test4; + uint test5; + uint test6; + uint test7; + mapping (string => uint) map; + function set(string memory s, uint n, uint a) public returns (uint) { + map[s] = 0; + uint[] memory x = new uint[](n); + return x[a]; + } +} +// ==== +// compileViaYul: also +// ---- +// set(string,uint256,uint256): 0x60, 5, 0, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 5, 1, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 5, 4, 32, "01234567890123456789012345678901" -> 0 +// set(string,uint256,uint256): 0x60, 5, 5, 32, "01234567890123456789012345678901" -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/function_pointers.sol b/test/libsolidity/semanticTests/viaYul/function_pointers.sol new file mode 100644 index 000000000..5bd433a3d --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/function_pointers.sol @@ -0,0 +1,25 @@ +contract C { + function f() public { + function() internal returns (uint) _f; + _f(); + } + function g() public { + function() external returns (uint) _g; + _g(); + } + function h1() internal returns (function() internal returns (uint) _h) {} + function h2() public { + h1()(); + } + function k1() internal returns (function() external returns (uint) _k) {} + function k2() public { + k1()(); + } +} +// ==== +// compileViaYul: also +// ---- +// f() -> FAILURE +// g() -> FAILURE +// h2() -> FAILURE +// k2() -> FAILURE diff --git a/test/libsolidity/semanticTests/viaYul/return_storage_pointers.sol b/test/libsolidity/semanticTests/viaYul/return_storage_pointers.sol new file mode 100644 index 000000000..d0085dec1 --- /dev/null +++ b/test/libsolidity/semanticTests/viaYul/return_storage_pointers.sol @@ -0,0 +1,16 @@ +contract C { + uint[] arr1; + uint[][] arr2; + function f() internal returns (uint[] storage ptr1, uint[][] storage ptr2) { + ptr1 = arr1; + ptr2 = arr2; + } + function g() public returns (uint, uint) { + return (arr1.length, arr2.length); + } + +} +// ==== +// compileViaYul: also +// ---- +// g() -> 0, 0 From ef2bef9ddc2562864285f8f386f005fa36e58a2f Mon Sep 17 00:00:00 2001 From: hrkrshnn Date: Thu, 2 Apr 2020 20:30:43 +0530 Subject: [PATCH 56/76] Added error for interface function with modifiers; test case --- libsolidity/analysis/SyntaxChecker.cpp | 4 +++- .../588_interface_function_modifier.sol | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/syntaxTests/nameAndTypeResolution/588_interface_function_modifier.sol diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp index 74fa8d458..ba973bffa 100644 --- a/libsolidity/analysis/SyntaxChecker.cpp +++ b/libsolidity/analysis/SyntaxChecker.cpp @@ -303,7 +303,9 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function) ); } - if (!_function.isImplemented() && !_function.modifiers().empty()) + if (m_isInterface && !_function.modifiers().empty()) + m_errorReporter.syntaxError(_function.location(), "Functions in interfaces cannot have modifiers."); + else if (!_function.isImplemented() && !_function.modifiers().empty()) m_errorReporter.syntaxError(_function.location(), "Functions without implementation cannot have modifiers."); return true; diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/588_interface_function_modifier.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/588_interface_function_modifier.sol new file mode 100644 index 000000000..b94334d38 --- /dev/null +++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/588_interface_function_modifier.sol @@ -0,0 +1,6 @@ +interface I { + function f() external m pure returns (uint); + modifier m() { _; } +} +// ---- +// SyntaxError: (16-60): Functions in interfaces cannot have modifiers. From 0d3303e4af0878a082043c66f36e681f131fc6b2 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 2 Apr 2020 17:46:29 +0200 Subject: [PATCH 57/76] Changelog entry for immutable variables. --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 82a40eced..2c757e402 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Important Bugfixes: Language Features: * Allow local storage variables to be declared without initialization, as long as they are assigned before they are accessed. + * State variables can be marked ``immutable`` which causes them to be read-only, but assignable in the constructor. The value will be stored directly in the code. Compiler Features: From bdcfd71f343f701d3fccf12ca99c2cbae2705dcc Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 2 Apr 2020 18:26:33 +0200 Subject: [PATCH 58/76] Skip verification for external access. --- libsolidity/analysis/ImmutableValidator.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libsolidity/analysis/ImmutableValidator.cpp b/libsolidity/analysis/ImmutableValidator.cpp index e80902009..6e0d0f6c7 100644 --- a/libsolidity/analysis/ImmutableValidator.cpp +++ b/libsolidity/analysis/ImmutableValidator.cpp @@ -74,6 +74,11 @@ bool ImmutableValidator::visit(MemberAccess const& _memberAccess) { _memberAccess.expression().accept(*this); + if (auto contractType = dynamic_cast(_memberAccess.expression().annotation().type)) + if (!contractType->isSuper()) + // external access, no analysis needed. + return false; + if (auto varDecl = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) analyseVariableReference(*varDecl, _memberAccess); else if (auto funcType = dynamic_cast(_memberAccess.annotation().type)) @@ -187,7 +192,8 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va else if (m_inConstructionContext) m_errorReporter.typeError( _expression.location(), - "Immutable variables cannot be read during contract creation time, which means they cannot be read in the constructor or any function or modifier called from it." + "Immutable variables cannot be read during contract creation time, which means " + "they cannot be read in the constructor or any function or modifier called from it." ); } From d7a39c86ce135b6c6f5435f3099fc0dafe589755 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 2 Apr 2020 17:13:31 +0200 Subject: [PATCH 59/76] Accessors for immutable variables. --- libsolidity/codegen/ContractCompiler.cpp | 2 -- libsolidity/codegen/ExpressionCompiler.cpp | 20 ++++++++++++++----- .../semanticTests/immutable/complexGetter.sol | 12 +++++++++++ .../semanticTests/immutable/getter.sol | 5 +++++ .../immutable/getter_call_in_constructor.sol | 17 ++++++++++++++++ 5 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 test/libsolidity/semanticTests/immutable/complexGetter.sol create mode 100644 test/libsolidity/semanticTests/immutable/getter.sol create mode 100644 test/libsolidity/semanticTests/immutable/getter_call_in_constructor.sol diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 49b08b6da..822ce3b66 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -566,8 +566,6 @@ bool ContractCompiler::visit(VariableDeclaration const& _variableDeclaration) if (_variableDeclaration.isConstant()) ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals) .appendConstStateVariableAccessor(_variableDeclaration); - else if (_variableDeclaration.immutable()) - solUnimplementedAssert(false, ""); else ExpressionCompiler(m_context, m_optimiserSettings.runOrderLiterals) .appendStateVariableAccessor(_variableDeclaration); diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index a406cdeb8..a02497561 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -91,16 +91,22 @@ void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration co void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& _varDecl) { - solAssert(!_varDecl.isConstant() && !_varDecl.immutable(), ""); + solAssert(!_varDecl.isConstant(), ""); CompilerContext::LocationSetter locationSetter(m_context, _varDecl); FunctionType accessorType(_varDecl); TypePointers paramTypes = accessorType.parameterTypes(); + if (_varDecl.immutable()) + solAssert(paramTypes.empty(), ""); + m_context.adjustStackOffset(1 + CompilerUtils::sizeOnStack(paramTypes)); - // retrieve the position of the variable - auto const& location = m_context.storageLocationOfVariable(_varDecl); - m_context << location.first << u256(location.second); + if (!_varDecl.immutable()) + { + // retrieve the position of the variable + auto const& location = m_context.storageLocationOfVariable(_varDecl); + m_context << location.first << u256(location.second); + } TypePointer returnType = _varDecl.annotation().type; @@ -182,6 +188,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& solAssert(returnTypes.size() >= 1, ""); if (StructType const* structType = dynamic_cast(returnType)) { + solAssert(!_varDecl.immutable(), ""); // remove offset m_context << Instruction::POP; auto const& names = accessorType.returnParameterNames(); @@ -208,7 +215,10 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const& { // simple value or array solAssert(returnTypes.size() == 1, ""); - StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true); + if (_varDecl.immutable()) + ImmutableItem(m_context, _varDecl).retrieveValue(SourceLocation()); + else + StorageItem(m_context, *returnType).retrieveValue(SourceLocation(), true); utils().convertType(*returnType, *returnTypes.front()); retSizeOnStack = returnTypes.front()->sizeOnStack(); } diff --git a/test/libsolidity/semanticTests/immutable/complexGetter.sol b/test/libsolidity/semanticTests/immutable/complexGetter.sol new file mode 100644 index 000000000..851120847 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/complexGetter.sol @@ -0,0 +1,12 @@ +contract C { + function() external returns (uint, uint) immutable public x = this.f; + function f() external pure returns (uint, uint) { + return (1, 2); + } + + function test() external returns (uint, uint) { + return this.x()(); + } +} +// ---- +// test() -> 1, 2 diff --git a/test/libsolidity/semanticTests/immutable/getter.sol b/test/libsolidity/semanticTests/immutable/getter.sol new file mode 100644 index 000000000..bb4b191cf --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/getter.sol @@ -0,0 +1,5 @@ +contract C { + uint immutable public x = 1; +} +// ---- +// x() -> 1 diff --git a/test/libsolidity/semanticTests/immutable/getter_call_in_constructor.sol b/test/libsolidity/semanticTests/immutable/getter_call_in_constructor.sol new file mode 100644 index 000000000..e4aa474f2 --- /dev/null +++ b/test/libsolidity/semanticTests/immutable/getter_call_in_constructor.sol @@ -0,0 +1,17 @@ +contract A { + uint immutable public x = 1; + uint public y; + constructor() public { + y = this.x(); + } +} +contract C { + function f() public returns (bool) { + try new A() { return false; } + catch { return true; } + } +} +// ==== +// EVMVersion: >=tangerineWhistle +// ---- +// f() -> true From cae6e7769f3e3b804228061c87bdcaac20a0324e Mon Sep 17 00:00:00 2001 From: Alexander Arlt Date: Tue, 31 Mar 2020 18:58:22 -0500 Subject: [PATCH 60/76] Apply modernize-use-override. --- libsolidity/analysis/ConstantEvaluator.h | 10 +++++----- libsolidity/analysis/StaticAnalyzer.h | 2 +- libyul/backends/evm/EVMAssembly.h | 2 +- libyul/backends/evm/EVMCodeTransform.h | 8 ++++---- libyul/backends/evm/NoOutputAssembly.h | 2 +- libyul/optimiser/ASTCopier.h | 2 +- libyul/optimiser/ExpressionSimplifier.h | 2 +- libyul/optimiser/FunctionHoister.h | 2 +- libyul/optimiser/NameCollector.cpp | 4 ++-- libyul/optimiser/NameCollector.h | 4 ++-- libyul/optimiser/Semantics.h | 4 ++-- libyul/optimiser/TypeInfo.cpp | 4 ++-- test/libsolidity/SMTChecker.cpp | 4 ++-- test/libsolidity/SolidityExecutionFramework.h | 2 +- .../libsolidity/SolidityExpressionCompiler.cpp | 18 +++++++++--------- test/libsolidity/SolidityParser.cpp | 2 +- test/libyul/SyntaxTest.h | 2 +- tools/solidityUpgrade/Upgrade050.h | 4 ++-- tools/solidityUpgrade/Upgrade060.h | 6 +++--- 19 files changed, 42 insertions(+), 42 deletions(-) diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h index 0f2dd558e..e92113bb8 100644 --- a/libsolidity/analysis/ConstantEvaluator.h +++ b/libsolidity/analysis/ConstantEvaluator.h @@ -54,11 +54,11 @@ public: TypePointer evaluate(Expression const& _expr); private: - virtual void endVisit(BinaryOperation const& _operation); - virtual void endVisit(UnaryOperation const& _operation); - virtual void endVisit(Literal const& _literal); - virtual void endVisit(Identifier const& _identifier); - virtual void endVisit(TupleExpression const& _tuple); + void endVisit(BinaryOperation const& _operation) override; + void endVisit(UnaryOperation const& _operation) override; + void endVisit(Literal const& _literal) override; + void endVisit(Identifier const& _identifier) override; + void endVisit(TupleExpression const& _tuple) override; void setType(ASTNode const& _node, TypePointer const& _type); TypePointer type(ASTNode const& _node); diff --git a/libsolidity/analysis/StaticAnalyzer.h b/libsolidity/analysis/StaticAnalyzer.h index f3ed30b20..57ac54fb1 100644 --- a/libsolidity/analysis/StaticAnalyzer.h +++ b/libsolidity/analysis/StaticAnalyzer.h @@ -50,7 +50,7 @@ class StaticAnalyzer: private ASTConstVisitor public: /// @param _errorReporter provides the error logging functionality. explicit StaticAnalyzer(langutil::ErrorReporter& _errorReporter); - ~StaticAnalyzer(); + ~StaticAnalyzer() override; /// Performs static analysis on the given source unit and all of its sub-nodes. /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings diff --git a/libyul/backends/evm/EVMAssembly.h b/libyul/backends/evm/EVMAssembly.h index e4ba4979a..744d8738b 100644 --- a/libyul/backends/evm/EVMAssembly.h +++ b/libyul/backends/evm/EVMAssembly.h @@ -38,7 +38,7 @@ class EVMAssembly: public AbstractAssembly { public: explicit EVMAssembly(bool _evm15 = false): m_evm15(_evm15) { } - virtual ~EVMAssembly() = default; + ~EVMAssembly() override = default; /// Set a new source location valid starting from the next instruction. void setSourceLocation(langutil::SourceLocation const& _location) override; diff --git a/libyul/backends/evm/EVMCodeTransform.h b/libyul/backends/evm/EVMCodeTransform.h index fb4bc38e1..eae870815 100644 --- a/libyul/backends/evm/EVMCodeTransform.h +++ b/libyul/backends/evm/EVMCodeTransform.h @@ -92,10 +92,10 @@ public: {} public: - void operator()(Identifier const& _identifier); - void operator()(FunctionDefinition const&); - void operator()(ForLoop const&); - void operator()(Block const& _block); + void operator()(Identifier const& _identifier) override; + void operator()(FunctionDefinition const&) override; + void operator()(ForLoop const&) override; + void operator()(Block const& _block) override; private: void increaseRefIfFound(YulString _variableName); diff --git a/libyul/backends/evm/NoOutputAssembly.h b/libyul/backends/evm/NoOutputAssembly.h index bc914f9ac..d72f9ea66 100644 --- a/libyul/backends/evm/NoOutputAssembly.h +++ b/libyul/backends/evm/NoOutputAssembly.h @@ -45,7 +45,7 @@ class NoOutputAssembly: public AbstractAssembly { public: explicit NoOutputAssembly(bool _evm15 = false): m_evm15(_evm15) { } - virtual ~NoOutputAssembly() = default; + ~NoOutputAssembly() override = default; void setSourceLocation(langutil::SourceLocation const&) override {} int stackHeight() const override { return m_stackHeight; } diff --git a/libyul/optimiser/ASTCopier.h b/libyul/optimiser/ASTCopier.h index 0ac0b5f82..3a05d2ca3 100644 --- a/libyul/optimiser/ASTCopier.h +++ b/libyul/optimiser/ASTCopier.h @@ -65,7 +65,7 @@ public: class ASTCopier: public ExpressionCopier, public StatementCopier { public: - virtual ~ASTCopier() = default; + ~ASTCopier() override = default; Expression operator()(Literal const& _literal) override; Expression operator()(Identifier const& _identifier) override; Expression operator()(FunctionCall const&) override; diff --git a/libyul/optimiser/ExpressionSimplifier.h b/libyul/optimiser/ExpressionSimplifier.h index c11ab9bf3..ce83b4e1e 100644 --- a/libyul/optimiser/ExpressionSimplifier.h +++ b/libyul/optimiser/ExpressionSimplifier.h @@ -46,7 +46,7 @@ public: static void run(OptimiserStepContext&, Block& _ast); using ASTModifier::operator(); - virtual void visit(Expression& _expression); + void visit(Expression& _expression) override; private: explicit ExpressionSimplifier(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {} diff --git a/libyul/optimiser/FunctionHoister.h b/libyul/optimiser/FunctionHoister.h index 2a88e1acf..7106c03a4 100644 --- a/libyul/optimiser/FunctionHoister.h +++ b/libyul/optimiser/FunctionHoister.h @@ -42,7 +42,7 @@ public: static void run(OptimiserStepContext&, Block& _ast) { FunctionHoister{}(_ast); } using ASTModifier::operator(); - virtual void operator()(Block& _block); + void operator()(Block& _block) override; private: FunctionHoister() = default; diff --git a/libyul/optimiser/NameCollector.cpp b/libyul/optimiser/NameCollector.cpp index 33a5b624a..f9bb40bf4 100644 --- a/libyul/optimiser/NameCollector.cpp +++ b/libyul/optimiser/NameCollector.cpp @@ -36,9 +36,9 @@ void NameCollector::operator()(VariableDeclaration const& _varDecl) void NameCollector::operator ()(FunctionDefinition const& _funDef) { m_names.emplace(_funDef.name); - for (auto const arg: _funDef.parameters) + for (auto const& arg: _funDef.parameters) m_names.emplace(arg.name); - for (auto const ret: _funDef.returnVariables) + for (auto const& ret: _funDef.returnVariables) m_names.emplace(ret.name); ASTWalker::operator ()(_funDef); } diff --git a/libyul/optimiser/NameCollector.h b/libyul/optimiser/NameCollector.h index 97fbd27c6..a48aa1a3b 100644 --- a/libyul/optimiser/NameCollector.h +++ b/libyul/optimiser/NameCollector.h @@ -61,8 +61,8 @@ public: {} using ASTWalker::operator (); - virtual void operator()(Identifier const& _identifier); - virtual void operator()(FunctionCall const& _funCall); + void operator()(Identifier const& _identifier) override; + void operator()(FunctionCall const& _funCall) override; static std::map countReferences(Block const& _block, CountWhat _countWhat = VariablesAndFunctions); static std::map countReferences(FunctionDefinition const& _function, CountWhat _countWhat = VariablesAndFunctions); diff --git a/libyul/optimiser/Semantics.h b/libyul/optimiser/Semantics.h index 4efc83247..46d2984ca 100644 --- a/libyul/optimiser/Semantics.h +++ b/libyul/optimiser/Semantics.h @@ -104,7 +104,7 @@ public: static bool containsMSize(Dialect const& _dialect, Block const& _ast); using ASTWalker::operator(); - void operator()(FunctionCall const& _funCall); + void operator()(FunctionCall const& _funCall) override; private: MSizeFinder(Dialect const& _dialect): m_dialect(_dialect) {} @@ -129,7 +129,7 @@ public: } using ASTWalker::operator(); - void operator()(Leave const&) { m_leaveFound = true; } + void operator()(Leave const&) override { m_leaveFound = true; } private: LeaveFinder() = default; diff --git a/libyul/optimiser/TypeInfo.cpp b/libyul/optimiser/TypeInfo.cpp index ea2d81a83..adb6e1cce 100644 --- a/libyul/optimiser/TypeInfo.cpp +++ b/libyul/optimiser/TypeInfo.cpp @@ -50,12 +50,12 @@ public: ASTWalker::operator()(_funDef); auto& funType = functionTypes[_funDef.name]; - for (auto const arg: _funDef.parameters) + for (auto const& arg: _funDef.parameters) { funType.parameters.emplace_back(arg.type); variableTypes[arg.name] = arg.type; } - for (auto const ret: _funDef.returnVariables) + for (auto const& ret: _funDef.returnVariables) { funType.returns.emplace_back(ret.type); variableTypes[ret.name] = ret.type; diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp index 06862ad08..6df19c1c7 100644 --- a/test/libsolidity/SMTChecker.cpp +++ b/test/libsolidity/SMTChecker.cpp @@ -34,14 +34,14 @@ namespace solidity::frontend::test class SMTCheckerFramework: public AnalysisFramework { protected: - virtual std::pair + std::pair parseAnalyseAndReturnError( std::string const& _source, bool _reportWarnings = false, bool _insertVersionPragma = true, bool _allowMultipleErrors = false, bool _allowRecoveryErrors = false - ) + ) override { return AnalysisFramework::parseAnalyseAndReturnError( "pragma experimental SMTChecker;\n" + _source, diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h index aaa17525f..2410195fa 100644 --- a/test/libsolidity/SolidityExecutionFramework.h +++ b/test/libsolidity/SolidityExecutionFramework.h @@ -46,7 +46,7 @@ public: ExecutionFramework(_evmVersion), m_showMetadata(solidity::test::CommonOptions::get().showMetadata) {} - virtual bytes const& compileAndRunWithoutCheck( + bytes const& compileAndRunWithoutCheck( std::string const& _sourceCode, u256 const& _value = 0, std::string const& _contractName = "", diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp index cdaee104b..5dfefc0b7 100644 --- a/test/libsolidity/SolidityExpressionCompiler.cpp +++ b/test/libsolidity/SolidityExpressionCompiler.cpp @@ -52,15 +52,15 @@ public: FirstExpressionExtractor(ASTNode& _node): m_expression(nullptr) { _node.accept(*this); } Expression* expression() const { return m_expression; } private: - virtual bool visit(Assignment& _expression) override { return checkExpression(_expression); } - virtual bool visit(UnaryOperation& _expression) override { return checkExpression(_expression); } - virtual bool visit(BinaryOperation& _expression) override { return checkExpression(_expression); } - virtual bool visit(FunctionCall& _expression) override { return checkExpression(_expression); } - virtual bool visit(MemberAccess& _expression) override { return checkExpression(_expression); } - virtual bool visit(IndexAccess& _expression) override { return checkExpression(_expression); } - virtual bool visit(Identifier& _expression) override { return checkExpression(_expression); } - virtual bool visit(ElementaryTypeNameExpression& _expression) override { return checkExpression(_expression); } - virtual bool visit(Literal& _expression) override { return checkExpression(_expression); } + bool visit(Assignment& _expression) override { return checkExpression(_expression); } + bool visit(UnaryOperation& _expression) override { return checkExpression(_expression); } + bool visit(BinaryOperation& _expression) override { return checkExpression(_expression); } + bool visit(FunctionCall& _expression) override { return checkExpression(_expression); } + bool visit(MemberAccess& _expression) override { return checkExpression(_expression); } + bool visit(IndexAccess& _expression) override { return checkExpression(_expression); } + bool visit(Identifier& _expression) override { return checkExpression(_expression); } + bool visit(ElementaryTypeNameExpression& _expression) override { return checkExpression(_expression); } + bool visit(Literal& _expression) override { return checkExpression(_expression); } bool checkExpression(Expression& _expression) { if (m_expression == nullptr) diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 192a26898..12fbc3fce 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -672,7 +672,7 @@ BOOST_AUTO_TEST_CASE(inline_asm_end_location) { public: bool visited = false; - virtual bool visit(InlineAssembly const& _inlineAsm) + bool visit(InlineAssembly const& _inlineAsm) override { auto loc = _inlineAsm.location(); auto asmStr = loc.source->source().substr(loc.start, loc.end - loc.start); diff --git a/test/libyul/SyntaxTest.h b/test/libyul/SyntaxTest.h index b4c838b54..342aa2845 100644 --- a/test/libyul/SyntaxTest.h +++ b/test/libyul/SyntaxTest.h @@ -37,7 +37,7 @@ public: return std::make_unique(_config.filename, _config.evmVersion); } SyntaxTest(std::string const& _filename, langutil::EVMVersion _evmVersion); - virtual ~SyntaxTest() {} + ~SyntaxTest() override {} protected: void parseAndAnalyze() override; diff --git a/tools/solidityUpgrade/Upgrade050.h b/tools/solidityUpgrade/Upgrade050.h index d85b69912..58670e7b2 100644 --- a/tools/solidityUpgrade/Upgrade050.h +++ b/tools/solidityUpgrade/Upgrade050.h @@ -37,7 +37,7 @@ public: void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } private: - void endVisit(frontend::ContractDefinition const& _contract); + void endVisit(frontend::ContractDefinition const& _contract) override; }; /** @@ -52,7 +52,7 @@ public: void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } private: - void endVisit(frontend::FunctionDefinition const& _function); + void endVisit(frontend::FunctionDefinition const& _function) override; }; } diff --git a/tools/solidityUpgrade/Upgrade060.h b/tools/solidityUpgrade/Upgrade060.h index ea29d3a7b..e2386d104 100644 --- a/tools/solidityUpgrade/Upgrade060.h +++ b/tools/solidityUpgrade/Upgrade060.h @@ -35,7 +35,7 @@ public: void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } private: - void endVisit(frontend::ContractDefinition const& _contract); + void endVisit(frontend::ContractDefinition const& _contract) override; }; /** @@ -49,7 +49,7 @@ public: void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } private: - void endVisit(frontend::ContractDefinition const& _contract); + void endVisit(frontend::ContractDefinition const& _contract) override; }; /** @@ -63,7 +63,7 @@ public: void analyze(frontend::SourceUnit const& _sourceUnit) { _sourceUnit.accept(*this); } private: - void endVisit(frontend::ContractDefinition const& _function); + void endVisit(frontend::ContractDefinition const& _function) override; }; } From 7f5857a14620326b4113cc9e48611f832d021db2 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Thu, 26 Mar 2020 15:11:00 -0400 Subject: [PATCH 61/76] Delete copy/move on annotation polymorphic bases --- libsolidity/ast/ASTAnnotations.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index a724472d0..021a742f5 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -47,6 +47,14 @@ using TypePointer = Type const*; struct ASTAnnotation { + ASTAnnotation() = default; + + ASTAnnotation(ASTAnnotation const&) = delete; + ASTAnnotation(ASTAnnotation&&) = delete; + + ASTAnnotation& operator=(ASTAnnotation const&) = delete; + ASTAnnotation& operator=(ASTAnnotation&&) = delete; + virtual ~ASTAnnotation() = default; }; @@ -58,7 +66,16 @@ struct DocTag struct StructurallyDocumentedAnnotation { + StructurallyDocumentedAnnotation() = default; + + StructurallyDocumentedAnnotation(StructurallyDocumentedAnnotation const&) = delete; + StructurallyDocumentedAnnotation(StructurallyDocumentedAnnotation&&) = delete; + + StructurallyDocumentedAnnotation& operator=(StructurallyDocumentedAnnotation const&) = delete; + StructurallyDocumentedAnnotation& operator=(StructurallyDocumentedAnnotation&&) = delete; + virtual ~StructurallyDocumentedAnnotation() = default; + /// Mapping docstring tag name -> content. std::multimap docTags; }; @@ -75,6 +92,16 @@ struct SourceUnitAnnotation: ASTAnnotation struct ScopableAnnotation { + ScopableAnnotation() = default; + + ScopableAnnotation(ScopableAnnotation const&) = delete; + ScopableAnnotation(ScopableAnnotation&&) = delete; + + ScopableAnnotation& operator=(ScopableAnnotation const&) = delete; + ScopableAnnotation& operator=(ScopableAnnotation&&) = delete; + + virtual ~ScopableAnnotation() = default; + /// The scope this declaration resides in. Can be nullptr if it is the global scope. /// Available only after name and type resolution step. ASTNode const* scope = nullptr; From d68c526eaacc1072e8c0ddb4b3513897cce352b7 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Fri, 3 Apr 2020 11:56:51 +0200 Subject: [PATCH 62/76] Disallow external function pointers as immutables. --- libsolidity/analysis/TypeChecker.cpp | 8 ++++++++ .../semanticTests/immutable/complexGetter.sol | 12 ----------- .../immutable/external_function_pointer.sol | 20 ------------------- .../immutable/external_function_pointer.sol | 5 +++++ 4 files changed, 13 insertions(+), 32 deletions(-) delete mode 100644 test/libsolidity/semanticTests/immutable/complexGetter.sol delete mode 100644 test/libsolidity/semanticTests/immutable/external_function_pointer.sol create mode 100644 test/libsolidity/syntaxTests/immutable/external_function_pointer.sol diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 8e7bef338..2bdc706a2 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -481,8 +481,16 @@ bool TypeChecker::visit(VariableDeclaration const& _variable) ); } else if (_variable.immutable()) + { if (!_variable.type()->isValueType()) m_errorReporter.typeError(_variable.location(), "Immutable variables cannot have a non-value type."); + if ( + auto const* functionType = dynamic_cast(_variable.type()); + functionType && functionType->kind() == FunctionType::Kind::External + ) + m_errorReporter.typeError(_variable.location(), "Immutable variables of external function type are not yet supported."); + solAssert(_variable.type()->sizeOnStack() == 1 || m_errorReporter.hasErrors(), ""); + } if (!_variable.isStateVariable()) { diff --git a/test/libsolidity/semanticTests/immutable/complexGetter.sol b/test/libsolidity/semanticTests/immutable/complexGetter.sol deleted file mode 100644 index 851120847..000000000 --- a/test/libsolidity/semanticTests/immutable/complexGetter.sol +++ /dev/null @@ -1,12 +0,0 @@ -contract C { - function() external returns (uint, uint) immutable public x = this.f; - function f() external pure returns (uint, uint) { - return (1, 2); - } - - function test() external returns (uint, uint) { - return this.x()(); - } -} -// ---- -// test() -> 1, 2 diff --git a/test/libsolidity/semanticTests/immutable/external_function_pointer.sol b/test/libsolidity/semanticTests/immutable/external_function_pointer.sol deleted file mode 100644 index c815a18c3..000000000 --- a/test/libsolidity/semanticTests/immutable/external_function_pointer.sol +++ /dev/null @@ -1,20 +0,0 @@ -contract D { - function f() external view returns (uint256) { - return 42; - } -} -contract C { - D d; - function() external view returns(uint256) immutable z; - constructor() public { - d = new D(); - z = d.f; - } - function f() public view returns (uint256) { - assert(z.address == address(d)); - assert(z.selector == D.f.selector); - return z(); - } -} -// ---- -// f() -> 42 diff --git a/test/libsolidity/syntaxTests/immutable/external_function_pointer.sol b/test/libsolidity/syntaxTests/immutable/external_function_pointer.sol new file mode 100644 index 000000000..7ab8393cf --- /dev/null +++ b/test/libsolidity/syntaxTests/immutable/external_function_pointer.sol @@ -0,0 +1,5 @@ +contract C { + function() external immutable f; +} +// ---- +// TypeError: (17-48): Immutable variables of external function type are not yet supported. From 205063f86b89050515c1f34fac0d442a9599b231 Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Fri, 3 Apr 2020 23:47:10 -0400 Subject: [PATCH 63/76] Delete copy and move operations for ASTVisitor and ASTConstVisitor --- libsolidity/ast/ASTVisitor.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/libsolidity/ast/ASTVisitor.h b/libsolidity/ast/ASTVisitor.h index 90dad003a..db67dc392 100644 --- a/libsolidity/ast/ASTVisitor.h +++ b/libsolidity/ast/ASTVisitor.h @@ -41,7 +41,16 @@ namespace solidity::frontend class ASTVisitor { public: + ASTVisitor() = default; + + ASTVisitor(ASTVisitor const&) = delete; + ASTVisitor(ASTVisitor&&) = delete; + + ASTVisitor& operator=(ASTVisitor const&) = delete; + ASTVisitor& operator=(ASTVisitor&&) = delete; + virtual ~ASTVisitor() = default; + virtual bool visit(SourceUnit& _node) { return visitNode(_node); } virtual bool visit(PragmaDirective& _node) { return visitNode(_node); } virtual bool visit(ImportDirective& _node) { return visitNode(_node); } @@ -158,7 +167,16 @@ protected: class ASTConstVisitor { public: + ASTConstVisitor() = default; + + ASTConstVisitor(ASTConstVisitor const&) = delete; + ASTConstVisitor(ASTConstVisitor&&) = delete; + + ASTConstVisitor& operator=(ASTConstVisitor const&) = delete; + ASTConstVisitor& operator=(ASTConstVisitor&&) = delete; + virtual ~ASTConstVisitor() = default; + virtual bool visit(SourceUnit const& _node) { return visitNode(_node); } virtual bool visit(PragmaDirective const& _node) { return visitNode(_node); } virtual bool visit(ImportDirective const& _node) { return visitNode(_node); } From 1847536d6d88c53c9a0614259cc9d8fde196bb92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 6 Apr 2020 10:15:13 +0200 Subject: [PATCH 64/76] reference-types.rst: Minor text correction, itself -> themselves --- docs/types/reference-types.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 1566a8859..09e863394 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -500,7 +500,7 @@ shown in the following example: The contract does not provide the full functionality of a crowdfunding contract, but it contains the basic concepts necessary to understand structs. -Struct types can be used inside mappings and arrays and they can itself +Struct types can be used inside mappings and arrays and they can themselves contain mappings and arrays. It is not possible for a struct to contain a member of its own type, From 303345b12c7d969a241e8242831e2b14e3d488f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kamil=20=C5=9Aliwak?= Date: Mon, 6 Apr 2020 10:15:42 +0200 Subject: [PATCH 65/76] reference-types.rst: Fix incorrectly wrapped line in the array slice section --- docs/types/reference-types.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/types/reference-types.rst b/docs/types/reference-types.rst index 09e863394..f7aca7190 100644 --- a/docs/types/reference-types.rst +++ b/docs/types/reference-types.rst @@ -387,7 +387,7 @@ If ``start`` is greater than ``end`` or if ``end`` is greater than the length of the array, an exception is thrown. Both ``start`` and ``end`` are optional: ``start`` defaults - to ``0`` and ``end`` defaults to the length of the array. +to ``0`` and ``end`` defaults to the length of the array. Array slices do not have any members. They are implicitly convertible to arrays of their underlying type From 2cfa44bba38fcc593c17a1975f4cedb3577cfd7b Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Mon, 6 Apr 2020 10:50:00 +0200 Subject: [PATCH 66/76] Allow constructing symbolic arrays from smt sort --- libsolidity/formal/SymbolicVariables.cpp | 13 +++++++++++++ libsolidity/formal/SymbolicVariables.h | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/libsolidity/formal/SymbolicVariables.cpp b/libsolidity/formal/SymbolicVariables.cpp index 8f285c883..549fc05e9 100644 --- a/libsolidity/formal/SymbolicVariables.cpp +++ b/libsolidity/formal/SymbolicVariables.cpp @@ -218,15 +218,28 @@ SymbolicArrayVariable::SymbolicArrayVariable( solAssert(isArray(m_type->category()), ""); } +SymbolicArrayVariable::SymbolicArrayVariable( + SortPointer _sort, + string _uniqueName, + EncodingContext& _context +): + SymbolicVariable(move(_sort), move(_uniqueName), _context) +{ + solAssert(m_sort->kind == Kind::Array, ""); +} + smt::Expression SymbolicArrayVariable::currentValue(frontend::TypePointer const& _targetType) const { if (_targetType) + { + solAssert(m_originalType, ""); // StringLiterals are encoded as SMT arrays in the generic case, // but they can also be compared/assigned to fixed bytes, in which // case they'd need to be encoded as numbers. if (auto strType = dynamic_cast(m_originalType)) if (_targetType->category() == frontend::Type::Category::FixedBytes) return smt::Expression(u256(toHex(util::asBytes(strType->value()), util::HexPrefix::Add))); + } return SymbolicVariable::currentValue(_targetType); } diff --git a/libsolidity/formal/SymbolicVariables.h b/libsolidity/formal/SymbolicVariables.h index be75931f2..e1c28a8b5 100644 --- a/libsolidity/formal/SymbolicVariables.h +++ b/libsolidity/formal/SymbolicVariables.h @@ -47,6 +47,8 @@ public: EncodingContext& _context ); + SymbolicVariable(SymbolicVariable&&) = default; + virtual ~SymbolicVariable() = default; virtual Expression currentValue(frontend::TypePointer const& _targetType = TypePointer{}) const; @@ -212,6 +214,13 @@ public: std::string _uniqueName, EncodingContext& _context ); + SymbolicArrayVariable( + SortPointer _sort, + std::string _uniqueName, + EncodingContext& _context + ); + + SymbolicArrayVariable(SymbolicArrayVariable&&) = default; Expression currentValue(frontend::TypePointer const& _targetType = TypePointer{}) const override; }; From 81652686bed7c3eed85ea63f012562da478101f8 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 2 Apr 2020 17:27:35 +0200 Subject: [PATCH 67/76] Debug information for immutable references. --- libevmasm/Assembly.cpp | 9 ++--- libevmasm/LinkerObject.h | 6 ++-- libsolidity/codegen/CompilerContext.cpp | 8 +---- libsolidity/interface/StandardCompiler.cpp | 36 ++++++++++++++++--- .../standard_immutable_references/input.json | 18 ++++++++++ .../standard_immutable_references/output.json | 2 ++ 6 files changed, 60 insertions(+), 19 deletions(-) create mode 100644 test/cmdlineTests/standard_immutable_references/input.json create mode 100644 test/cmdlineTests/standard_immutable_references/output.json diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp index e67ccb8f5..fa6ea4f16 100644 --- a/libevmasm/Assembly.cpp +++ b/libevmasm/Assembly.cpp @@ -530,7 +530,7 @@ LinkerObject const& Assembly::assemble() const LinkerObject& ret = m_assembledObject; size_t subTagSize = 1; - map> immutableReferencesBySub; + map>> immutableReferencesBySub; for (auto const& sub: m_subs) { auto const& linkerObject = sub->assemble(); @@ -554,7 +554,7 @@ LinkerObject const& Assembly::assemble() const for (auto const& i: m_items) if (i.type() == AssignImmutable) { - i.setImmutableOccurrences(immutableReferencesBySub[i.data()].size()); + i.setImmutableOccurrences(immutableReferencesBySub[i.data()].second.size()); setsImmutables = true; } else if (i.type() == PushImmutable) @@ -660,11 +660,12 @@ LinkerObject const& Assembly::assemble() const break; case PushImmutable: ret.bytecode.push_back(uint8_t(Instruction::PUSH32)); - ret.immutableReferences[i.data()].emplace_back(ret.bytecode.size()); + ret.immutableReferences[i.data()].first = m_immutables.at(i.data()); + ret.immutableReferences[i.data()].second.emplace_back(ret.bytecode.size()); ret.bytecode.resize(ret.bytecode.size() + 32); break; case AssignImmutable: - for (auto const& offset: immutableReferencesBySub[i.data()]) + for (auto const& offset: immutableReferencesBySub[i.data()].second) { ret.bytecode.push_back(uint8_t(Instruction::DUP1)); // TODO: should we make use of the constant optimizer methods for pushing the offsets? diff --git a/libevmasm/LinkerObject.h b/libevmasm/LinkerObject.h index ccf5588bb..ab0e26507 100644 --- a/libevmasm/LinkerObject.h +++ b/libevmasm/LinkerObject.h @@ -40,9 +40,9 @@ struct LinkerObject /// need to be replaced by the actual addresses by the linker. std::map linkReferences; - /// Map from hashes of the identifiers of immutable variables to a list of offsets into the bytecode - /// that refer to their values. - std::map> immutableReferences; + /// Map from hashes of the identifiers of immutable variables to the full identifier of the immutable and + /// to a list of offsets into the bytecode that refer to their values. + std::map>> immutableReferences; /// Appends the bytecode of @a _other and incorporates its link references. void append(LinkerObject const& _other); diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 6984a030b..06080afbd 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -90,13 +90,7 @@ size_t CompilerContext::immutableMemoryOffset(VariableDeclaration const& _variab vector CompilerContext::immutableVariableSlotNames(VariableDeclaration const& _variable) { - string baseName = - _variable.annotation().contract->fullyQualifiedName() + - "." + - _variable.name() + - " (" + - to_string(_variable.id()) + - ")"; + string baseName = to_string(_variable.id()); solAssert(_variable.annotation().type->sizeOnStack() > 0, ""); if (_variable.annotation().type->sizeOnStack() == 1) return {baseName}; diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 77e00df05..00662718e 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -234,6 +234,7 @@ bool isBinaryRequested(Json::Value const& _outputSelection) "wast", "wasm", "ewasm.wast", "ewasm.wasm", "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences", + "evm.deployedBytecode.immutableReferences", "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences", "evm.gasEstimates", "evm.legacyAssembly", "evm.assembly" @@ -309,13 +310,36 @@ Json::Value formatLinkReferences(std::map const& linkRefere return ret; } -Json::Value collectEVMObject(evmasm::LinkerObject const& _object, string const* _sourceMap) +Json::Value formatImmutableReferences(map>> const& _immutableReferences) +{ + Json::Value ret(Json::objectValue); + + for (auto const& immutableReference: _immutableReferences) + { + auto const& [identifier, byteOffsets] = immutableReference.second; + Json::Value array(Json::arrayValue); + for (size_t byteOffset: byteOffsets) + { + Json::Value byteRange(Json::arrayValue); + byteRange.append(Json::UInt(byteOffset)); + byteRange.append(Json::UInt(32)); // immutable references are currently always 32 bytes wide + array.append(byteRange); + } + ret[identifier] = array; + } + + return ret; +} + +Json::Value collectEVMObject(evmasm::LinkerObject const& _object, string const* _sourceMap, bool _runtimeObject) { Json::Value output = Json::objectValue; output["object"] = _object.toHex(); output["opcodes"] = evmasm::disassemble(_object.bytecode); output["sourceMap"] = _sourceMap ? *_sourceMap : ""; output["linkReferences"] = formatLinkReferences(_object.linkReferences); + if (_runtimeObject) + output["immutableReferences"] = formatImmutableReferences(_object.immutableReferences); return output; } @@ -982,19 +1006,21 @@ Json::Value StandardCompiler::compileSolidity(StandardCompiler::InputsAndSetting )) evmData["bytecode"] = collectEVMObject( compilerStack.object(contractName), - compilerStack.sourceMapping(contractName) + compilerStack.sourceMapping(contractName), + false ); if (compilationSuccess && isArtifactRequested( _inputsAndSettings.outputSelection, file, name, - { "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences" }, + { "evm.deployedBytecode", "evm.deployedBytecode.object", "evm.deployedBytecode.opcodes", "evm.deployedBytecode.sourceMap", "evm.deployedBytecode.linkReferences", "evm.deployedBytecode.immutableReferences" }, wildcardMatchesExperimental )) evmData["deployedBytecode"] = collectEVMObject( compilerStack.runtimeObject(contractName), - compilerStack.runtimeSourceMapping(contractName) + compilerStack.runtimeSourceMapping(contractName), + true ); if (!evmData.empty()) @@ -1081,7 +1107,7 @@ Json::Value StandardCompiler::compileYul(InputsAndSettings _inputsAndSettings) { "evm.bytecode", "evm.bytecode.object", "evm.bytecode.opcodes", "evm.bytecode.sourceMap", "evm.bytecode.linkReferences" }, wildcardMatchesExperimental )) - output["contracts"][sourceName][contractName]["evm"]["bytecode"] = collectEVMObject(*object.bytecode, object.sourceMappings.get()); + output["contracts"][sourceName][contractName]["evm"]["bytecode"] = collectEVMObject(*object.bytecode, object.sourceMappings.get(), false); if (isArtifactRequested(_inputsAndSettings.outputSelection, sourceName, contractName, "irOptimized", wildcardMatchesExperimental)) output["contracts"][sourceName][contractName]["irOptimized"] = stack.print(); diff --git a/test/cmdlineTests/standard_immutable_references/input.json b/test/cmdlineTests/standard_immutable_references/input.json new file mode 100644 index 000000000..15213be74 --- /dev/null +++ b/test/cmdlineTests/standard_immutable_references/input.json @@ -0,0 +1,18 @@ +{ + "language": "Solidity", + "sources": { + "a.sol": { + "content": "contract A { uint256 immutable x = 1; function f() public view returns (uint256) { return x; } }" + } + }, + "settings": { + "evmVersion": "petersburg", + "outputSelection": { + "*": { + "A": [ + "evm.deployedBytecode.immutableReferences" + ] + } + } + } +} diff --git a/test/cmdlineTests/standard_immutable_references/output.json b/test/cmdlineTests/standard_immutable_references/output.json new file mode 100644 index 000000000..1ba91eff7 --- /dev/null +++ b/test/cmdlineTests/standard_immutable_references/output.json @@ -0,0 +1,2 @@ +{"contracts":{"a.sol":{"A":{"evm":{"deployedBytecode":{"immutableReferences":{"3":[[77,32]]},"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"0:96:0:-:0;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;0:96:0;;;;;;;;;;;;;;;;12:1:-1;9;2:12;38:56:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;72:7;90:1;83:8;;38:56;:::o"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! +","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"}],"sources":{"a.sol":{"id":0}}} From 06562e3431e0b77800577755ed3998d55ad90528 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 6 Apr 2020 11:11:20 +0200 Subject: [PATCH 68/76] Prepare changelog for 0.6.5. --- Changelog.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index 2c757e402..17d4fd0ae 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,7 +1,7 @@ -### 0.6.5 (unreleased) +### 0.6.5 (2020-04-06) Important Bugfixes: - * Code Generator: Restrict the size of dynamic memory arrays to 64 bits during creation at runtime fixing a possible overflow. + * Code Generator: Restrict the length of dynamic memory arrays to 64 bits during creation at runtime fixing a possible overflow. Language Features: @@ -10,17 +10,17 @@ Language Features: Compiler Features: - * Metadata: Added support for IPFS hashes of large files that need to be split in multiple chunks. * Commandline Interface: Enable output of storage layout with `--storage-layout`. + * Metadata: Added support for IPFS hashes of large files that need to be split in multiple chunks. Bugfixes: + * Inheritance: Allow public state variables to override functions with dynamic memory types in their return values. * Inline Assembly: Fix internal error when accessing invalid constant variables. * Inline Assembly: Fix internal error when accessing functions. + * JSON AST: Always add pointer suffix for memory reference types. * Reference Resolver: Fix internal error when accessing invalid struct members. * Type Checker: Fix internal errors when assigning nested tuples. - * Inheritance: Allow public state variables to override functions with dynamic memory types in their return values. - * JSON AST: Always add pointer suffix for memory reference types. ### 0.6.4 (2020-03-10) From 84d7bac4f6468b5aa354a6824d3f984288c7b8b5 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 6 Apr 2020 11:21:53 +0200 Subject: [PATCH 69/76] Format immutable references similarly to link references. --- libsolidity/interface/StandardCompiler.cpp | 6 +++--- test/cmdlineTests/standard_immutable_references/output.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 00662718e..a97114e83 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -320,9 +320,9 @@ Json::Value formatImmutableReferences(map>> co Json::Value array(Json::arrayValue); for (size_t byteOffset: byteOffsets) { - Json::Value byteRange(Json::arrayValue); - byteRange.append(Json::UInt(byteOffset)); - byteRange.append(Json::UInt(32)); // immutable references are currently always 32 bytes wide + Json::Value byteRange(Json::objectValue); + byteRange["start"] = Json::UInt(byteOffset); + byteRange["length"] = Json::UInt(32); // immutable references are currently always 32 bytes wide array.append(byteRange); } ret[identifier] = array; diff --git a/test/cmdlineTests/standard_immutable_references/output.json b/test/cmdlineTests/standard_immutable_references/output.json index 1ba91eff7..2788a8e73 100644 --- a/test/cmdlineTests/standard_immutable_references/output.json +++ b/test/cmdlineTests/standard_immutable_references/output.json @@ -1,2 +1,2 @@ -{"contracts":{"a.sol":{"A":{"evm":{"deployedBytecode":{"immutableReferences":{"3":[[77,32]]},"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"0:96:0:-:0;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;0:96:0;;;;;;;;;;;;;;;;12:1:-1;9;2:12;38:56:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;72:7;90:1;83:8;;38:56;:::o"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! +{"contracts":{"a.sol":{"A":{"evm":{"deployedBytecode":{"immutableReferences":{"3":[{"length":32,"start":77}]},"linkReferences":{},"object":"bytecode removed","opcodes":"opcodes removed","sourceMap":"0:96:0:-:0;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;0:96:0;;;;;;;;;;;;;;;;12:1:-1;9;2:12;38:56:0;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;72:7;90:1;83:8;;38:56;:::o"}}}}},"errors":[{"component":"general","formattedMessage":"a.sol: Warning: Source file does not specify required compiler version! ","message":"Source file does not specify required compiler version!","severity":"warning","sourceLocation":{"end":-1,"file":"a.sol","start":-1},"type":"Warning"}],"sources":{"a.sol":{"id":0}}} From 4a23ce087ca868a0fe74f80ba976f9e7c36ee107 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Mon, 6 Apr 2020 11:22:07 +0200 Subject: [PATCH 70/76] Document immutable references output. --- docs/using-the-compiler.rst | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst index 8d647a4a7..02218592e 100644 --- a/docs/using-the-compiler.rst +++ b/docs/using-the-compiler.rst @@ -302,7 +302,8 @@ Input Description // evm.bytecode.opcodes - Opcodes list // evm.bytecode.sourceMap - Source mapping (useful for debugging) // evm.bytecode.linkReferences - Link references (if unlinked object) - // evm.deployedBytecode* - Deployed bytecode (has the same options as evm.bytecode) + // evm.deployedBytecode* - Deployed bytecode (has all the options that evm.bytecode has) + // evm.deployedBytecode.immutableReferences - Map from AST ids to bytecode ranges that reference immutables // evm.methodIdentifiers - The list of function hashes // evm.gasEstimates - Function gas estimates // ewasm.wast - eWASM S-expressions format (not supported at the moment) @@ -424,8 +425,14 @@ Output Description } } }, - // The same layout as above. - "deployedBytecode": { }, + "deployedBytecode": { + ..., // The same layout as above. + "immutableReferences": [ + // There are two references to the immutable with AST ID 3, both 32 bytes long. One is + // at bytecode offset 42, the other at bytecode offset 80. + "3": [{ "start": 42, "length": 32 }, { "start": 80, "length": 32 }] + ] + }, // The list of function hashes "methodIdentifiers": { "delegate(address)": "5c19a95c" From 05a85461fe43f61e972f564d481188e51746022a Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 24 Mar 2020 20:15:15 +0100 Subject: [PATCH 71/76] Symbolic state --- libsolidity/CMakeLists.txt | 2 + libsolidity/formal/BMC.cpp | 3 +- libsolidity/formal/EncodingContext.cpp | 57 +----------------- libsolidity/formal/EncodingContext.h | 25 ++------ libsolidity/formal/SMTEncoder.cpp | 9 +-- libsolidity/formal/SymbolicState.cpp | 82 ++++++++++++++++++++++++++ libsolidity/formal/SymbolicState.h | 71 ++++++++++++++++++++++ 7 files changed, 169 insertions(+), 80 deletions(-) create mode 100644 libsolidity/formal/SymbolicState.cpp create mode 100644 libsolidity/formal/SymbolicState.h diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt index c7a517a0b..efb553e1a 100644 --- a/libsolidity/CMakeLists.txt +++ b/libsolidity/CMakeLists.txt @@ -106,6 +106,8 @@ set(sources formal/Sorts.h formal/SSAVariable.cpp formal/SSAVariable.h + formal/SymbolicState.cpp + formal/SymbolicState.h formal/SymbolicTypes.cpp formal/SymbolicTypes.h formal/SymbolicVariables.cpp diff --git a/libsolidity/formal/BMC.cpp b/libsolidity/formal/BMC.cpp index 141a3eb32..1b4e8174a 100644 --- a/libsolidity/formal/BMC.cpp +++ b/libsolidity/formal/BMC.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -376,7 +377,7 @@ void BMC::endVisit(FunctionCall const& _funCall) SMTEncoder::endVisit(_funCall); auto value = _funCall.arguments().front(); solAssert(value, ""); - smt::Expression thisBalance = m_context.balance(); + smt::Expression thisBalance = m_context.state().balance(); addVerificationTarget( VerificationTarget::Type::Balance, diff --git a/libsolidity/formal/EncodingContext.cpp b/libsolidity/formal/EncodingContext.cpp index 1f7b7a5e0..7ba51d657 100644 --- a/libsolidity/formal/EncodingContext.cpp +++ b/libsolidity/formal/EncodingContext.cpp @@ -25,13 +25,8 @@ using namespace solidity::util; using namespace solidity::frontend::smt; EncodingContext::EncodingContext(): - m_thisAddress(make_unique("this", *this)) + m_state(*this) { - auto sort = make_shared( - SortProvider::intSort, - SortProvider::intSort - ); - m_balances = make_unique(sort, "balances", *this); } void EncodingContext::reset() @@ -39,8 +34,7 @@ void EncodingContext::reset() resetAllVariables(); m_expressions.clear(); m_globalContext.clear(); - m_thisAddress->resetIndex(); - m_balances->resetIndex(); + m_state.reset(); m_assertions.clear(); } @@ -183,40 +177,6 @@ bool EncodingContext::knownGlobalSymbol(string const& _var) const return m_globalContext.count(_var); } -// Blockchain - -Expression EncodingContext::thisAddress() -{ - return m_thisAddress->currentValue(); -} - -Expression EncodingContext::balance() -{ - return balance(m_thisAddress->currentValue()); -} - -Expression EncodingContext::balance(Expression _address) -{ - return Expression::select(m_balances->currentValue(), move(_address)); -} - -void EncodingContext::transfer(Expression _from, Expression _to, Expression _value) -{ - unsigned indexBefore = m_balances->index(); - addBalance(_from, 0 - _value); - addBalance(_to, move(_value)); - unsigned indexAfter = m_balances->index(); - solAssert(indexAfter > indexBefore, ""); - m_balances->increaseIndex(); - /// Do not apply the transfer operation if _from == _to. - auto newBalances = Expression::ite( - move(_from) == move(_to), - m_balances->valueAtIndex(indexBefore), - m_balances->valueAtIndex(indexAfter) - ); - addAssertion(m_balances->currentValue() == newBalances); -} - /// Solver. Expression EncodingContext::assertions() @@ -248,16 +208,3 @@ void EncodingContext::addAssertion(Expression const& _expr) else m_assertions.back() = _expr && move(m_assertions.back()); } - -/// Private helpers. - -void EncodingContext::addBalance(Expression _address, Expression _value) -{ - auto newBalances = Expression::store( - m_balances->currentValue(), - _address, - balance(_address) + move(_value) - ); - m_balances->increaseIndex(); - addAssertion(newBalances == m_balances->currentValue()); -} diff --git a/libsolidity/formal/EncodingContext.h b/libsolidity/formal/EncodingContext.h index 648f7dc33..d62de055d 100644 --- a/libsolidity/formal/EncodingContext.h +++ b/libsolidity/formal/EncodingContext.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include @@ -121,18 +122,6 @@ public: bool knownGlobalSymbol(std::string const& _var) const; //@} - /// Blockchain. - //@{ - /// Value of `this` address. - Expression thisAddress(); - /// @returns the symbolic balance of address `this`. - Expression balance(); - /// @returns the symbolic balance of an address. - Expression balance(Expression _address); - /// Transfer _value from _from to _to. - void transfer(Expression _from, Expression _to, Expression _value); - //@} - /// Solver. //@{ /// @returns conjunction of all added assertions. @@ -148,10 +137,9 @@ public: } //@} -private: - /// Adds _value to _account's balance. - void addBalance(Expression _account, Expression _value); + SymbolicState& state() { return m_state; } +private: /// Symbolic expressions. //{@ /// Symbolic variables. @@ -164,11 +152,8 @@ private: /// variables and functions. std::unordered_map> m_globalContext; - /// Symbolic `this` address. - std::unique_ptr m_thisAddress; - - /// Symbolic balances. - std::unique_ptr m_balances; + /// Symbolic representation of the blockchain state. + SymbolicState m_state; //@} /// Solver related. diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index 0efaf90a9..f02d32aae 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -619,10 +620,10 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) auto const& value = args.front(); solAssert(value, ""); - smt::Expression thisBalance = m_context.balance(); + smt::Expression thisBalance = m_context.state().balance(); setSymbolicUnknownValue(thisBalance, TypeProvider::uint256(), m_context); - m_context.transfer(m_context.thisAddress(), expr(address), expr(*value)); + m_context.state().transfer(m_context.state().thisAddress(), expr(address), expr(*value)); break; } default: @@ -711,7 +712,7 @@ void SMTEncoder::endVisit(Identifier const& _identifier) defineGlobalVariable(_identifier.name(), _identifier); else if (_identifier.name() == "this") { - defineExpr(_identifier, m_context.thisAddress()); + defineExpr(_identifier, m_context.state().thisAddress()); m_uninterpretedTerms.insert(&_identifier); } else @@ -858,7 +859,7 @@ bool SMTEncoder::visit(MemberAccess const& _memberAccess) _memberAccess.expression().accept(*this); if (_memberAccess.memberName() == "balance") { - defineExpr(_memberAccess, m_context.balance(expr(_memberAccess.expression()))); + defineExpr(_memberAccess, m_context.state().balance(expr(_memberAccess.expression()))); setSymbolicUnknownValue(*m_context.expression(_memberAccess), m_context); m_uninterpretedTerms.insert(&_memberAccess); return false; diff --git a/libsolidity/formal/SymbolicState.cpp b/libsolidity/formal/SymbolicState.cpp new file mode 100644 index 000000000..d38e79c54 --- /dev/null +++ b/libsolidity/formal/SymbolicState.cpp @@ -0,0 +1,82 @@ +/* + 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 . +*/ + +#include + +#include + +using namespace std; +using namespace solidity::frontend::smt; + +SymbolicState::SymbolicState(EncodingContext& _context): + m_context(_context) +{ +} + +void SymbolicState::reset() +{ + m_thisAddress.resetIndex(); + m_balances.resetIndex(); +} + +// Blockchain + +Expression SymbolicState::thisAddress() +{ + return m_thisAddress.currentValue(); +} + +Expression SymbolicState::balance() +{ + return balance(m_thisAddress.currentValue()); +} + +Expression SymbolicState::balance(Expression _address) +{ + return Expression::select(m_balances.currentValue(), move(_address)); +} + +void SymbolicState::transfer(Expression _from, Expression _to, Expression _value) +{ + unsigned indexBefore = m_balances.index(); + addBalance(_from, 0 - _value); + addBalance(_to, move(_value)); + unsigned indexAfter = m_balances.index(); + solAssert(indexAfter > indexBefore, ""); + m_balances.increaseIndex(); + /// Do not apply the transfer operation if _from == _to. + auto newBalances = Expression::ite( + move(_from) == move(_to), + m_balances.valueAtIndex(indexBefore), + m_balances.valueAtIndex(indexAfter) + ); + m_context.addAssertion(m_balances.currentValue() == newBalances); +} + +/// Private helpers. + +void SymbolicState::addBalance(Expression _address, Expression _value) +{ + auto newBalances = Expression::store( + m_balances.currentValue(), + _address, + balance(_address) + move(_value) + ); + m_balances.increaseIndex(); + m_context.addAssertion(newBalances == m_balances.currentValue()); +} + diff --git a/libsolidity/formal/SymbolicState.h b/libsolidity/formal/SymbolicState.h new file mode 100644 index 000000000..380bfda51 --- /dev/null +++ b/libsolidity/formal/SymbolicState.h @@ -0,0 +1,71 @@ +/* + 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 . +*/ + +#pragma once + +#include +#include +#include + +namespace solidity::frontend::smt +{ + +class EncodingContext; + +/** + * Symbolic representation of the blockchain state. + */ +class SymbolicState +{ +public: + SymbolicState(EncodingContext& _context); + + void reset(); + + /// Blockchain. + //@{ + /// Value of `this` address. + Expression thisAddress(); + /// @returns the symbolic balance of address `this`. + Expression balance(); + /// @returns the symbolic balance of an address. + Expression balance(Expression _address); + /// Transfer _value from _from to _to. + void transfer(Expression _from, Expression _to, Expression _value); + //@} + +private: + /// Adds _value to _account's balance. + void addBalance(Expression _account, Expression _value); + + EncodingContext& m_context; + + /// Symbolic `this` address. + SymbolicAddressVariable m_thisAddress{ + "this", + m_context + }; + + /// Symbolic balances. + SymbolicArrayVariable m_balances{ + std::make_shared(SortProvider::intSort, SortProvider::intSort), + "balances", + m_context + }; +}; + +} From 30c9705c146432447cf14b98a4ca5aa41b57ac5d Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 6 Apr 2020 13:17:01 +0200 Subject: [PATCH 72/76] Update buglist. --- docs/bugs_by_version.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 0d0f30c48..b12393016 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1013,5 +1013,9 @@ "MemoryArrayCreationOverflow" ], "released": "2020-03-10" + }, + "0.6.5": { + "bugs": [], + "released": "2020-04-06" } } \ No newline at end of file From 1cf8a6c9d240bb498d5167a75b7a8aef362ef03e Mon Sep 17 00:00:00 2001 From: Jason Cobb Date: Mon, 6 Apr 2020 10:10:58 -0400 Subject: [PATCH 73/76] Use readlink -f instead of realpath in ASTImportTest.sh --- scripts/ASTImportTest.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/ASTImportTest.sh b/scripts/ASTImportTest.sh index f9172df0c..b3a1b6724 100755 --- a/scripts/ASTImportTest.sh +++ b/scripts/ASTImportTest.sh @@ -4,7 +4,7 @@ # first exporting a .sol file to JSON, then loading it into the compiler # and exporting it again. The second JSON should be identical to the first -REPO_ROOT=$(realpath "$(dirname "$0")"/..) +REPO_ROOT=$(readlink -f "$(dirname "$0")"/..) SOLIDITY_BUILD_DIR=${SOLIDITY_BUILD_DIR:-build} SOLC=${REPO_ROOT}/${SOLIDITY_BUILD_DIR}/solc/solc SPLITSOURCES=${REPO_ROOT}/scripts/splitSources.py From 8cc16eb128e551f38ec99701aec475a2f180bc7a Mon Sep 17 00:00:00 2001 From: Djordje Mijovic Date: Mon, 6 Apr 2020 17:30:01 +0200 Subject: [PATCH 74/76] Refactoring nextConstructor by moving it from CompilerContext to ContractDefinition --- libsolidity/ast/AST.cpp | 31 +++++++++++++++++++++ libsolidity/ast/AST.h | 4 +++ libsolidity/codegen/CompilerContext.cpp | 35 +----------------------- libsolidity/codegen/CompilerContext.h | 4 --- libsolidity/codegen/ContractCompiler.cpp | 6 ++-- 5 files changed, 40 insertions(+), 40 deletions(-) diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp index b711758d6..d9425937f 100644 --- a/libsolidity/ast/AST.cpp +++ b/libsolidity/ast/AST.cpp @@ -217,6 +217,37 @@ ContractDefinitionAnnotation& ContractDefinition::annotation() const return initAnnotation(); } +ContractDefinition const* ContractDefinition::superContract(ContractDefinition const& _mostDerivedContract) const +{ + auto const& hierarchy = _mostDerivedContract.annotation().linearizedBaseContracts; + auto it = find(hierarchy.begin(), hierarchy.end(), this); + solAssert(it != hierarchy.end(), "Base not found in inheritance hierarchy."); + ++it; + if (it == hierarchy.end()) + return nullptr; + else + { + solAssert(*it != this, ""); + return *it; + } +} + +FunctionDefinition const* ContractDefinition::nextConstructor(ContractDefinition const& _mostDerivedContract) const +{ + ContractDefinition const* next = superContract(_mostDerivedContract); + if (next == nullptr) + return nullptr; + for (ContractDefinition const* c: _mostDerivedContract.annotation().linearizedBaseContracts) + if (c == next || next == nullptr) + { + if (c->constructor()) + return c->constructor(); + next = nullptr; + } + + return nullptr; +} + TypeNameAnnotation& TypeName::annotation() const { return initAnnotation(); diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index 4979d5d4e..b1328d008 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -518,6 +518,10 @@ public: bool abstract() const { return m_abstract; } + ContractDefinition const* superContract(ContractDefinition const& _mostDerivedContract) const; + /// @returns the next constructor in the inheritance hierarchy. + FunctionDefinition const* nextConstructor(ContractDefinition const& _mostDerivedContract) const; + private: std::vector> m_baseContracts; std::vector> m_subNodes; diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 6984a030b..f5c4977f8 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -275,29 +275,11 @@ evmasm::AssemblyItem CompilerContext::functionEntryLabelIfExists(Declaration con FunctionDefinition const& CompilerContext::superFunction(FunctionDefinition const& _function, ContractDefinition const& _base) { solAssert(m_mostDerivedContract, "No most derived contract set."); - ContractDefinition const* super = superContract(_base); + ContractDefinition const* super = _base.superContract(mostDerivedContract()); solAssert(super, "Super contract not available."); return _function.resolveVirtual(mostDerivedContract(), super); } -FunctionDefinition const* CompilerContext::nextConstructor(ContractDefinition const& _contract) const -{ - ContractDefinition const* next = superContract(_contract); - if (next == nullptr) - return nullptr; - for (ContractDefinition const* c: m_mostDerivedContract->annotation().linearizedBaseContracts) - if (next != nullptr && next != c) - continue; - else - { - next = nullptr; - if (c->constructor()) - return c->constructor(); - } - - return nullptr; -} - ContractDefinition const& CompilerContext::mostDerivedContract() const { solAssert(m_mostDerivedContract, "Most derived contract not set."); @@ -542,21 +524,6 @@ LinkerObject const& CompilerContext::assembledObject() const return object; } -ContractDefinition const* CompilerContext::superContract(ContractDefinition const& _contract) const -{ - auto const& hierarchy = mostDerivedContract().annotation().linearizedBaseContracts; - auto it = find(hierarchy.begin(), hierarchy.end(), &_contract); - solAssert(it != hierarchy.end(), "Base not found in inheritance hierarchy."); - ++it; - if (it == hierarchy.end()) - return nullptr; - else - { - solAssert(*it != &_contract, ""); - return *it; - } -} - string CompilerContext::revertReasonIfDebug(string const& _message) { return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message); diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h index a8e494435..0720a8f3b 100644 --- a/libsolidity/codegen/CompilerContext.h +++ b/libsolidity/codegen/CompilerContext.h @@ -117,8 +117,6 @@ public: /// @returns the function that overrides the given declaration from the most derived class just /// above _base in the current inheritance hierarchy. FunctionDefinition const& superFunction(FunctionDefinition const& _function, ContractDefinition const& _base); - /// @returns the next constructor in the inheritance hierarchy. - FunctionDefinition const* nextConstructor(ContractDefinition const& _contract) const; /// Sets the contract currently being compiled - the most derived one. void setMostDerivedContract(ContractDefinition const& _contract) { m_mostDerivedContract = &_contract; } ContractDefinition const& mostDerivedContract() const; @@ -313,8 +311,6 @@ public: RevertStrings revertStrings() const { return m_revertStrings; } private: - /// @returns a pointer to the contract directly above the given contract. - ContractDefinition const* superContract(ContractDefinition const& _contract) const; /// Updates source location set in the assembly. void updateSourceLocation(); diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index 822ce3b66..a599de6a4 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -159,7 +159,7 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c if (FunctionDefinition const* constructor = _contract.constructor()) appendConstructor(*constructor); - else if (auto c = m_context.nextConstructor(_contract)) + else if (auto c = _contract.nextConstructor(m_context.mostDerivedContract())) appendBaseConstructor(*c); else appendCallValueCheck(); @@ -596,7 +596,9 @@ bool ContractCompiler::visit(FunctionDefinition const& _function) appendStackVariableInitialisation(*variable); if (_function.isConstructor()) - if (auto c = m_context.nextConstructor(dynamic_cast(*_function.scope()))) + if (auto c = dynamic_cast(*_function.scope()).nextConstructor( + m_context.mostDerivedContract() + )) appendBaseConstructor(*c); solAssert(m_returnTags.empty(), ""); From 0988ee641b2f65a608285f9e43318ad8770ad276 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 6 Apr 2020 18:12:21 +0200 Subject: [PATCH 75/76] Set version to 0.6.6. --- CMakeLists.txt | 2 +- Changelog.md | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dba182345..008f458ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.6.5") +set(PROJECT_VERSION "0.6.6") # OSX target needed in order to support std::visit set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) diff --git a/Changelog.md b/Changelog.md index 17d4fd0ae..07be2d7ed 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,15 @@ +### 0.6.6 (unreleased) + +Language Features: + + +Compiler Features: + + +Bugfixes: + + + ### 0.6.5 (2020-04-06) Important Bugfixes: From e3ec22124eb3cdbe0f720682ff2dc202f63908fa Mon Sep 17 00:00:00 2001 From: Leonardo Alt Date: Tue, 7 Apr 2020 01:09:03 +0200 Subject: [PATCH 76/76] [SMTChecker] Fix ICE in CHC internal calls --- Changelog.md | 1 + libsolidity/formal/CHC.cpp | 6 ++++- .../functions/internal_call_inheritance.sol | 20 ++++++++++++++++ .../functions/internal_call_inheritance_2.sol | 24 +++++++++++++++++++ 4 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 test/libsolidity/smtCheckerTests/functions/internal_call_inheritance.sol create mode 100644 test/libsolidity/smtCheckerTests/functions/internal_call_inheritance_2.sol diff --git a/Changelog.md b/Changelog.md index 07be2d7ed..828d86ed4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -7,6 +7,7 @@ Compiler Features: Bugfixes: + * SMTChecker: Fix internal error in the CHC engine when calling inherited functions internally. diff --git a/libsolidity/formal/CHC.cpp b/libsolidity/formal/CHC.cpp index e10c16d39..5067e1396 100644 --- a/libsolidity/formal/CHC.cpp +++ b/libsolidity/formal/CHC.cpp @@ -961,7 +961,11 @@ smt::Expression CHC::predicate(FunctionCall const& _funCall) for (auto const& var: function->returnParameters()) args.push_back(m_context.variable(*var)->currentValue()); - return (*m_summaries.at(contract).at(function))(args); + if (contract->isLibrary()) + return (*m_summaries.at(contract).at(function))(args); + + solAssert(m_currentContract, ""); + return (*m_summaries.at(m_currentContract).at(function))(args); } void CHC::addRule(smt::Expression const& _rule, string const& _ruleName) diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_inheritance.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_inheritance.sol new file mode 100644 index 000000000..5332dc590 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_inheritance.sol @@ -0,0 +1,20 @@ +pragma experimental SMTChecker; + +contract C { + function c() public pure returns (uint) { return 42; } +} + +contract B is C { + function b() public pure returns (uint) { return c(); } +} + +contract A is B { + uint public x; + + function a() public { + x = b(); + assert(x < 40); + } +} +// ---- +// Warning: (254-268): Assertion violation happens here diff --git a/test/libsolidity/smtCheckerTests/functions/internal_call_inheritance_2.sol b/test/libsolidity/smtCheckerTests/functions/internal_call_inheritance_2.sol new file mode 100644 index 000000000..262f0e498 --- /dev/null +++ b/test/libsolidity/smtCheckerTests/functions/internal_call_inheritance_2.sol @@ -0,0 +1,24 @@ +pragma experimental SMTChecker; + +contract C { + uint y; + function c(uint _y) public returns (uint) { + y = _y; + return y; + } +} + +contract B is C { + function b() public returns (uint) { return c(42); } +} + +contract A is B { + uint public x; + + function a() public { + x = b(); + assert(x < 40); + } +} +// ---- +// Warning: (274-288): Assertion violation happens here